diff --git a/.gitignore b/.gitignore index 36ad4901d..fe733f8be 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,7 @@ *.ipr *.iws .idea/ - +/lib/temp # 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. @@ -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/.metadata b/.metadata index b510f8bd6..95f73f55c 100644 --- a/.metadata +++ b/.metadata @@ -1,11 +1,45 @@ # 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. - +# This file should be version controlled. version: - revision: 0b8abb4724aa590dd0f429683339b1e045a1594d + revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 channel: stable project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: android + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: ios + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: linux + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: macos + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: web + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + - platform: windows + create_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + base_revision: fb57da5f945d02ef4f98dfd9409a72b7cce74268 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' 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 3fe4edea3..0881234c8 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,100 @@ +

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

+ +

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

+ +

+ + +License: GPL-3.0 +

+ +

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

+ +

FlutterUnit App

+ +--- + +### 环境与构建 + +#### Flutter 版本 + +``` +·]>> 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://gitee.com/toly1994328/FlutterUnit/attach_files/466731/download) +- 🔥 [免费] [掘金小册 -《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://github.com/toly1994328/FlutterUnit/issues/22) -- [Flutter实用插件集录 ](https://github.com/toly1994328/FlutterUnit/issues/41) -- [Flutter要点集录 ](https://github.com/toly1994328/FlutterUnit/labels/point) +- 🔥 [掘金小册 -《Flutter 滑动探索 - 珠联璧合》](https://juejin.cn/book/6984685333312962573) +- 🔥 [掘金小册 -《Flutter 布局探索 - 薪火相传》](https://juejin.cn/book/7075958265250578469) +- 🔥 [掘金小册 -《Flutter 渲染机制 - 聚沙成塔》](https://juejin.cn/book/6965102582473687071) +--- -### FlutterUnit 下载体验: 复制链接,在浏览器下载~ +- [Flutter环境配置](https://github.com/toly1994328/FlutterUnit/issues/22) +- [Flutter实用插件集录 ](https://github.com/toly1994328/FlutterUnit/issues/41) +- [Flutter要点集录 ](https://github.com/toly1994328/FlutterUnit/labels/point) +--- -| 平台类型 | 下载地址 | 项目分支地址 | 相关文章 | -|------|------------|------|------------| -| Android版 | http://toly1994.com/file/FlutterUnit.apk |[flutter_unit](https://github.com/toly1994328/FlutterUnit)| [《FlutterUnit食用指南》](https://juejin.im/post/6844904147045597191)| -| iOS版 | 暂未提供,可自己下载项目运行 |[flutter_unit](https://github.com/toly1994328/FlutterUnit)| [《FlutterUnit 食用指南》](https://juejin.im/post/6844904147045597191)| -| MacOS版 | http://toly1994.com/file/flutter_unit_mac.zip |[flutter_unit_desk](https://github.com/toly1994328/FlutterUnit/tree/flutter_unit_desk)| [《mac版闪亮登场》](https://juejin.im/post/6844904147817332743)| -| Windows版 |http://toly1994.com/file/FlutterUnitWin.zip | [flutter_unit_desk](https://github.com/toly1994328/FlutterUnit/tree/flutter_unit_desk) | [《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)| +#### MacOS 桌面版本组件界面 ---- +![](./doc/screens/macos-2.webp) -> 当前Flutter 版本 +#### Windows 桌面版本组件界面 -``` -Flutter 2.2.3 • channel stable • https://github.com/flutter/flutter.git -Framework • revision f4abaa0735 (5 weeks ago) • 2021-07-01 12:46:11 -0700 -Engine • revision 241c87ad80 -Tools • Dart 2.13.4 +![](./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+组件收录` @@ -45,9 +105,7 @@ FlutterUnit 对`大大小小,常用不常用`的组件能收的尽量收录。 | . | . | . | |------|------------|------------| -| ![](https://user-gold-cdn.xitu.io/2020/4/14/17175af35f63c8fb?w=1080&h=2340&f=jpeg&s=158267)| ![](https://user-gold-cdn.xitu.io/2020/4/14/17175b0c1c92a004?w=1080&h=2340&f=jpeg&s=161619)| ![](https://user-gold-cdn.xitu.io/2020/4/14/17175b0a95d5c549?w=1080&h=2340&f=jpeg&s=150406) | -| ![](https://user-gold-cdn.xitu.io/2020/4/14/17175af9b09f76f6?w=1080&h=2340&f=jpeg&s=153575) | ![](https://user-gold-cdn.xitu.io/2020/4/14/17175b0766ed455b?w=1080&h=2340&f=jpeg&s=166128) |![](https://user-gold-cdn.xitu.io/2020/4/14/17175af6b9523083?w=1080&h=2340&f=jpeg&s=163290) | - +| ![](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) | --- @@ -59,9 +117,8 @@ FlutterUnit 对`大大小小,常用不常用`的组件能收的尽量收录。 | . | . | . | |------|------------|------------| -| ![](https://user-gold-cdn.xitu.io/2020/4/14/17175c3f21476fc5?w=1080&h=2340&f=jpeg&s=67728)| ![](https://user-gold-cdn.xitu.io/2020/4/14/17175c44a1cfa94c?w=1080&h=2340&f=jpeg&s=92664)| ![](https://user-gold-cdn.xitu.io/2020/4/14/17175c4a7cd90126?w=1080&h=2340&f=jpeg&s=114306) | -| ![](https://user-gold-cdn.xitu.io/2020/4/14/17175c5171d0373f?w=1080&h=2340&f=jpeg&s=159437) | ![](https://user-gold-cdn.xitu.io/2020/4/14/17175c56ce136676?w=1080&h=2340&f=jpeg&s=87311) |![](https://user-gold-cdn.xitu.io/2020/4/14/17175c61623c6462?w=1080&h=2340&f=jpeg&s=108215)| - +| ![](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)| --- @@ -71,7 +128,8 @@ FlutterUnit 对`大大小小,常用不常用`的组件能收的尽量收录。 | . | . | . | |------|------------|------------| -| ![](https://user-gold-cdn.xitu.io/2020/4/14/17175df98f83e05c?w=362&h=724&f=gif&s=1061771)| ![](https://user-gold-cdn.xitu.io/2020/4/14/17175dcce9022ddc?w=362&h=724&f=gif&s=232124)| ![](https://user-gold-cdn.xitu.io/2020/4/14/17175de9b348a26a?w=362&h=724&f=gif&s=946647)| +| ![](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)| --- @@ -79,11 +137,10 @@ FlutterUnit 对`大大小小,常用不常用`的组件能收的尽量收录。 > `相关组件通过link to 可以进行切换, 满足你的探索欲。` 如果有的关联未加入,欢迎联系我,对我来说,加个数字就行了。 -![](https://user-gold-cdn.xitu.io/2020/4/14/17175ea0ea610669?w=610&h=226&f=png&s=37961) - | . | . | . | |------|------------|------------| -|![](https://user-gold-cdn.xitu.io/2020/4/14/17175e8c2a46e1f3?w=362&h=724&f=gif&s=471182)|![](https://user-gold-cdn.xitu.io/2020/4/14/17175e921dfc5c81?w=362&h=724&f=gif&s=658708)| ![](https://user-gold-cdn.xitu.io/2020/4/14/17175e968c4f68e4?w=362&h=724&f=gif&s=300803)| +|![](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)| + --- @@ -91,10 +148,10 @@ FlutterUnit 对`大大小小,常用不常用`的组件能收的尽量收录。 > 激动人心的是,你可以通过右侧的图标`展开/隐藏 实现下面效果的代码` 并且`支持分享`,如果你想亲自体验,so,easy ! 而且`代码高亮样式可以自定义`。 + | . | . | . | |------|------------|------------| -| ![](https://user-gold-cdn.xitu.io/2020/4/14/171760369b9ae9d6?w=362&h=724&f=gif&s=1207409)| ![](https://user-gold-cdn.xitu.io/2020/4/14/1717603ad9119f2a?w=362&h=724&f=gif&s=1064037)| ![](https://user-gold-cdn.xitu.io/2020/4/14/1717604b10154271?w=362&h=724&f=gif&s=2849830)| - +| ![](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)| --- @@ -106,9 +163,7 @@ FlutterUnit 对`大大小小,常用不常用`的组件能收的尽量收录。 | . | . | . | |------|------------|------------| -| ![](https://user-gold-cdn.xitu.io/2020/4/14/171760c51633383d?w=1080&h=2340&f=jpeg&s=94977)| ![](https://user-gold-cdn.xitu.io/2020/4/14/171760cbc7d0ddba?w=1080&h=2340&f=jpeg&s=89001) | ![](https://user-gold-cdn.xitu.io/2020/4/14/171760b8c24c188f?w=1080&h=2340&f=jpeg&s=168263) | - - +| ![](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) | --- @@ -119,8 +174,7 @@ FlutterUnit 对`大大小小,常用不常用`的组件能收的尽量收录。 | . | . | . | |------|------------|------------| -| ![](https://user-gold-cdn.xitu.io/2020/4/14/1717615741f8d2e3?w=1080&h=2340&f=png&s=167438)| ![](https://user-gold-cdn.xitu.io/2020/4/14/171761667bbf6051?w=1080&h=2340&f=png&s=808002) | ![](https://user-gold-cdn.xitu.io/2020/4/14/1717617b8ab59421?w=1080&h=2340&f=png&s=796618)| - +| ![](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)| --- @@ -130,8 +184,7 @@ FlutterUnit 对`大大小小,常用不常用`的组件能收的尽量收录。 | . | . | . | |------|------------|------------| -|![](https://user-gold-cdn.xitu.io/2020/4/14/1717620037fd9a50?w=1080&h=2340&f=jpeg&s=105051)| ![](https://user-gold-cdn.xitu.io/2020/4/14/1717620161fa89ec?w=1080&h=2340&f=jpeg&s=158327)| ![](https://user-gold-cdn.xitu.io/2020/4/14/171762026eb8656d?w=1080&h=2340&f=jpeg&s=146688)| - +|![](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.代码面板风格设置 @@ -140,7 +193,8 @@ FlutterUnit 对`大大小小,常用不常用`的组件能收的尽量收录。 | . | . | |------|------------| -|![](https://user-gold-cdn.xitu.io/2020/4/14/1717628b5fe1591c?w=1080&h=759&f=png&s=105023)| ![](https://user-gold-cdn.xitu.io/2020/4/14/1717629001ade9b0?w=1080&h=773&f=png&s=102672)| +|![](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)| --- @@ -157,7 +211,7 @@ FlutterUnit 对`大大小小,常用不常用`的组件能收的尽量收录。 | . | . | . | |------|------------|------------| -| ![](https://user-gold-cdn.xitu.io/2020/4/14/171775fc594e4605?w=1080&h=2340&f=png&s=265281)| ![](https://user-gold-cdn.xitu.io/2020/4/14/171775fd99268a78?w=1080&h=2340&f=png&s=424599)| ![](https://user-gold-cdn.xitu.io/2020/4/14/171775fefef50fb9?w=1080&h=2340&f=png&s=414437) | +| ![](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) | --- @@ -165,25 +219,17 @@ FlutterUnit 对`大大小小,常用不常用`的组件能收的尽量收录。 | 添加收藏集 | 修改收藏集 | 删除收藏集 | |------|------------|------------| -| ![](https://user-gold-cdn.xitu.io/2020/4/23/171a7baf50b2d9f5?w=288&h=576&f=gif&s=447423) | ![](https://user-gold-cdn.xitu.io/2020/4/23/171a7be9b43874ce?w=288&h=576&f=gif&s=881222) | ![](https://user-gold-cdn.xitu.io/2020/4/23/171a7bfec06a2bb8?w=288&h=576&f=gif&s=837022) | +| ![](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://user-gold-cdn.xitu.io/2020/4/24/171a7c73230f803d?w=288&h=576&f=gif&s=563981) |![](https://user-gold-cdn.xitu.io/2020/4/24/171a7c7cb0780bc6?w=288&h=576&f=gif&s=628105) | ![](https://user-gold-cdn.xitu.io/2020/4/23/171a7a6fa57e3c6f?w=288&h=576&f=gif&s=448572) | +| ![](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://user-gold-cdn.xitu.io/2020/4/23/171a76fd06702dda?w=288&h=576&f=gif&s=885232) | ![](https://user-gold-cdn.xitu.io/2020/4/23/171a773c1bfd5153?w=288&h=576&f=gif&s=887426) | ![](https://user-gold-cdn.xitu.io/2020/4/23/171a7c36021a18f9?w=288&h=576&f=gif&s=714061) | +| ![](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 1.0 `目前基本就是这么多功能,可以在Github中下载打包后的apk玩玩 +> `FlutterUnit 2.0 `目前基本就是这么多功能,可以在Github中下载打包后的apk玩玩 希望能对你的Flutter学习有所帮助。 --- - -#### 3.关于我与项目 -> 不多说,都在图里。 - -| . | . | . | -|------|------------|------------| -|![](https://user-gold-cdn.xitu.io/2020/4/14/171777c67ed0c205?w=1080&h=2340&f=png&s=1272888)|![](https://user-gold-cdn.xitu.io/2020/4/14/171777c8ccfce16b?w=1080&h=2340&f=png&s=1105006)| ![](https://user-gold-cdn.xitu.io/2020/4/14/171777caed85b26a?w=1080&h=2340&f=png&s=1190414) -| diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 000000000..d54e87f36 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,22 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + rules: + avoid_print: false # Uncomment to disable the `avoid_print` rule + file_names: false + +analyzer: +# exclude: +# - modules/widget_system/widgets/** + + + diff --git a/android/.gitignore b/android/.gitignore index bc2100d8f..6f568019d 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -5,3 +5,9 @@ gradle-wrapper.jar /gradlew.bat /local.properties GeneratedPluginRegistrant.java + +# Remember to never publicly share your keystore. +# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle b/android/app/build.gradle deleted file mode 100644 index 2486e7fb7..000000000 --- a/android/app/build.gradle +++ /dev/null @@ -1,89 +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" - -//add------------------start -def keystorePropertiesFile = rootProject.file("key.properties") -def keystoreProperties = new Properties() -keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) -//add------------------end - -android { - compileSdkVersion 28 - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - lintOptions { - disable 'InvalidPackage' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.toly1994.flutter_unit" - minSdkVersion 16 - targetSdkVersion 28 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - signingConfigs { - release { - keyAlias keystoreProperties['keyAlias'] - keyPassword keystoreProperties['keyPassword'] - storeFile file(keystoreProperties['storeFile']) - storePassword keystoreProperties['storePassword'] - } - } - 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.release - //开启混淆 - minifyEnabled true - //开启资源压缩 - shrinkResources true - //开启zip对齐 - zipAlignEnabled true - //如果开启混淆,记得在混淆文件中添加混淆规则 - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' -} 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 1d5b54c6e..678734418 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,20 +1,28 @@ - - - + - - + - + + + + + + + + + + + + + + + + + + + + + + @@ -33,5 +59,8 @@ + diff --git a/android/app/src/main/kotlin/com/toly1994/flutter_unit/MainActivity.kt b/android/app/src/main/kotlin/com/toly1994/flutter_unit/MainActivity.kt index 40bf8609b..8b30f15be 100644 --- a/android/app/src/main/kotlin/com/toly1994/flutter_unit/MainActivity.kt +++ b/android/app/src/main/kotlin/com/toly1994/flutter_unit/MainActivity.kt @@ -1,12 +1,6 @@ package com.toly1994.flutter_unit -import androidx.annotation.NonNull; import io.flutter.embedding.android.FlutterActivity -import io.flutter.embedding.engine.FlutterEngine -import io.flutter.plugins.GeneratedPluginRegistrant class MainActivity: FlutterActivity() { - override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { - GeneratedPluginRegistrant.registerWith(flutterEngine); - } } diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 000000000..f74085f3f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 000000000..449a9f930 --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml index 00fa4417c..d74aa35c2 100644 --- a/android/app/src/main/res/values/styles.xml +++ b/android/app/src/main/res/values/styles.xml @@ -1,8 +1,18 @@ - + + diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index 68727617d..000000000 --- a/android/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -buildscript { - ext.kotlin_version = '1.3.50' - repositories { - google() - jcenter() - } - - dependencies { - classpath 'com.android.tools.build:gradle:3.5.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - - -allprojects { - repositories { - google() - jcenter() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 000000000..89176ef44 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,21 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/android/gradle.properties b/android/gradle.properties index 38c8d4544..259717082 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M -android.enableR8=true +org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 296b146b7..80765cdf0 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip + + diff --git a/android/key.properties b/android/key.properties deleted file mode 100644 index c78b0c53f..000000000 --- a/android/key.properties +++ /dev/null @@ -1,4 +0,0 @@ -storePassword=toly1994 -keyPassword=toly1994328 -keyAlias= king -storeFile=/Users/mac/Coder/files/key/toly1994.jks \ No newline at end of file diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index 5a2f14fb1..000000000 --- a/android/settings.gradle +++ /dev/null @@ -1,15 +0,0 @@ -include ':app' - -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() - -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } -} - -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory -} diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 000000000..ab39a10a2 --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1,25 @@ +pluginManagement { + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.7.3" apply false + id("org.jetbrains.kotlin.android") version "2.1.0" apply false +} + +include(":app") diff --git a/android/settings_aar.gradle b/android/settings_aar.gradle deleted file mode 100644 index e7b4def49..000000000 --- a/android/settings_aar.gradle +++ /dev/null @@ -1 +0,0 @@ -include ':app' diff --git a/assets/article.db b/assets/article.db new file mode 100644 index 000000000..2d4ee64f0 Binary files /dev/null and b/assets/article.db differ diff --git a/assets/data/gallery_info.json b/assets/data/gallery_info.json new file mode 100644 index 000000000..eb44916bc --- /dev/null +++ b/assets/data/gallery_info.json @@ -0,0 +1,27 @@ +[ + { + "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/assets/data/web/node.json b/assets/data/web/node.json new file mode 100644 index 000000000..b27cbda5e --- /dev/null +++ b/assets/data/web/node.json @@ -0,0 +1 @@ +[{"id":null,"widgetId":9,"name":"CircleAvatar的表现","priority":1,"subtitle":"【radius】 : 半径 【double】\n【backgroundImage】 : 图片资源 【ImageProvider】\n【foregroundColor】: 前景色 【Color】\n【backgroundColor】: 背景色 【Color】\n【minRadius】: 最小半径 【double】\n【maxRadius】: 最大半径 【double】\n【child】: 孩子组件 【Child】","code":"import 'package:flutter/material.dart';\nclass CustomCircleAvatar extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return CircleAvatar(\n radius: 50,\n backgroundImage: AssetImage(\"assets/images/wy_200x300.jpg\"),\n foregroundColor: Colors.white,\n child: Icon(\n Icons.check,\n size: 50,\n ),\n );\n }\n}\n"},{"id":null,"widgetId":19,"name":"RadioListTile需要一个泛型T","priority":1,"subtitle":"【value】 : 条目对象 【T】\n【groupValue】 : 选中对象 【T】\n【selected】: 是否选中 【bool】\n【secondary】: 右侧组件 【Widget】\n【title】: 中间上组件 【Widget】\n【subtitle】: 中间下组件 【Widget】\n【onChanged】: 切换事件 【Function(T)】","code":"import 'package:flutter/material.dart';\nclass ItemBean {\n final String title;\n final String subTitle;\n final String imgUrl;\n\n ItemBean(this.title, this.subTitle, this.imgUrl);\n}\n\nclass CustomRadioListTile extends StatefulWidget {\n @override\n _CustomRadioListTileState createState() => _CustomRadioListTileState();\n}\n\nclass _CustomRadioListTileState extends State {\n final Map languages = {\n ItemType.java:\n ItemBean(\"Java\", \"曾经世界上最流行的语言\", \"assets/images/java.jpeg\"),\n ItemType.kotlin:\n ItemBean(\"Kotlin\", \"未来世界上最流行的语言\", \"assets/images/kotlin.jpg\"),\n ItemType.dart:\n ItemBean(\"Dart\", \"世界上最优雅的语言\", \"assets/images/dart.jpg\"),\n };\n var _type = ItemType.java;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n color: Colors.grey.withAlpha(11),\n child: Column(\n mainAxisSize: MainAxisSize.min,\n children: languages.keys\n .map((type) => RadioListTile(\n value: type,\n groupValue: _type,\n title: Text(languages[type].title),\n subtitle: Text(languages[type].subTitle),\n selected: _type == type,\n secondary: CircleAvatar(\n backgroundImage: AssetImage(languages[type].imgUrl),\n ),\n onChanged: (type) => setState(() => _type = type),\n ))\n .toList()),\n );\n }\n}\n"},{"id":null,"widgetId":19,"name":"RadioListTile选中色和密排","priority":2,"subtitle":"【activeColor】 : 选中时颜色 【Color】\n【dense】: 是否密排 【bool】","code":"import 'package:flutter/material.dart';\nclass ItemBean {\n final String title;\n final String subTitle;\n final String imgUrl;\n\n ItemBean(this.title, this.subTitle, this.imgUrl);\n}\n\nclass DenseRadioListTile extends StatefulWidget {\n @override\n _DenseRadioListTileState createState() => _DenseRadioListTileState();\n}\n\nclass _DenseRadioListTileState extends State {\n final Map languages = {\n ItemType.java:\n ItemBean(\"Java\", \"曾经世界上最流行的语言\", \"assets/images/java.jpeg\"),\n ItemType.kotlin:\n ItemBean(\"Kotlin\", \"未来世界上最流行的语言\", \"assets/images/kotlin.jpg\"),\n ItemType.dart:\n ItemBean(\"Dart\", \"世界上最优雅的语言\", \"assets/images/dart.jpg\"),\n };\n var _type = ItemType.java;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n color: Colors.grey.withAlpha(11),\n child: Column(\n mainAxisSize: MainAxisSize.min,\n children: languages.keys\n .map((type) => RadioListTile(\n value: type,\n groupValue: _type,\n title: Text(languages[type].title),\n activeColor: Colors.orangeAccent,\n dense: true,\n subtitle: Text(languages[type].subTitle),\n selected: _type == type,\n secondary: CircleAvatar(\n backgroundImage: AssetImage(languages[type].imgUrl),\n ),\n onChanged: (type) => setState(() => _type = type),\n ))\n .toList()),\n );\n }\n}"},{"id":null,"widgetId":28,"name":"mini属性","priority":2,"subtitle":"【mini】: 是否是迷你 【bool】","code":"import 'package:flutter/material.dart';\nclass MiniFAB extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var data = {\n Colors.red: Icons.add,\n Colors.blue: Icons.bluetooth,\n Colors.green: Icons.android,\n };\n return Wrap(\n spacing: 20,\n children: data.keys\n .map((e) => FloatingActionButton(\n heroTag: e.toString()+\"b\",\n onPressed: () {},\n backgroundColor: e,\n mini: true,\n foregroundColor: Colors.white,\n child: Icon(data[e]),\n tooltip: \"android\",\n elevation: 5, //z-阴影盖度\n ))\n .toList());\n }\n}"},{"id":null,"widgetId":28,"name":"FloatingActionButton点击事件","priority":1,"subtitle":"【child】: 子组件 【Widget】\n【tooltip】: 长按时提示文字 【String】\n【backgroundColor】: 背景色 【Color】\n【foregroundColor】: 前景色 【Color】\n【elevation】: 影深 【double】\n【onPressed】: 点击事件 【Function】","code":"import 'package:flutter/material.dart';\nclass CustomFAB extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var data = {\n Colors.red: Icons.add,\n Colors.blue: Icons.bluetooth,\n Colors.green: Icons.android,\n };\n return Wrap(\n spacing: 20,\n children: data.keys\n .map((e) => FloatingActionButton(\n heroTag: e.toString()+\"a\",\n onPressed: () {},\n backgroundColor: e,\n foregroundColor: Colors.white,\n child: Icon(data[e]),\n tooltip: \"android\",\n elevation: 5, //z-阴影盖度\n ))\n .toList());\n }\n}"},{"id":null,"widgetId":28,"name":"shape属性","priority":3,"subtitle":"【shape】: 形状 【ShapeBorder】","code":"import 'dart:math';\nimport 'package:flutter/material.dart';\nimport '../../../../app/utils/pather.dart';\nclass ShapeFAB extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var data = {\n Colors.red: Icons.add,\n Colors.blue: Icons.bluetooth,\n Colors.green: Icons.android,\n };\n return Wrap(\n spacing: 20,\n children: data.keys\n .map((e) => FloatingActionButton(\n heroTag: e.toString()+\"c\",\n onPressed: () {},\n backgroundColor: e,\n shape: StarBorder(),\n foregroundColor: Colors.white,\n child: Icon(data[e]),\n tooltip: \"android\",\n elevation: 5,\n ))\n .toList());\n }\n}\n\n/// 边线形状类\nclass StarBorder extends ShapeBorder {\n @override\n EdgeInsetsGeometry get dimensions => null;\n\n @override\n Path getInnerPath(Rect rect, {TextDirection textDirection}) {\n return null;\n }\n\n @override\n Path getOuterPath(Rect rect, {TextDirection textDirection}) {\n return Pather.create.nStarPath(20, 25, 25 * cos((360 / 9 / 2) * pi / 180),\n dx: rect.height / 2, dy: rect.width / 2);\n }\n\n @override\n void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}\n\n @override\n ShapeBorder scale(double t) {\n return null;\n }\n}"},{"id":null,"widgetId":154,"name":"Drawer基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【elevation】 : 影深 【double】","code":"import 'package:flutter/material.dart';\nclass CustomDrawer extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 400,\n child: Scaffold(\n appBar: AppBar(\n title: Text('Flutter Unit'),\n ),\n drawer: Drawer(\n elevation: 3,\n child: _buildChild(),\n ),\n ),\n );\n }\n\n Widget _buildChild() => ListView(\n padding: EdgeInsets.zero,\n children: const [\n DrawerHeader(\n decoration: BoxDecoration(\n image: DecorationImage(\n image: AssetImage('assets/images/caver.jpeg'),\n fit: BoxFit.cover),\n ),\n child: Text(\n '张风捷特烈',\n style: TextStyle(fontSize: 24, color: Colors.white, shadows: [\n Shadow(color: Colors.black, offset: Offset(1, 1), blurRadius: 3)\n ]),\n ),\n ),\n ListTile(\n leading: Icon(\n Icons.star,\n color: Colors.blue,\n ),\n title: Text('我的收藏'),\n ),\n ListTile(\n leading: Icon(\n Icons.palette,\n color: Colors.orangeAccent,\n ),\n title: Text('我的绘画'),\n ),\n ListTile(\n leading: Icon(\n Icons.insert_drive_file,\n color: Colors.green,\n ),\n title: Text('我的文件'),\n ),\n ],\n );\n}\n"},{"id":null,"widgetId":134,"name":"DayPicker基本使用","priority":1,"subtitle":" \n【selectedDate】 : 选中日期 【DateTime】\n【currentDate】 : 当前日期 【DateTime】\n【firstDate】 : 最前日期限制 【DateTime】\n【lastDate】 : 最后日期限制 【DateTime】\n【displayedMonth】 : 当前展示的月份 【DateTime】\n【onChanged】 : 点击回调 【Function(DateTime)】","code":"import 'package:flutter/material.dart';\nclass CustomDayPicker extends StatefulWidget {\n @override\n _CustomDayPickerState createState() => _CustomDayPickerState();\n}\n\nclass _CustomDayPickerState extends State {\n\n DateTime _date = DateTime.now();\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 350,\n child: DayPicker(\n selectedDate: _date,\n currentDate: DateTime.now(),\n onChanged: (date){\n setState(() => _date = date);\n },\n firstDate: DateTime(2018),\n lastDate: DateTime(2030),\n displayedMonth: DateTime.now()\n ),\n );\n }\n}\n"},{"id":null,"widgetId":27,"name":"OutlineButton点击事件","priority":1,"subtitle":"【textColor】: 子组件文字颜色 【Color】\n【splashColor】: 水波纹颜色 【Color】\n【highlightColor】: 长按高亮色 【Color】\n【highlightedBorderColor】: 高亮时框色 【Color】\n【child】: 子组件 【Widget】\n【padding】: 内边距 【EdgeInsetsGeometry】\n【borderSide】: 边线 【BorderSide】\n【onPressed】: 点击事件 【Function】","code":"import 'package:flutter/material.dart';\nclass CustomOutlineButton extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return OutlineButton(//边线按钮\n onPressed: () {},\n child: Text(\"OutlineButton\"),\n padding: EdgeInsets.all(8),\n splashColor: Colors.green,\n highlightColor: Colors.orangeAccent,\n highlightedBorderColor: Colors.grey,\n textColor: Color(0xff000000),\n borderSide: BorderSide(color: Color(0xff0A66F8), width: 2),\n );\n }\n}\n"},{"id":null,"widgetId":147,"name":"Listener基本事件","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【onPointerDown】 : 按下事件 【Function(PointerDownEvent)】\n【onPointerMove】 : 移动事件 【Function(PointerMoveEvent)】\n【onPointerMove】 : 抬起事件 【Function(PointerUpEvent)】\n【onPointerCancel】 : 取消事件 【Function(PointerUpEvent)】","code":"import 'package:flutter/material.dart';\nclass CustomListener extends StatefulWidget {\n @override\n _CustomListenerState createState() => _CustomListenerState();\n}\n\nclass _CustomListenerState extends State {\n var _info = '';\n\n @override\n Widget build(BuildContext context) {\n return Listener(\n onPointerDown: (detail) => setState(() => _info = detail.toString()),\n onPointerMove: (detail) => setState(() => _info = detail.toString()),\n onPointerUp: (detail) => setState(() => _info = detail.toString()),\n onPointerCancel: (detail) => setState(() => _info = detail.toString()),\n\n child: Container(\n alignment: Alignment.center,\n width: 300,\n height: 300 * 0.618,\n color: Colors.grey.withAlpha(33),\n child: Text(\n _info,\n style: TextStyle(fontSize: 16, color: Colors.blue),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":159,"name":"PositionedDirectional基本使用","priority":1,"subtitle":" \n【child】 : 组件 【Widget】\n【top】 : 到父顶距离 【double】\n【end】 : 到父右距离 【double】\n【start】 : 到父左距离 【double】\n【bottom】 : 到父底距离 【double】","code":"import 'package:flutter/material.dart';\nclass CustomPositionedDirectional extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var yellowBox = Container(\n color: Colors.yellow,\n height: 100,\n width: 100,\n );\n\n var redBox = Container(\n color: Colors.red,\n height: 90,\n width: 90,\n );\n\n var greenBox = Container(\n color: Colors.green,\n height: 80,\n width: 80,\n );\n\n var cyanBox = Container(\n color: Colors.cyanAccent,\n height: 70,\n width: 70,\n );\n\n return Container(\n width: 200,\n height: 120,\n color: Colors.grey.withAlpha(33),\n child: Stack(\n children: [\n yellowBox,\n redBox,\n PositionedDirectional(top: 20, start: 20, child: greenBox),\n PositionedDirectional(\n child: cyanBox,\n bottom: 10,\n end: 10,\n )\n ],\n ));\n }\n}\n"},{"id":null,"widgetId":3,"name":"可以通过shape属性实现裁切效果","priority":2,"subtitle":"【shape】 : 形状 【ShapeBorder】\n【margin】: 外边距 【double】\n【color】: 颜色 【Color】\n【child】: 孩子 【Widget】","code":"import 'package:flutter/material.dart';\nimport '../../../../app/utils/pather.dart';\nclass ShapeCard extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Card(\n color: Color(0xffB3FE65),\n elevation: 6,\n shape: StarShapeBorder(),\n child: Container(\n alignment: Alignment.center,\n width: 100,\n height: 100,\n child: Text(\"Card\", style: TextStyle(fontSize: 20)),\n ),\n );\n }\n}\n\nclass StarShapeBorder extends ShapeBorder {\n @override\n EdgeInsetsGeometry get dimensions => null;\n\n @override\n Path getInnerPath(Rect rect, {TextDirection textDirection}) {\n return null;\n }\n\n @override\n Path getOuterPath(Rect rect, {TextDirection textDirection}) =>\n Pather.create.nStarPath(9, 50, 40, dx: 50, dy: 50);\n\n @override\n void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {\n }\n\n @override\n ShapeBorder scale(double t) {\n return null;\n }\n}"},{"id":null,"widgetId":3,"name":"Card可以让一个组件卡片化","priority":1,"subtitle":"【elevation】 : 影深 【double】\n【margin】: 外边距 【double】\n【color】: 颜色 【Color】\n【child】: 孩子 【Widget】","code":"import 'package:flutter/material.dart';\nclass CustomCard extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Card(\n color: Color(0xffB3FE65),\n elevation: 4,\n margin: EdgeInsets.all(10),\n child: Container(\n alignment: Alignment.topLeft,\n width: 200,\n height: 0.618*200,\n margin: EdgeInsets.all(10),\n child: Text(\"Card\", style: TextStyle(fontSize: 20)),\n ),\n );\n }\n}"},{"id":null,"widgetId":204,"name":"PreferredSize的转化使用","priority":2,"subtitle":"【PreferredSize将普通组件转化为PreferredSizeWidget","code":"import 'package:flutter/material.dart';\nclass AdapterPreferredSize extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: Scaffold(\n appBar: PreferredSize(\n preferredSize: Size.fromHeight(150),\n child: AppBar(\n title: Text('PreferredSize'),\n bottom: PreferredSize(\n preferredSize: Size.fromHeight(40),\n child: Container(\n height: 40,\n color: Colors.orange,\n ),\n ),\n ),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":204,"name":"PreferredSize调整AppBar高度","priority":1,"subtitle":"【preferredSize】 : 尺寸 【Size】","code":"import 'package:flutter/material.dart';\nclass CustomPreferredSize extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: Scaffold(\n appBar: PreferredSize(\n preferredSize: Size.fromHeight(150),\n child: AppBar(\n title: Text('PreferredSize'),\n ),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":29,"name":"ButtonBar边距和高","priority":2,"subtitle":"【buttonPadding】: 内边距 【EdgeInsetsGeometry】\n【buttonHeight】: 高 【double】","code":"import 'package:flutter/material.dart';\nimport '../../../dialogs/dialog_about.dart';\nclass PaddingButtonBar extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return ButtonBar(\n alignment: MainAxisAlignment.center,\n buttonHeight: 40,\n buttonPadding: EdgeInsets.only(left: 15,right: 15),\n children: [\n RaisedButton(\n color: Colors.blue,\n child: Text(\"Raised\"),\n onPressed: () => DialogAbout.show(context)),\n OutlineButton(\n child: Text(\"Outline\"),\n onPressed: () => DialogAbout.show(context)),\n FlatButton(\n color: Colors.blue,\n onPressed: () => DialogAbout.show(context),\n child: Text(\"Flat\"),\n )\n ],\n );\n }\n}\n"},{"id":null,"widgetId":29,"name":"ButtonBar对齐方式","priority":1,"subtitle":"【alignment】: 对齐方式 【MainAxisAlignment】\n【children】: 子组件集 【List】","code":"import 'package:flutter/material.dart';\nimport '../../../dialogs/dialog_about.dart';\nclass CustomButtonBar extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return ButtonBar(\n alignment: MainAxisAlignment.center,\n children: [\n RaisedButton(\n color: Colors.blue,\n child: Text(\"Raised\"),\n onPressed: () => DialogAbout.show(context)),\n OutlineButton(\n child: Text(\"Outline\"),\n onPressed: () => DialogAbout.show(context)),\n FlatButton(\n color: Colors.blue,\n onPressed: () => DialogAbout.show(context),\n child: Text(\"Flat\"),\n )\n ],\n );\n }\n}\n\n"},{"id":null,"widgetId":146,"name":"GestureDetector基本事件","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【onTap】 : 点击事件 【Function()】\n【onDoubleTap】 : 双击事件 【Function()】\n【onLongPress】 : 长按事件 【Function()】","code":"import 'package:flutter/material.dart';\nclass CustomGestureDetector extends StatefulWidget {\n @override\n _CustomGestureDetectorState createState() => _CustomGestureDetectorState();\n}\n\nclass _CustomGestureDetectorState extends State {\n var _info = '';\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onTap: () => setState(() => _info = 'onTap'),\n onDoubleTap: () => setState(() => _info = 'onDoubleTap'),\n onLongPress: () => setState(() => _info = 'onLongPress'),\n child: Container(\n alignment: Alignment.center,\n width: 300,\n height: 300 * 0.4,\n color: Colors.grey.withAlpha(33),\n child: Text(\n _info,\n style: TextStyle(fontSize: 18, color: Colors.blue),\n ),\n ),\n );\n }\n}"},{"id":null,"widgetId":146,"name":"GestureDetector详情信息","priority":2,"subtitle":" \n【onTapDown】 : 按下回调 【Function(TapDownDetails)】\n【onTapUp】 : 子组件 【Function(TapUpDetails)】\n【onTapCancel】 : 点击取消 【Function()】","code":"import 'package:flutter/material.dart';\nclass TapGestureDetector extends StatefulWidget {\n @override\n _TapGestureDetectorState createState() => _TapGestureDetectorState();\n}\n\nclass _TapGestureDetectorState extends State {\n var _info = '';\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onTapDown: (detail) => setState(() => _info =\n 'onTapDown:\\n相对落点:${detail.localPosition}\\n绝对落点:${detail.globalPosition}'),\n onTapUp: (detail) => setState(() => _info =\n 'onTapUp:\\n相对落点:${detail.localPosition}\\n绝对落点:${detail.globalPosition}'),\n onTapCancel: () => setState(() => _info = 'onTapCancel'),\n child: Container(\n alignment: Alignment.center,\n width: 300,\n height: 300 * 0.618,\n color: Colors.grey.withAlpha(33),\n child: Text(\n _info,\n style: TextStyle(fontSize: 18, color: Colors.blue),\n ),\n ),\n );\n }\n}"},{"id":null,"widgetId":146,"name":"GestureDetector的Pan事件","priority":3,"subtitle":" \n【onPanDown】 : 按下回调 【Function(DragDownDetails)】\n【onPanEnd】 : 拖动结束 【Function(DragEndDetails)】\n【onPanStart】 : 开始拖动 【Function(DragStartDetails)】\n【onPanUpdate】 : 拖动更新 【Function(TapUpDetails)】\n【onPanCancel】 : 拖动取消 【Function()】","code":"import 'package:flutter/material.dart';\nclass PanGestureDetector extends StatefulWidget {\n @override\n _PanGestureDetectorState createState() => _PanGestureDetectorState();\n}\n\nclass _PanGestureDetectorState extends State {\n var _info = '';\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onPanDown: (detail) => setState(() => _info =\n 'onPanDown:\\n相对落点:${detail.localPosition}\\n绝对落点:${detail.globalPosition}'),\n onPanEnd: (detail) => setState(() => _info =\n 'onPanEnd:\\n初速度:${detail.primaryVelocity}\\n最终速度:${detail.velocity}'),\n onPanUpdate: (detail) => setState(() => _info =\n 'onPanUpdate:\\n相对落点:${detail.localPosition}\\n绝对落点:${detail.globalPosition}'),\n onPanStart: (detail) => setState(() => _info =\n 'onPanStart:\\n相对落点:${detail.localPosition}\\n绝对落点:${detail.globalPosition}'),\n onPanCancel: () => setState(() => _info = 'onTapCancel'),\n child: SingleChildScrollView(\n child: Container(\n alignment: Alignment.center,\n width: 300,\n height: 300 * 0.618,\n color: Colors.grey.withAlpha(33),\n child: Text(\n _info,\n style: TextStyle(fontSize: 18, color: Colors.blue),\n ),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":13,"name":"ActionChip的普通表现如下","priority":1,"subtitle":"【onPressed】: 点击事件 【Function】\n【pressElevation】: 按下时影深 【double】\n其他属性同Chip组件,无右侧组件。","code":"import 'package:flutter/material.dart';\nimport '../../../dialogs/dialog_about.dart';\nclass CustomActionChip extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return ActionChip(\n padding: EdgeInsets.all(5),\n labelPadding: EdgeInsets.all(3),\n label: Text(\"This is a ActionChip.\"),\n backgroundColor: Colors.grey.withAlpha(66),\n avatar: Image.asset(\"assets/images/icon_head.png\"),\n shadowColor: Colors.orangeAccent,\n elevation: 3,\n pressElevation: 5,\n onPressed: ()=> DialogAbout.show(context),\n );\n }\n}\n"},{"id":null,"widgetId":26,"name":"RaisedButton点击事件","priority":1,"subtitle":"【color】: 颜色 【Color】\n【splashColor】: 水波纹颜色 【Color】\n【elevation】: 影深 【double】\n【child】: 子组件 【Widget】\n【textColor】: 子组件文字颜色 【Color】\n【highlightColor】: 长按高亮色 【Color】\n【padding】: 内边距 【EdgeInsetsGeometry】\n【onPressed】: 点击事件 【Function】","code":"import 'package:flutter/material.dart';\nclass CustomRaisedButton extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return RaisedButton(\n color: Colors.blue,\n splashColor: Colors.green,\n onPressed: () {},\n child: Text(\"RaisedButton\"),\n textColor: Color(0xffFfffff),\n padding: EdgeInsets.all(8),\n elevation: 5,\n highlightColor: Color(0xffF88B0A),\n );\n }\n}\n"},{"id":null,"widgetId":211,"name":"MaterialBanner两行的使用","priority":2,"subtitle":"【contentTextStyle】: 中间位置样式 【TextStyle】\n【leadingPadding】: 左侧组件边距 【EdgeInsetsGeometry】\n当尾部组件数量大于1,该组件结构为左中下。","code":"import 'package:flutter/material.dart';\nclass MaterialBannerDemoTwo extends StatelessWidget {\n final info =\n 'A banner displays an important, succinct message, and provides actions for users to address. '\n 'A user action is required for itto be dismissed.';\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [MaterialBanner(\n content: Text(\n info,\n style: TextStyle(color: Colors.white),\n ),\n backgroundColor: Colors.purple,\n leading: Icon(Icons.warning, color: Colors.yellow),\n padding: EdgeInsetsDirectional.only(start: 16.0, top: 2.0,end: 2),\n leadingPadding:EdgeInsetsDirectional.only(end: 16.0) ,\n actions: [\n RaisedButton(\n color: Colors.white,\n onPressed: () {},\n child: Text(\n 'I KNOW',\n style: TextStyle(\n color: Colors.purple,\n fontWeight: FontWeight.bold,\n fontSize: 14),\n ),\n ),\n\n RaisedButton(\n color: Colors.white,\n onPressed: () {},\n child: Text(\n 'I IGNORE',\n style: TextStyle(\n color: Colors.purple,\n fontWeight: FontWeight.bold,\n fontSize: 14),\n ),\n ),\n ],\n )],\n );\n }\n\n}\n"},{"id":null,"widgetId":211,"name":"MaterialBanner一行的使用","priority":1,"subtitle":"【content】 : 中间组件 【Widget】\n【leading】: 左侧组件 【Widget】\n【actions】: 右侧组件列表 【List】\n【padding】: 内边距 【EdgeInsetsGeometry】\n【forceActionsBelow】: 是否按钮在下方 【bool】\n【backgroundColor】: 背景色 【Color】","code":"import 'package:flutter/material.dart';\nclass MaterialBannerDemo extends StatelessWidget {\n final info =\n 'Welcome to Flutter Unit!';\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [MaterialBanner(\n content: Text(\n info,\n style: TextStyle(color: Colors.white),\n ),\n backgroundColor: Colors.purple,\n leading: Icon(Icons.info, color: Colors.lightBlueAccent),\n padding: EdgeInsetsDirectional.only(start: 16.0, top: 2.0),\n forceActionsBelow: false, // 默认false\n actions: [\n Text(\n 'I KNOW',\n style:TextStyle(\n color: Colors.orange,\n fontWeight: FontWeight.bold,\n fontSize: 14) ,\n )\n ],\n )],\n );\n }\n\n}\n"},{"id":null,"widgetId":164,"name":"SingleChildScrollView基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【padding】 : 点击事件 【EdgeInsetsGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomSingleChildScrollView extends StatelessWidget {\n final data = [\n Colors.blue[50],\n Colors.blue[100],\n Colors.blue[200],\n Colors.blue[300],\n Colors.blue[400],\n Colors.blue[500],\n Colors.blue[600],\n Colors.blue[700],\n Colors.blue[800],\n Colors.blue[900],\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: SingleChildScrollView(\n padding: EdgeInsets.symmetric(horizontal: 10),\n child: Column(\n children: data\n .map((color) => Container(\n alignment: Alignment.center,\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":164,"name":"SingleChildScrollView滑动方向","priority":2,"subtitle":" \n【scrollDirection】 : 滑动方向 【Axis】\n【reverse】 : 是否反向 【Axis】","code":"import 'package:flutter/material.dart';\nclass DirectionSingleChildScrollView extends StatelessWidget {\n final data = [\n Colors.blue[50],\n Colors.blue[100],\n Colors.blue[200],\n Colors.blue[300],\n Colors.blue[400],\n Colors.blue[500],\n Colors.blue[600],\n Colors.blue[700],\n Colors.blue[800],\n Colors.blue[900],\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: SingleChildScrollView(\n scrollDirection: Axis.horizontal,\n reverse: true,\n padding: EdgeInsets.symmetric(horizontal: 10),\n child: Row(\n children: data\n .map((color) => Container(\n alignment: Alignment.center,\n width: 90,\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":33,"name":"ToggleButtons多选切换","priority":3,"subtitle":" 可以控制状态转化的逻辑来形成不同的效果。","code":"import 'package:flutter/material.dart';\nclass ProToggleButtons extends StatefulWidget {\n @override\n _ProToggleButtonsState createState() => _ProToggleButtonsState();\n}\n\nclass _ProToggleButtonsState extends State {\n var _isSelected = [false, false, false];\n @override\n Widget build(BuildContext context) {\n return ToggleButtons(\n children: [\n Icon(Icons.skip_previous),\n Icon(Icons.pause),\n Icon(Icons.skip_next),\n ],\n borderWidth: 1,\n borderColor: Colors.blue,\n selectedBorderColor: Colors.orangeAccent,\n splashColor: Colors.purple.withAlpha(66),\n borderRadius: BorderRadius.circular(10),\n selectedColor: Colors.red,\n fillColor: Colors.green.withAlpha(11),\n isSelected: _isSelected,\n onPressed: (value) => setState(() {\n _isSelected[value] = !_isSelected[value];\n }),\n );\n }\n}"},{"id":null,"widgetId":33,"name":"ToggleButtons单选切换","priority":1,"subtitle":"【children】: 子组件集 【List】\n【borderWidth】: 边线宽 【double】\n【borderRadius】: 圆角 【BorderRadius】\n【isSelected】: 是否选中集 【List】\n【onPressed】: 点击事件 【Function(int)】","code":"import 'package:flutter/material.dart';\nclass CustomToggleButtons extends StatefulWidget {\n @override\n _CustomToggleButtonsState createState() => _CustomToggleButtonsState();\n}\n\nclass _CustomToggleButtonsState extends State {\n var _isSelected = [true, false, false];\n\n @override\n Widget build(BuildContext context) {\n return ToggleButtons(\n children: [\n Icon(Icons.skip_previous),\n Icon(Icons.pause),\n Icon(Icons.skip_next),\n ],\n borderWidth: 1,\n borderRadius: BorderRadius.circular(10),\n isSelected: _isSelected,\n onPressed: (value) => setState(() {\n _isSelected = _isSelected.map((e) => false).toList();\n _isSelected[value] = true;\n }),\n );\n\n }\n}"},{"id":null,"widgetId":33,"name":"ToggleButtons颜色属性","priority":2,"subtitle":"【borderColor】: 边线色 【Color】\n【selectedBorderColor】: 选中边线色 【Color】\n【selectedColor】: 选中时组件色 【Color】\n【fillColor】: 选中时填充色 【Color】\n【splashColor】: 水波纹色 【Color】","code":"import 'package:flutter/material.dart';\nclass ColorToggleButtons extends StatefulWidget {\n @override\n _ColorToggleButtonsState createState() => _ColorToggleButtonsState();\n}\n\nclass _ColorToggleButtonsState extends State {\n var _isSelected = [true, false, false];\n @override\n Widget build(BuildContext context) {\n return ToggleButtons(\n children: [\n Icon(Icons.skip_previous),\n Icon(Icons.pause),\n Icon(Icons.skip_next),\n ],\n borderWidth: 1,\n borderColor: Colors.orangeAccent,\n selectedBorderColor: Colors.blue,\n splashColor: Colors.purple.withAlpha(66),\n borderRadius: BorderRadius.circular(10),\n selectedColor: Colors.red,\n fillColor: Colors.green.withAlpha(11),\n isSelected: _isSelected,\n onPressed: (value) => setState(() {\n _isSelected = _isSelected.map((e) => false).toList();\n _isSelected[value] = true;\n }),\n );\n }\n}\n"},{"id":null,"widgetId":10,"name":"replacement可在隐藏时进行占位","priority":2,"subtitle":"【replacement】 : 隐藏时的占位组件 【Widget】","code":"import 'package:flutter/material.dart';\nclass ReplacementVisibility extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n children: [\n _buildVisibility(true),\n _buildVisibility(false),\n ],\n );\n }\n\n _buildVisibility(bool visible) {\n var box = Container(\n height: 30,\n width: 30,\n color: Colors.blue,\n );\n return Container(\n width: 150,\n height: 150 * 0.618,\n color: Colors.cyanAccent.withAlpha(33),\n child: Row(\n mainAxisAlignment: MainAxisAlignment.center,\n children: [\n box,\n Visibility(\n visible: visible,\n replacement: Container(\n alignment: Alignment.center,\n height: 80 * 0.618,\n width: 80,\n ),\n child: Container(\n alignment: Alignment.center,\n height: 80 * 0.618,\n width: 80,\n color: Colors.red,\n child: Text(\n \"visible\\ntrue\",\n style: TextStyle(fontSize: 20),\n ),\n )),\n box,\n ],\n ),\n );\n }\n}\n"},{"id":null,"widgetId":10,"name":"根据visible控制内部子组件的显隐情况","priority":1,"subtitle":"【visible】 : 是否显示 【bool】\n【child】: 孩子 【Widget】\n默认孩子隐藏时会失去原来所在区域。","code":"import 'package:flutter/material.dart';\nclass CustomVisibility extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n children: [\n _buildVisibility(true),\n _buildVisibility(false),\n ],\n );\n }\n\n _buildVisibility(bool visible) {\n var box = Container(\n height: 30,\n width: 30,\n color: Colors.blue,\n );\n return Container(\n width: 150,\n height: 150 * 0.618,\n color: Colors.cyanAccent.withAlpha(33),\n child: Row(\n mainAxisAlignment: MainAxisAlignment.center,\n children: [\n box,\n Visibility(\n visible: visible,\n child: Container(\n alignment: Alignment.center,\n height: 80 * 0.618,\n width: 80,\n color: Colors.red,\n child: Text(\n \"visible\\ntrue\",\n style: TextStyle(fontSize: 20),\n ),\n )),\n box,\n ],\n ),\n );\n }\n}\n"},{"id":null,"widgetId":208,"name":"Title基本使用","priority":1,"subtitle":"【title】 : 名称 【int】\n【color】: 颜色 【Color】\n【child】: 子组件 【Widget】","code":"import 'package:flutter/material.dart';\nclass TitleDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Title(\n title: '张风捷特烈',\n color: Color(0xFF9C27B0),\n child: Center(child: Text('应用菜单栏中该页的名称为: 张风捷特烈'))),\n );\n }\n}\n"},{"id":null,"widgetId":163,"name":"GridView.extent构造","priority":3,"subtitle":" \n【maxCrossAxisExtent】 : box轴向长度 【double】\n【reverse】 : 是否反向滑动 【bool】\n【shrinkWrap】 : 无边界时是否包裹 【bool】","code":"import 'package:flutter/material.dart';\nclass ExtentGridView extends StatelessWidget {\n final data = List.generate(128, (i) => Color(0xFF00FFFF - 2*i));\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: GridView.extent(\n scrollDirection: Axis.horizontal,\n maxCrossAxisExtent: 80.0,\n mainAxisSpacing: 2,\n crossAxisSpacing: 2,\n childAspectRatio: 0.618,\n children: data\n .map((color) => _buildItem(color))\n .toList(),\n ),\n );\n }\n\n Container _buildItem(Color color) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 30,\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\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":163,"name":"GridView.count构造","priority":1,"subtitle":" \n【children】 : 子组件列表 【List】\n【crossAxisCount】 : 主轴一行box数量 【int】\n【mainAxisSpacing】 : 主轴每行间距 【double】\n【crossAxisSpacing】 : 交叉轴每行间距 【double】\n【childAspectRatio】 : box主长/交叉轴长 【double】\n【crossAxisCount】 : 主轴一行数量 【int】","code":"import 'package:flutter/material.dart';\nclass CustomGridView extends StatelessWidget {\n final data = List.generate(128, (i) => Color(0xFFFF00FF - 2*i));\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: GridView.count(\n crossAxisCount: 4,\n mainAxisSpacing: 2,\n crossAxisSpacing: 2,\n childAspectRatio: 1/0.618,\n children: data\n .map((color) => _buildItem(color))\n .toList(),\n ),\n );\n }\n\n Container _buildItem(Color color) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 30,\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\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":163,"name":"GridView.builder构造","priority":4,"subtitle":" \n【itemCount】 : 条目数量 【int】\n【gridDelegate】 : 网格代理 【SliverGridDelegate】\n【itemBuilder】 : 条目构造器 【IndexedWidgetBuilder】","code":"import 'package:flutter/material.dart';\nclass BuilderGridView extends StatelessWidget {\n final data = List.generate(128, (i) => Color(0xFF33FFF - 2*i));\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: GridView.builder(\n itemCount: data.length,\n scrollDirection: Axis.vertical,\n gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(//网格代理:定交叉轴数目\n crossAxisCount: 4,//条目个数\n mainAxisSpacing: 5,//主轴间距\n crossAxisSpacing: 5,//交叉轴间距\n childAspectRatio:1/0.618),\n itemBuilder: (_, int position)=> _buildItem(data[position])\n ),\n );\n }\n\n Container _buildItem(Color color) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 30,\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\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":163,"name":"GridView滑动方向","priority":2,"subtitle":" \n【scrollDirection】 : 滑动方向 【Axis】\n【reverse】 : 是否反向滑动 【bool】\n【shrinkWrap】 : 无边界时是否包裹 【bool】","code":"import 'package:flutter/material.dart';\nclass HorizontalGridView extends StatelessWidget {\n final data = List.generate(128, (i) => Color(0xFF00FFFF - 2*i));\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: GridView.count(\n scrollDirection: Axis.horizontal,\n reverse: true,\n crossAxisCount: 4,\n mainAxisSpacing: 2,\n crossAxisSpacing: 2,\n childAspectRatio: 0.618,\n children: data\n .map((color) => _buildItem(color))\n .toList(),\n ),\n );\n }\n\n Container _buildItem(Color color) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 30,\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\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":140,"name":"SnackBar基本使用","priority":1,"subtitle":" \n【content】 : 中间内容组件 【Widget】\n【action】 : 右侧按钮 【SnackBarAction】\n【duration】 : 持续时长 【Widget】\n【backgroundColor】 : 背景色 【Color】\n【elevation】 : 影深 【double】\n【shape】 : 形状 【ShapeBorder】\n【onVisible】 : 显示时回调 【Function()】","code":"import 'package:flutter/material.dart';\nclass CustomSnackBar extends StatefulWidget {\n @override\n _CustomSnackBarState createState() => _CustomSnackBarState();\n}\n\nclass _CustomSnackBarState extends State {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: FlatButton(\n color: Colors.blue,\n onPressed: () =>\n Scaffold.of(context).showSnackBar(_buildSnackBar()),\n child: Text(\n '点我弹出SnackBar',\n style: TextStyle(color: Colors.white),\n )));\n }\n\n Widget _buildSnackBar() {\n return SnackBar(\n elevation: 3,\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.only(\n topLeft: Radius.circular(10), topRight: Radius.circular(10))),\n content: Text('Wellcome to for join Flutter Unit!'),\n duration: Duration(seconds: 3),\n //持续时间\n backgroundColor: Colors.red,\n onVisible: () => print('onVisible'),\n action: SnackBarAction(\n textColor: Colors.white, label: '确定', onPressed: () {}),\n );\n }\n}\n"},{"id":null,"widgetId":206,"name":"TabPageSelectorIndicator基本使用","priority":1,"subtitle":"【size】: 大小 【double】\n【backgroundColor】: 背景色 【Color】\n【borderColor】: 边线色 【Color】","code":"import 'package:flutter/material.dart';\nclass TabPageSelectorIndicatorDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [\n TabPageSelectorIndicator(\n backgroundColor: Colors.greenAccent,\n borderColor: Colors.deepPurpleAccent,\n size: 15,\n ),\n TabPageSelectorIndicator(\n backgroundColor: Colors.blue,\n borderColor: Colors.grey,\n size: 20,\n ),\n TabPageSelectorIndicator(\n backgroundColor: Colors.green,\n borderColor: Colors.red,\n size: 25,\n ),\n TabPageSelectorIndicator(\n backgroundColor: Colors.yellow,\n borderColor: Colors.brown,\n size: 30,\n ),\n TabPageSelectorIndicator(\n backgroundColor: Colors.amber,\n borderColor: Colors.purpleAccent,\n size: 35,\n ),\n ],\n ),\n );\n }\n\n}\n\n"},{"id":null,"widgetId":16,"name":"ListTile的密排属性","priority":3,"subtitle":"【dense】: 是否密排 【bool】","code":"import 'package:flutter/material.dart';\nclass DenseListTile extends StatefulWidget {\n @override\n _DenseListTileState createState() => _DenseListTileState();\n}\n\nclass _DenseListTileState extends State {\n bool _dense = false;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n margin: EdgeInsets.all(10),\n color: Colors.grey.withAlpha(22),\n child: ListTile(\n leading: Image.asset(\"assets/images/icon_head.png\"),\n title: Text(\"以梦为马\"),\n subtitle: Text(\"海子\"),\n selected: false,\n contentPadding: EdgeInsets.all(5),\n trailing: Icon(Icons.more_vert),\n dense: _dense,\n onTap: () => setState(() => _dense = !_dense),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":16,"name":"ListTile选中效果和长按事件","priority":2,"subtitle":"【selected】: 是否选中 【bool】\n【onTap】: 点击事件 【Function()】","code":"import 'package:flutter/material.dart';\nclass SelectListTile extends StatefulWidget {\n @override\n _SelectListTileState createState() => _SelectListTileState();\n}\n\nclass _SelectListTileState extends State {\n bool _selected = false;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n margin: EdgeInsets.all(10),\n color: Colors.grey.withAlpha(22),\n child: ListTile(\n leading: Image.asset(\"assets/images/icon_head.png\"),\n selected: _selected,\n title: Text(\"以梦为马\"),\n subtitle: Text(\"海子\"),\n contentPadding: EdgeInsets.all(5),\n trailing: Icon(Icons.more_vert),\n onTap: () => setState(() => _selected = !_selected),\n ),\n );\n }\n}"},{"id":null,"widgetId":16,"name":"ListTile的基本表现如下","priority":1,"subtitle":"【leading】: 左侧组件 【Widget】\n【title】: 中间上组件 【Widget】\n【subtitle】: 中间下组件 【Widget】\n【trailing】: 尾组件 【Widget】\n【contentPadding】: 内边距 【EdgeInsetsGeometry】\n【onLongPress】: 点击事件 【Function()】","code":"import 'package:flutter/material.dart';\nclass CustomListTile extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n margin: EdgeInsets.all(10),\n color: Colors.grey.withAlpha(22),\n child: ListTile(\n leading: Image.asset(\"assets/images/icon_head.png\"),\n title: Text(\"以梦为马\"),\n subtitle: Text(\"海子\"),\n contentPadding: EdgeInsets.all(5),\n trailing: Icon(Icons.more_vert),\n onLongPress: () => Navigator.of(context).pushNamed('AboutMePage'),\n ),\n );\n }\n}"},{"id":null,"widgetId":20,"name":"GridTileBar的基本表现如下","priority":1,"subtitle":"【value】 : 条目对象 【T】\n【leading】: 左侧组件 【Widget】\n【trailing】: 尾组件 【Widget】\n【title】: 中间上组件 【Widget】\n【subtitle】: 中间下组件 【Widget】\n【backgroundColor】: 背景色 【Color】","code":"import 'package:flutter/material.dart';\nclass CustomGridTileBar extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return GridTileBar(\n backgroundColor: Colors.blue.withAlpha(120),\n trailing: Icon(\n Icons.star,\n color: Colors.red,\n ),\n leading: CircleAvatar(\n backgroundImage: AssetImage(\"assets/images/wy_200x300.jpg\"),\n ),\n title: Text(\"百里·巫缨\"),\n subtitle: Text(\"倾国必倾城\"),\n );\n }\n}\n"},{"id":null,"widgetId":21,"name":"GridTile的基本表现如下","priority":1,"subtitle":"【header】: 头组件 【Widget】\n【child】: 子组件 【Widget】\n【footer】: 脚组件 【Widget】","code":"import 'package:flutter/material.dart';\nclass CustomGridTile extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 200, height: 200,\n child: GridTile(\n header: GridTileBar(\n backgroundColor: Colors.blue.withAlpha(120),\n trailing: Icon(\n Icons.star,\n color: Colors.red,\n ),\n leading: CircleAvatar(\n backgroundImage: AssetImage(\"assets/images/wy_200x300.jpg\"),\n ),\n title: Text(\"百里·巫缨\"),\n subtitle: Text(\"倾国必倾城\"),\n ),\n child: Opacity(\n opacity: 0.5,\n child: Image.asset(\n \"assets/images/sabar.jpg\",\n fit: BoxFit.cover,\n ),\n ),\n footer: Padding(\n padding: const EdgeInsets.all(8.0),\n child: Text(\n \"ID:z\\$ySX32&29\",\n style: TextStyle(shadows: [\n Shadow(\n color: Colors.blue,\n offset: Offset(.1, .1),\n blurRadius: 2),\n ]),\n ),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":11,"name":"可以设置颜色和阴影","priority":2,"subtitle":"【backgroundColor】: 背景色 【Color】\n【shadowColor】: 阴影色 【Color】\n【elevation】: 影深 【double】","code":"import 'package:flutter/material.dart';\nclass ColorOfChip extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 20,\n children: [\n Chip(\n avatar: Image.asset(\"assets/images/icon_head.png\"),\n label: Text(\"张风捷特烈\"),\n padding: EdgeInsets.all(5),\n labelPadding: EdgeInsets.all(5),\n backgroundColor: Colors.grey.withAlpha(66),\n shadowColor: Colors.orangeAccent,\n elevation: 3,\n ),\n Chip(\n avatar: Image.asset(\"assets/images/icon_head.png\"),\n label: Text(\"张风捷特烈\"),\n padding: EdgeInsets.all(5),\n labelPadding: EdgeInsets.all(5),\n backgroundColor: Colors.cyanAccent.withAlpha(11),\n shadowColor: Colors.blue.withAlpha(88),\n elevation: 4,\n ),\n ],\n );\n }\n}"},{"id":null,"widgetId":11,"name":"可以设置右侧点击按钮","priority":3,"subtitle":"【deleteIcon】: 右侧组件(通常为Icon) 【Widget】\n【deleteIconColor】: 右侧组件颜色 【Color】\n【onDeleted】: 右侧组件点击事件 【Function】","code":"import 'package:flutter/material.dart';\nimport '../../../dialogs/dialog_about.dart';\nclass DeleteOfChip extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Chip(\n avatar: Image.asset(\"assets/images/icon_head.png\"),\n label: Text(\"张风捷特烈\"),\n padding: EdgeInsets.all(5),\n labelPadding: EdgeInsets.all(3),\n backgroundColor: Colors.grey.withAlpha(66),\n shadowColor: Colors.orangeAccent,\n// deleteIcon: Icon(Icons.close,size: 18),\n deleteIconColor: Colors.red,\n onDeleted: () => DialogAbout.show(context),\n elevation: 3,\n );\n }\n}"},{"id":null,"widgetId":11,"name":"Chip的普通表现如下","priority":1,"subtitle":"【avatar】: 左侧组件 【Widget】\n【label】: 中间组件 【Widget】\n【padding】 : 内边距 【EdgeInsetsGeometry】\n【labelPadding】: label边距 【EdgeInsetsGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomChip extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 20,\n children: [\n Chip(\n avatar: Image.asset(\"assets/images/icon_head.png\"),\n label: Text(\"张风捷特烈\"),\n padding: EdgeInsets.all(5),\n labelPadding: EdgeInsets.all(5),\n ),\n Chip(\n avatar: CircleAvatar(\n backgroundImage:\n AssetImage(\"assets/images/wy_200x300.jpg\")),\n label: Text(\"百里巫缨\"),\n padding: EdgeInsets.all(8),\n labelPadding: EdgeInsets.all(6),\n ),\n ],\n );\n }\n}"},{"id":null,"widgetId":214,"name":"NavigationToolbar基本使用","priority":1,"subtitle":"【leading】 : 左侧组件 【Widget】\n【middle】: 中间组件 【Widget】\n【trailing】: 右侧组件组件 【Widget】\n【centerMiddle】: 中间组件是否居中 【bool】\n【middleSpacing】: 中间组件距左距离 【double】","code":"import 'package:flutter/material.dart';\nclass NavigationToolbarDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Column(\n mainAxisSize: MainAxisSize.min,\n children: [\n Container(\n height: 60,\n child: NavigationToolbar(\n leading: Icon(Icons.ac_unit),\n middle: Text('middleSpacing#true'),\n middleSpacing: 20,\n centerMiddle: true,\n trailing: Icon(Icons.more_vert),\n ),\n ),\n Container(\n height: 60,\n child: NavigationToolbar(\n leading: Icon(Icons.ac_unit),\n middle: Text('middleSpacing#false'),\n middleSpacing: 20,\n centerMiddle: false,\n trailing: Icon(Icons.more_vert),\n ),\n ),\n ],\n );\n }\n}"},{"id":null,"widgetId":128,"name":"SimpleDialog基本使用","priority":1,"subtitle":" \n【title】 : 顶部组件 【Widget】\n【children】 : 子组件列表 【List】\n【titlePadding】 : 顶部内边距 【EdgeInsetsGeometry】\n【contentPadding】 : 内容内边距 【EdgeInsetsGeometry】\n【backgroundColor】 : 右下角组件列表 【背景色】\n【elevation】 : 右下角组件列表 【背景色】\n【shape】 : 影深 【double】","code":"import 'dart:math';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomSimpleDialog extends StatelessWidget {\n final info = [\n '性别: 男 未婚',\n '微信: zdl1994328',\n \"掘金: 张风捷特烈\",\n \"github: toly1994328\",\n \"邮箱: 1981462008@qq.com\",\n ];\n\n @override\n Widget build(BuildContext context) {\n return Stack(\n children: [\n _buildSimpleDialog(context),\n Positioned(\n top: 70,\n right: 30,\n child: _buildRaisedButton(context)),\n\n ],\n );\n }\n Widget _buildRaisedButton(BuildContext context) => RaisedButton(\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.all(Radius.circular(10))),\n color: Colors.blue,\n onPressed: () {\n showDialog(context: context, builder: (ctx) => _buildSimpleDialog(ctx));\n },\n child: Text(\n 'Just Show It',\n style: TextStyle(color: Colors.white),\n ),\n );\n\n SimpleDialog _buildSimpleDialog(BuildContext context) {\n return SimpleDialog(\n title: _buildTitle(),\n titlePadding: EdgeInsets.only(\n top: 5,\n left: 20,\n ),\n contentPadding: EdgeInsets.symmetric(horizontal: 5),\n children: _buildChild(context),\n backgroundColor: Colors.white,\n elevation: 4,\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.all(Radius.circular(10))),\n );\n }\n\n List _buildChild(BuildContext context) {\n return info\n .map((str) => Column(\n crossAxisAlignment: CrossAxisAlignment.start,\n children: [\n SimpleDialogOption(\n onPressed: () => print(str),\n child: Container(\n width: double.infinity,\n child: Text(\n str,\n style: TextStyle(color: Color(0xff999999), fontSize: 16),\n ),\n ),\n ),\n Divider(\n indent: 20,\n height: 12,\n color: info.indexOf(str) == info.length - 1\n ? Colors.transparent\n : Theme.of(context).dividerColor,\n )\n ],\n ))\n .toList();\n }\n\n Widget _buildTitle() {\n return Row(\n //标题\n children: [\n Image.asset(\n \"assets/images/icon_head.png\",\n width: 30,\n height: 30,\n ),\n SizedBox(\n width: 10,\n ),\n Expanded(\n child: Text(\n \"张风捷特烈\",\n style: TextStyle(fontSize: 18),\n )),\n CloseButton()\n ],\n );\n }\n}\n"},{"id":null,"widgetId":23,"name":"MaterialButton长按事件","priority":2,"subtitle":"【highlightColor】: 长按高亮色 【Color】\n【onLongPress】: 长按事件 【Function】","code":"import 'package:flutter/material.dart';\nclass LongPressMaterialButton extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return MaterialButton(\n height: 40,\n elevation: 5,\n color: Colors.blue,\n highlightColor: Colors.green,\n textColor: Colors.white,\n padding: EdgeInsets.all(8),\n child: Text(\"MaterialButton\"),\n onLongPress: () => Navigator.of(context).pushNamed('AboutMePage'),\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage'));\n }\n}"},{"id":null,"widgetId":23,"name":"MaterialButton点击事件","priority":1,"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】","code":"import 'package:flutter/material.dart';\nclass CustomMaterialButton extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return MaterialButton(\n height: 40,\n elevation: 5,\n color: Colors.orangeAccent,\n textColor: Colors.white,\n splashColor: Colors.blue,\n padding: EdgeInsets.all(8),\n child: Text(\"MaterialButton\"),\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage'));\n }\n}"},{"id":null,"widgetId":23,"name":"MaterialButton的自定义形状","priority":3,"subtitle":"【shape】: 形状 【ShapeBorder】","code":"import 'package:flutter/material.dart';\nclass ShapeMaterialButton extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 20,\n children: [\n Container(\n width: 40,\n height: 40,\n child: MaterialButton(\n padding: EdgeInsets.all(0),\n textColor: Color(0xffFfffff),\n elevation: 3,\n color: Colors.blue,\n highlightColor: Color(0xffF88B0A),\n splashColor: Colors.red,\n child: Icon(\n Icons.add,\n color: Colors.white,\n ),\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onLongPress: () => Navigator.of(context).pushNamed('AboutMePage'),\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage')),\n ),\n Container(\n width: 100,\n height: 40,\n child: MaterialButton(\n padding: EdgeInsets.all(0),\n textColor: Color(0xffFfffff),\n elevation: 3,\n color: Colors.blue,\n highlightColor: Color(0xffF88B0A),\n splashColor: Colors.red,\n child: Icon(\n Icons.remove,\n color: Colors.white,\n ),\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.all(Radius.circular(15))),\n onLongPress: () => Navigator.of(context).pushNamed('AboutMePage'),\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage')),\n ),\n ],\n );\n }\n}"},{"id":null,"widgetId":32,"name":"CloseButton点击事件","priority":1,"subtitle":" 点击时会退出当前栈","code":"import 'package:flutter/material.dart';\nclass CustomCloseButton extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return CloseButton();\n }\n}\n"},{"id":null,"widgetId":58,"name":"TabBar基本使用","priority":1,"subtitle":" \n【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】","code":"import 'package:flutter/material.dart';\nclass CustomTabBar extends StatefulWidget {\n @override\n _CustomTabBarState createState() => _CustomTabBarState();\n}\n\nclass _CustomTabBarState extends State\n with SingleTickerProviderStateMixin {\n final tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼'];\n TabController _tabController;\n\n @override\n void initState() {\n super.initState();\n _tabController = TabController(vsync: this, length: tabs.length);\n }\n\n @override\n void dispose() {\n _tabController.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return TabBar(\n onTap: (tab) {\n print(tab);\n },\n labelStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),\n unselectedLabelStyle: TextStyle(fontSize: 16),\n isScrollable: true,\n controller: _tabController,\n labelColor: Colors.blue,\n indicatorWeight: 3,\n indicatorPadding: EdgeInsets.symmetric(horizontal: 10),\n unselectedLabelColor: Colors.grey,\n indicatorColor: Colors.orangeAccent,\n tabs: tabs.map((e) => Tab(text: e)).toList(),\n );\n }\n}"},{"id":null,"widgetId":58,"name":"通过修改源码可实现无水波纹","priority":1,"subtitle":" \n详见:components/flutter/no_shadow_tab_bar.dart","code":"import 'package:flutter/material.dart';\nimport 'no_shadow_tab_bar.dart';\nclass NoShadowTabBarDemo extends StatefulWidget {\n @override\n _NSTabBarState createState() => _NSTabBarState();\n}\n\nclass _NSTabBarState extends State\n with SingleTickerProviderStateMixin {\n final tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼'];\n TabController _tabController;\n\n @override\n void initState() {\n super.initState();\n _tabController = TabController(vsync: this, length: tabs.length);\n }\n\n @override\n void dispose() {\n _tabController.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return NoShadowTabBar(\n onTap: (tab) {\n print(tab);\n },\n labelStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),\n unselectedLabelStyle: TextStyle(fontSize: 16),\n isScrollable: true,\n controller: _tabController,\n labelColor: Colors.blue,\n indicatorWeight: 3,\n indicatorPadding: EdgeInsets.symmetric(horizontal: 10),\n unselectedLabelColor: Colors.grey,\n indicatorColor: Colors.orangeAccent,\n tabs: tabs.map((e) => Tab(text: e)).toList(),\n );\n }\n}"},{"id":null,"widgetId":37,"name":"GridPage基础属性","priority":1,"subtitle":"【child】: 子组件 【Widget】\n【color】: 颜色 【Color】\n【interval】: 小块边长 【double】","code":"import 'package:flutter/material.dart';\nclass CustomGridPage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 200,\n height: 100,\n child: GridPaper(\n color: Colors.red,\n interval: 50,\n child: Image.asset(\n \"assets/images/wy_300x200.jpg\",\n fit: BoxFit.cover,\n )));\n }\n}"},{"id":null,"widgetId":37,"name":"GridPage再分割","priority":2,"subtitle":"【child】: 子组件 【Widget】\n【color】: 颜色 【Color】\n【subdivisions】: 小块中子块个数 【int】\n【divisions】: 小块中子块的分割数 【int】","code":"import 'package:flutter/material.dart';\nclass DivisionsGridPage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 200,\n height: 100,\n child: GridPaper(\n color: Colors.red,\n interval: 50,\n divisions: 4,\n subdivisions: 4,\n child: Image.asset(\n \"assets/images/wy_300x200.jpg\",\n fit: BoxFit.cover,\n )));\n }\n}\n"},{"id":null,"widgetId":129,"name":"CupertinoAlertDialog基本使用","priority":1,"subtitle":" \n【title】 : 顶部组件 【Widget】\n【content】 : 内容组件 【Widget】\n【actions】 : 顶部文字样式 【List】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoAlertDialog extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildRaisedButton(context),\n _buildCupertinoAlertDialog(context),\n ],\n );\n }\n\n Widget _buildRaisedButton(BuildContext context) => RaisedButton(\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.all(Radius.circular(10))),\n color: Colors.blue,\n onPressed: () {\n showDialog(\n context: context,\n builder: (ctx) => _buildCupertinoAlertDialog(context));\n },\n child: Text(\n 'Just Show It !',\n style: TextStyle(color: Colors.white),\n ),\n );\n\n Widget _buildCupertinoAlertDialog(BuildContext context) {\n return Material(\n color: Colors.transparent,\n child: CupertinoAlertDialog(\n title: _buildTitle(context),\n content: _buildContent(),\n actions: [\n CupertinoButton(\n child: Text(\"Yes, Delete\"),\n onPressed: () => Navigator.pop(context),\n ),\n CupertinoButton(\n child: Text(\"Cancle\"),\n onPressed: () => Navigator.pop(context),\n ),\n ]),\n );\n }\n\n Widget _buildTitle(context) {\n return Row(\n //标题\n children: [\n Icon(\n CupertinoIcons.delete_solid,\n color: Colors.red,\n ),\n Expanded(\n child: Text(\n 'Delete File',\n style: TextStyle(color: Colors.red, fontSize: 20),\n )),\n InkWell(\n child: Icon(CupertinoIcons.clear_thick),\n onTap: () => Navigator.pop(context),\n )\n ]);\n }\n\n Widget _buildContent() {\n return Padding(\n padding: const EdgeInsets.only(top: 18.0),\n child: Column(\n children: [\n Text(\n ' Hi toly! If you push the conform buttom ,'\n ' You will lose this file. Are you sure wand to do that?',\n style: TextStyle(color: Color(0xff999999), fontSize: 16),\n textAlign: TextAlign.justify,\n ),\n ],\n ),\n );\n }\n}\n"},{"id":null,"widgetId":218,"name":"返回按钮基本使用","priority":1,"subtitle":"【onPressed】 : 点击事件 【VoidCallback】\n【color】: 颜色 【Color】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CupertinoNavigationBarBackButtonDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: CupertinoNavigationBarBackButton(\n color: Colors.deepPurpleAccent,\n onPressed: ()=>Navigator.of(context).pop(),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":107,"name":"Spacer基本使用","priority":1,"subtitle":" \n一个Spacer会占据可延伸区域","code":"import 'package:flutter/material.dart';\nclass OneSpacer extends StatelessWidget {\n\n @override\n Widget build(BuildContext context) {\n return\n Container(\n color: Colors.grey.withAlpha(33),\n child: Row(children: [\n Container(\n alignment: Alignment.center,\n width: 100,\n height: 50,\n color: Colors.red,\n ),\n Spacer(),\n Container(\n alignment: Alignment.center,\n width: 60,\n height: 50,\n color: Colors.blue,\n ),\n ],),\n );\n }\n}"},{"id":null,"widgetId":148,"name":"Tab基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【text】 : 文字 【String】\n【icon】 : 下方组件 【Widgit】\n text和child不能同时存在","code":"import 'package:flutter/material.dart';\nclass CustomTab extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [\n Tab(\n icon:Icon( Icons.add,color: Colors.blue,),\n child: Text('添加'),\n ),\n Tab(\n icon:Icon( Icons.close,color: Colors.red,),\n text: '删除',\n ),\n Tab(\n icon:Icon( Icons.refresh,color: Colors.green),\n text: '更新',\n ),\n ],\n ),\n );\n }\n}\n"},{"id":null,"widgetId":107,"name":"多个Spacer空间分配","priority":2,"subtitle":" \n【flex】 : 剩余空间分配占比 【int】","code":"import 'package:flutter/material.dart';\nclass ManySpacer extends StatefulWidget {\n @override\n _ManySpacerState createState() => _ManySpacerState();\n}\n\nclass _ManySpacerState extends State {\n int _flexA=1;\n int _flexB=1;\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildSliders(),\n Container(\n color: Colors.grey.withAlpha(33),\n child: Row(children: [\n Spacer(flex: _flexA),\n Container(\n alignment: Alignment.center,\n width: 100,\n height: 50,\n color: Colors.red,\n ),\n Spacer(flex: _flexB),\n Container(\n alignment: Alignment.center,\n width: 60,\n height: 50,\n color: Colors.blue,\n ),\n ],),\n )\n ],\n );\n }\n\n Widget _buildSliders() {\n return Column(\n children: [\n Slider(\n divisions: 20,\n min: 1,\n max: 10,\n label: \"左边flex: $_flexA\",\n value: _flexA.toDouble(),\n onChanged: (v) => setState(() => _flexA = v.round())\n ),\n Slider(\n divisions: 20,\n label: \"右边flex: $_flexB\",\n min: 1,\n max: 10,\n value: _flexB.toDouble(),\n onChanged: (v) => setState(() => _flexB = v.round())\n ),\n ],\n );\n }\n}"},{"id":null,"widgetId":35,"name":"VerticalDivider颜色和粗细","priority":1,"subtitle":"【color】: 颜色 【Color】\n【thickness】: 线粗细 【double】","code":"import 'package:flutter/material.dart';\nclass CustomVerticalDivider extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var dataColor = [\n Colors.red, Colors.yellow,\n Colors.blue, Colors.green];\n var dataThickness = [1.0, 2.0, 4.0, 6.0];\n var data = Map.fromIterables(dataColor, dataThickness);\n return Container(\n height: 150,\n child: Row(\n mainAxisSize: MainAxisSize.min,\n children: dataColor\n .map((e) => VerticalDivider(\n color: e,\n thickness: data[e],\n ))\n .toList(),\n ),\n );\n }\n}"},{"id":null,"widgetId":35,"name":"VerticalDivider宽度和空缺","priority":2,"subtitle":"【indent】: 前面空缺长度 【double】\n【endIndent】: 后面空缺长度 【double】\n【height】: 占位高 【double】","code":"import 'package:flutter/material.dart';\nclass HeightVerticalDivider extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var dataColor = [\n Colors.red, Colors.yellow,\n Colors.blue, Colors.green];\n var dataThickness = [10.0, 20.0, 30.0, 40.0];\n var data = Map.fromIterables(dataColor, dataThickness);\n return Container(\n height: 150,\n child: Row(\n mainAxisSize: MainAxisSize.min,\n children: dataColor\n .map((e) => VerticalDivider(\n color: e,\n indent:data[e],\n endIndent: data[e]*2,\n width: data[e],\n thickness: data[e]/10,\n ))\n .toList(),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":7,"name":"用于显示一个纯色图片","priority":1,"subtitle":"【入参】 : 图片资源 【ImageProvider】\n【size】 : 大小 【double】\n【color】: 角标颜色 【Color】","code":"import 'package:flutter/material.dart';\nclass CustomImageIcon extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var data = {\n Colors.blue: 50.0,\n Colors.red: 60.0,\n Colors.green: 70.0,\n Colors.yellow: 80.0,\n };\n return Wrap(\n spacing: 10,\n children: data.keys\n .map((e) => ImageIcon(\n AssetImage(\"assets/images/leaf.png\"),\n color: e,\n size: data[e],\n ))\n .toList(),\n );\n }\n}\n"},{"id":null,"widgetId":22,"name":"右上角和底部","priority":2,"subtitle":"【otherAccountsPictures】: 右上组件 【List】\n【onDetailsPressed】: 右下角点击事件 【Function()】\n【arrowColor】: 右下角按钮颜色 【Color】\n【margin】: 外边距 【EdgeInsetsGeometry】","code":"import 'package:flutter/material.dart';\nclass ProUAGHP extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width / 3 * 2,\n child: UserAccountsDrawerHeader(\n margin: EdgeInsets.all(10),\n accountName: Container(\n padding: const EdgeInsets.all(8.0),\n child: Text(\n \"张风捷特烈\",\n style:\n TextStyle(color: Colors.orangeAccent, fontSize: 22,\n shadows: [\n Shadow(\n color: Colors.black,\n offset: Offset(.5, .5),\n blurRadius: 2),\n ]),\n ),\n ),\n accountEmail: Padding(\n padding: const EdgeInsets.all(8.0),\n child: Text(\"1981462002@qq.com\",\n style: TextStyle(\n color: Colors.white, fontSize: 14,\n shadows: [\n Shadow(\n color: Colors.orangeAccent,\n offset: Offset(.5, .5),\n blurRadius: 2),\n ])),\n ),\n currentAccountPicture: Container(\n padding: const EdgeInsets.all(15.0),\n child: CircleAvatar(\n backgroundImage: AssetImage(\"assets/images/icon_head.png\"),\n ),\n ),\n otherAccountsPictures: [\n FlutterLogo(colors: Colors.green),\n ],\n onDetailsPressed: () {\n\n },\n arrowColor: Colors.white,\n decoration: BoxDecoration(\n image: DecorationImage(\n image: AssetImage(\"assets/images/caver.jpeg\")),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":22,"name":"该组件的基本表现如下","priority":1,"subtitle":"【currentAccountPicture】: 上组件 【Widget】\n【accountName】: 中组件 【Widget】\n【accountEmail】: 下组件 【Widget】\n【decoration】: 装饰 【Decoration】","code":"import 'package:flutter/material.dart';\nclass CustomUAGHP extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width / 3 * 2,\n child: UserAccountsDrawerHeader(\n accountName: Container(\n padding: const EdgeInsets.all(8.0),\n child: Text(\n \"张风捷特烈\",\n style:\n TextStyle(color: Colors.orangeAccent, fontSize: 22, shadows: [\n Shadow(\n color: Colors.black, offset: Offset(.5, .5), blurRadius: 2),\n ]),\n ),\n ),\n accountEmail: Padding(\n padding: const EdgeInsets.all(8.0),\n child: Text(\"1981462002@qq.com\",\n style: TextStyle(color: Colors.white, fontSize: 14, shadows: [\n Shadow(\n color: Colors.orangeAccent,\n offset: Offset(.5, .5),\n blurRadius: 2),\n ])),\n ),\n currentAccountPicture: Container(\n padding: const EdgeInsets.all(15.0),\n child: CircleAvatar(\n backgroundImage: AssetImage(\"assets/images/icon_head.png\"),\n ),\n ),\n decoration: BoxDecoration(\n image: DecorationImage(image: AssetImage(\"assets/images/caver.jpeg\")),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":126,"name":"Dialog基本使用","priority":1,"subtitle":" \n【child】 : 动画图标数据 【Widget】\n【elevation】 : 影深 【double】\n【backgroundColor】 : 背景色 【Color】\n【shape】 : 形状 【ShapeBorder】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomDialog extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildRaisedButton(context),\n _buildDialog(),\n ],\n );\n }\n\n Widget _buildDialog() => Dialog(\n backgroundColor: Colors.white,\n elevation: 5,\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.all(Radius.circular(10))),\n child: Container(\n width: 50,\n child: DeleteDialog(),\n ),\n );\n\n Widget _buildRaisedButton(BuildContext context) => RaisedButton(\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.all(Radius.circular(10))),\n color: Colors.blue,\n onPressed: () {\n showDialog(context: context, builder: (ctx) => _buildDialog());\n },\n child: Text(\n 'Just Show It !',\n style: TextStyle(color: Colors.white),\n ),\n\n );\n}\n\nclass DeleteDialog extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n\n child: Column(\n mainAxisSize: MainAxisSize.min,\n children: [\n _buildBar(context),\n _buildTitle(),\n _buildContent(),\n _buildFooter(context),\n ],\n ),\n );\n }\n\n Widget _buildTitle() {\n return Text(\n 'Delete Doucument',\n style: TextStyle(color: Color(0xff5CC5E9), fontSize: 24),\n );\n }\n\n Widget _buildContent() {\n return Padding(\n padding: const EdgeInsets.all(15.0),\n child: Text(\n ' Hi toly! If you push the conform buttom ,'\n ' You will lose this file. Are you sure wand to do that?',\n style: TextStyle(color: Color(0xffCFCFCF), fontSize: 16),\n textAlign: TextAlign.justify,\n ),\n );\n }\n\n Widget _buildFooter(context) {\n return Padding(\n padding: const EdgeInsets.only(bottom: 15.0, top: 10,left: 10,right: 10),\n child: Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [\n Container(\n alignment: Alignment.center,\n height: 40,\n width: 100,\n decoration: BoxDecoration(\n borderRadius: BorderRadius.all(Radius.circular(30)),\n color: Color(0xff73D1EE)),\n child: Text('Yes',\n style: TextStyle(color: Colors.white, fontSize: 16)),\n ),\n InkWell(\n onTap: ()=>Navigator.of(context).pop(),\n child: Container(\n alignment: Alignment.center,\n height: 40,\n width: 100,\n decoration: BoxDecoration(\n borderRadius: BorderRadius.all(Radius.circular(30)),\n color: Colors.orangeAccent),\n child: Text('Cancle',\n style: TextStyle(color: Colors.white, fontSize: 16)),\n ),\n )\n ],\n ),\n );\n }\n\n _buildBar(context) => Container(\n height: 30,\n alignment: Alignment.centerRight,\n margin: EdgeInsets.only(right: 10, top: 5),\n child: InkWell(\n onTap: ()=>Navigator.of(context).pop(),\n child: Icon(\n Icons.close,\n color: Color(0xff82CAE3),\n ),\n ),\n );\n}\n"},{"id":null,"widgetId":131,"name":"CupertinoActionSheet基本使用","priority":1,"subtitle":" \n【title】 : 第一行组件 【Widget】\n【message】 : 第二行组件 【Widget】\n【cancelButton】 : 取消按钮处组件 【Widget】\n【actions】 : 中间组件列表 【List】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoActionSheet extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Column(\n mainAxisSize: MainAxisSize.min,\n children: [\n _buildRaisedButton(context),\n _buildCupertinoActionSheet(context),\n ],\n );\n }\n\n Widget _buildCupertinoActionSheet(BuildContext context) =>\n Container(\n alignment: Alignment.bottomCenter,\n child: CupertinoActionSheet(\n title: Text(\"Please chose a language\"),\n message: Text('the language you use in this application.'),\n cancelButton: CupertinoActionSheetAction(\n onPressed: () => Navigator.pop(context), child: Text(\"Cancel\")),\n actions: [\n CupertinoActionSheetAction(\n onPressed: () => Navigator.pop(context), child: Text('Dart')),\n CupertinoActionSheetAction(\n onPressed: () => Navigator.pop(context), child: Text('Java')),\n CupertinoActionSheetAction(\n onPressed: () => Navigator.pop(context), child: Text('Kotlin')),\n ],\n ),\n );\n\n Widget _buildRaisedButton(BuildContext context) => RaisedButton(\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.all(Radius.circular(10))),\n color: Colors.blue,\n onPressed: () => showDialog(\n context: context,\n builder: (ctx) => _buildCupertinoActionSheet(context)),\n child: Text(\n 'Just Show It !',\n style: TextStyle(color: Colors.white),\n ),\n );\n}\n"},{"id":null,"widgetId":155,"name":"DrawerHeader基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【decoration】 : 装饰 【Decoration】\n【margin】 : 外边距 【EdgeInsetsGeometry】\n【padding】 : 内边距 【EdgeInsetsGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomDrawerHeader extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 400,\n child: Scaffold(\n appBar: AppBar(\n title: Text('Flutter Unit'),\n ),\n drawer: Drawer(\n elevation: 3,\n child: _buildChild(),\n ),\n ),\n );\n }\n\n Widget _buildChild() => ListView(\n padding: EdgeInsets.zero,\n children: [\n _buildHeader(),\n ListTile(\n leading: Icon(\n Icons.star,\n color: Colors.blue,\n ),\n title: Text('我的收藏'),\n ),\n ListTile(\n leading: Icon(\n Icons.palette,\n color: Colors.orangeAccent,\n ),\n title: Text('我的绘画'),\n ),\n ListTile(\n leading: Icon(\n Icons.insert_drive_file,\n color: Colors.green,\n ),\n title: Text('我的文件'),\n ),\n ],\n );\n\n Widget _buildHeader() {\n return DrawerHeader(\n margin: EdgeInsets.all(10),\n padding: EdgeInsets.only(left: 20,top: 15),\n decoration: BoxDecoration(\n borderRadius: BorderRadius.only(\n topLeft:Radius.circular(40),\n topRight:Radius.circular(40)\n ),\n image: DecorationImage(\n image: AssetImage('assets/images/caver.jpeg'),\n fit: BoxFit.cover),\n ),\n child: Text(\n '张风捷特烈',\n style: TextStyle(fontSize: 24, color: Colors.white, shadows: [\n Shadow(color: Colors.black, offset: Offset(1, 1), blurRadius: 3)\n ]),\n ),\n );\n }\n}"},{"id":null,"widgetId":1,"name":"Container还具有变换性","priority":5,"subtitle":"【transform】 : 变换矩阵 【Matrix4】\n基于Matrix4的矩阵变换,变换详情见线性代数","code":"import 'package:flutter/material.dart';\nimport 'dart:math';\nclass ContainerTransform extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n //容器\n alignment: Alignment.center,\n color: Colors.cyanAccent,\n width: 150,\n height: 150 * 0.618,\n transform: Matrix4.skew(-pi / 10, 0),\n child: Text(\n \"Container\",\n style: TextStyle(fontSize: 20),\n ),\n );\n }\n}"},{"id":null,"widgetId":1,"name":"Container的约束性","priority":6,"subtitle":" \n【constraints】 : 约束 【BoxConstraints】\n会约束该区域的尺寸,不会小于指定的最小宽高,也不会大于指定的最大宽高。","code":"import 'package:flutter/material.dart';\nclass ContainerConstraints extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n //容器\n color: Colors.blue,\n width: 200,\n height: 200 * 0.618,\n constraints: BoxConstraints(\n minWidth: 100, maxWidth: 150,\n minHeight: 20, maxHeight: 100),\n );\n }\n}"},{"id":null,"widgetId":1,"name":"可对子组件进行对齐定位","priority":3,"subtitle":"【alignment】 : 对齐定位 【AlignmentGeometry】","code":"import 'package:flutter/material.dart';\nclass ContainerAlignment extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n alignment: Alignment.bottomRight,\n width: 200,\n height: 200 * 0.618,\n color: Colors.grey.withAlpha(88),\n child: Icon(\n Icons.android,\n color: Colors.green,\n ),\n );\n }\n}"},{"id":null,"widgetId":1,"name":"可用于显示一个指定宽高的区域","priority":1,"subtitle":"【width】 : 宽 【int】\n【高】: 外边距 【int】\n【color】: 子组件 【Color】","code":"import 'package:flutter/material.dart';\nclass CustomContainer extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n alignment: Alignment.topLeft,\n width: 200,\n height: 200 * 0.618,\n color: Colors.red.withAlpha(88),\n );\n }\n}"},{"id":null,"widgetId":1,"name":"可以在区域中放入一个子组件","priority":2,"subtitle":"【padding】 : 内边距 【EdgeInsetsGeometry】\n【margin】: 外边距 【EdgeInsetsGeometry】\n【child】: 子组件 【Widget】","code":"import 'package:flutter/material.dart';\nclass ContainerWithChild extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n alignment: Alignment.topLeft,\n padding: EdgeInsets.all(20),\n margin: EdgeInsets.all(10),\n width: 200,\n height: 200 * 0.618,\n color: Colors.grey.withAlpha(88),\n child: Icon(Icons.android),\n );\n }\n}"},{"id":null,"widgetId":1,"name":"可对子组件进行装饰","priority":4,"subtitle":" \n【decoration】 : 装饰 【Decoration】\n可装饰: 边线、圆弧、颜色、渐变色、阴影、图片等内容","code":"import 'package:flutter/material.dart';\nclass ContainerDecoration extends StatelessWidget {\n\n static const rainbow = [\n 0xffff0000,\n 0xffFF7F00,\n 0xffFFFF00,\n 0xff00FF00,\n 0xff00FFFF,\n 0xff0000FF,\n 0xff8B00FF\n ];\n\n @override\n Widget build(BuildContext context) {\n var stops = [0.0, 1 / 6, 2 / 6, 3 / 6, 4 / 6, 5 / 6, 1.0];\n return Container(//容器\n alignment: Alignment.center,\n width: 200,\n height: 200 * 0.618,\n margin: EdgeInsets.all(20),\n padding: EdgeInsets.all(20),\n decoration: BoxDecoration(//添加渐变色\n gradient: LinearGradient(\n stops: stops,\n colors: rainbow.map((e) => Color(e)).toList()),\n borderRadius: BorderRadius.only(\n topLeft: Radius.circular(50),\n bottomRight: Radius.circular(50)),\n boxShadow: [\n BoxShadow(\n color: Colors.grey,\n offset: Offset(1, 1),\n blurRadius: 10,\n spreadRadius: 1),\n ]),\n child: Text(\n \"Container\",\n style: TextStyle(fontSize: 20),\n ),\n );\n }\n}"},{"id":null,"widgetId":162,"name":"ListView.separated构造","priority":3,"subtitle":" \n【separatorBuilder】 : 条目构造器 【IndexedWidgetBuilder】","code":"import 'package:flutter/material.dart';\nclass SeparatedListView 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: ListView.separated(\n separatorBuilder: (context, index) => Divider(\n thickness: 1,\n height: 1,\n color: Colors.orange,\n ),\n itemCount: data.length,\n itemBuilder: (context, index) => _buildItem(data[index]),\n ),\n );\n }\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n\n Widget _buildItem(Color 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(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2)\n ]),\n ),\n );\n}\n"},{"id":null,"widgetId":162,"name":"ListView基本使用","priority":1,"subtitle":" \n【children】 : 子组件列表 【List】\n【padding】 : 点击事件 【EdgeInsetsGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomListView 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: 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 String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":162,"name":"ListView.builder构造","priority":3,"subtitle":" \n【itemCount】 : 条目个数 【int】\n【itemBuilder】 : 条目构造器 【IndexedWidgetBuilder】","code":"import 'package:flutter/material.dart';\nclass BuilderListView 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: ListView.builder(\n itemCount: data.length,\n itemBuilder: (context, index) => _buildItem(data[index]),\n ),\n );\n }\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n\n Widget _buildItem(Color 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(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2)\n ]),\n ),\n );\n}"},{"id":null,"widgetId":162,"name":"ListView横向滑动","priority":2,"subtitle":" \n【scrollDirection】 : 滑动方向 【Axis】\n【reverse】 : 是否反向滑动 【bool】\n【shrinkWrap】 : 无边界时是否包裹 【bool】","code":"import 'package:flutter/material.dart';\nclass HorizontalListView 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: ListView(\n reverse: true,\n shrinkWrap: true,\n scrollDirection: Axis.horizontal,\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 String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":12,"name":"ChoiceChip的普通表现如下","priority":1,"subtitle":"【selectedColor】: 选中时颜色 【Color】\n【selectedShadowColor】: 选中时阴影颜色 【Color】\n【onSelected】: 选中事件 【Fuction(bool)】\n 其他属性同Chip组件,无右侧组件。","code":"import 'package:flutter/material.dart';\nclass CustomChoiceChip extends StatefulWidget {\n @override\n _CustomChoiceChipState createState() => _CustomChoiceChipState();\n}\n\nclass _CustomChoiceChipState extends State {\n bool _select = false;\n\n @override\n Widget build(BuildContext context) {\n return ChoiceChip(\n selected: _select,\n padding: EdgeInsets.all(5),\n labelPadding: EdgeInsets.all(5),\n label: Text(\n _select ?\n \"You are selected it.\" :\n \"This is a ChoiceChip.\",\n style: TextStyle(fontSize: 16),\n ),\n backgroundColor: Colors.grey.withAlpha(66),\n avatar: Image.asset(\"assets/images/icon_head.png\"),\n selectedColor: Colors.orangeAccent.withAlpha(44),\n selectedShadowColor: Colors.blue,\n shadowColor: Colors.orangeAccent,\n elevation: 3,\n onSelected: (value) => setState(() => _select = value),\n );\n }\n}"},{"id":null,"widgetId":141,"name":"SnackBarAction基本使用","priority":1,"subtitle":" \n【label】 : 标签 【String】\n【textColor】 : 文字颜色 【Color】\n【disabledTextColor】 : 文字失效色 【Color】\n【onPressed】 : 点击回调 【Function()】","code":"import 'package:flutter/material.dart';\nclass CustomSnackBarAction extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: SnackBarAction(\n disabledTextColor: Colors.red,\n textColor: Colors.blue,\n label: '确定',\n onPressed: () => print('onPressed')));\n }\n}\n"},{"id":null,"widgetId":168,"name":"文字样式-ThemeData#TextTheme","priority":1,"subtitle":" \n子组件可以通过ThemeData.of获取主题的数据进行使用。","code":"import 'package:flutter/material.dart';\nclass TextThemeDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var queryData = Theme.of(context).textTheme;\n var styles = {\n \"headline: \": queryData.headline,\n \"title: \": queryData.title,\n \"subhead: \": queryData.subhead,\n \"subtitle: \": queryData.body1,\n \"body2: \": queryData.body2,\n \"button: \": queryData.button,\n \"overline: \": queryData.overline,\n \"subtitle: \": queryData.subtitle,\n \"button: \": queryData.caption,\n \"display1: \": queryData.display1,\n \"display2: \": queryData.display2,\n \"display3: \": queryData.display3,\n \"display4: \": queryData.display4,\n };\n\n return Container(\n child: Column(\n children: styles.keys.map((e) => buildItem(e, styles[e])).toList(),\n ),\n );\n }\n\n Widget buildItem(String e, TextStyle style) => Column(\n children: [\n Padding(\n padding: const EdgeInsets.all(8.0),\n child: Row(\n mainAxisAlignment: MainAxisAlignment.spaceBetween,\n children: [\n Text(\n e,\n style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),\n ),\n Text(\n \"@toly\",\n style: style,\n )\n ],\n ),\n ),\n Divider(\n height: 1,\n )\n ],\n );\n}"},{"id":null,"widgetId":168,"name":"Theme的用法","priority":2,"subtitle":" \n使用Theme,可以指定非常多的属性作为主题,这些属性将应用于所有的后代组件,如指定字体、滑块、卡片、文字、分割线、按钮等属性。","code":"import 'package:flutter/material.dart';\nclass CustomTheme extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Theme(\n data: ThemeData(\n cardTheme: CardTheme(color: Colors.red, elevation: 4),\n dividerTheme: DividerThemeData(\n color: Colors.blue,\n thickness: 2\n ),\n sliderTheme: SliderThemeData(\n thumbColor: Colors.red,\n activeTrackColor: Colors.green,\n inactiveTrackColor: Colors.grey,\n )),\n child: Wrap(\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n Card(\n child: Container(\n width: 50,\n height: 50,\n color: Colors.transparent,\n ),\n ),\n Container(\n width: 150,\n child: Slider(value: 0.8, onChanged: (v) => {})),\n Container( width: 150,child: Divider())\n ]));\n }\n}"},{"id":null,"widgetId":15,"name":"FilterChip可接受选择事件","priority":1,"subtitle":"【selected】: 是否选择 【bool】\n【onSelected】: 选择事件 【Function(bool)】\n【selectedColor】: 选择后的颜色 【Color】\n【selectedShadowColor】: 选择后的阴影颜色 【Color】,","code":"import 'package:flutter/material.dart';\nclass CustomFilterChip extends StatefulWidget {\n @override\n _CustomFilterChipState createState() => _CustomFilterChipState();\n}\n\nclass _CustomFilterChipState extends State {\n final Map map = {\n 'A': 'Ant',\n 'B': 'Bug',\n 'C': 'Cat',\n 'D': 'Dog',\n };\n List _selected = [];\n\n @override\n Widget build(BuildContext context) {\n return Column(\n mainAxisAlignment: MainAxisAlignment.center,\n children: [\n Wrap(\n children: map.keys.map((key) =>\n _buildChild(key)).toList(),\n ),\n Container(\n padding: EdgeInsets.all(10),\n child: Text('您已选择: ${_selected.join(', ')}')),\n ],\n );\n }\n\n Padding _buildChild(String key) {\n return Padding(\n padding: const EdgeInsets.all(4.0),\n child: FilterChip(\n selectedColor: Colors.orange.withAlpha(55),\n selectedShadowColor: Colors.blue,\n shadowColor: Colors.orangeAccent,\n pressElevation: 5,\n elevation: 3,\n avatar: CircleAvatar(child: Text(key)),\n label: Text(map[key]),\n selected: _selected.contains(map[key]),\n onSelected: (bool value) {\n setState(() {\n if (value) {\n _selected.add(map[key]);\n } else {\n _selected.removeWhere((name) => name == map[key]);\n }\n });\n },\n ),\n );\n }\n}\n"},{"id":null,"widgetId":352,"name":"CupertinoDialogAction基本使用","priority":1,"subtitle":"【isDefaultAction】 : 是否是默认性操作 【bool】\n【isDestructiveAction】 : 是否是毁灭性操作 【bool】\n【textStyle】: 文字样式 【TextStyle】\n【onPressed】: 点击事件 【VoidCallback】\n【child】: 子组件 【Widget】","code":"import 'dart:async';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'dart:ui' as ui;\nclass CupertinoDialogActionDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n CupertinoDialogAction(\n isDestructiveAction: false,\n onPressed: ()=>_toast(context),\n child: Text('CupertinoDialogAction'),\n ),\n CupertinoDialogAction(\n isDestructiveAction: true,\n onPressed: ()=>_toast(context),\n child: Text('CupertinoDialogAction'),\n ),\n ],\n );\n }\n\n _toast(BuildContext context){\n Scaffold.of(context).showSnackBar(SnackBar(\n backgroundColor: Theme.of(context).primaryColor,\n content: Text('CupertinoDialogAction'),));\n }\n}\n"},{"id":null,"widgetId":34,"name":"Divider颜色和粗细","priority":1,"subtitle":"【color】: 颜色 【Color】\n【thickness】: 线粗细 【double】","code":"import 'package:flutter/material.dart';\nclass CustomDivider extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var dataColor = [\n Colors.red, Colors.yellow,\n Colors.blue, Colors.green];\n var dataThickness = [1.0, 2.0, 4.0, 6.0];\n var data = Map.fromIterables(dataColor, dataThickness);\n return Column(\n children: dataColor\n .map((e) => Divider(\n color: e,\n thickness: data[e],\n ))\n .toList(),\n );\n }\n}"},{"id":null,"widgetId":34,"name":"Divider高度和空缺","priority":2,"subtitle":"【indent】: 前面空缺长度 【double】\n【endIndent】: 后面空缺长度 【double】\n【height】: 占位高 【double】","code":"import 'package:flutter/material.dart';\nclass HeightDivider extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var dataColor = [\n Colors.red, Colors.yellow,\n Colors.blue, Colors.green];\n var dataThickness = [10.0, 20.0, 30.0, 40.0];\n var data = Map.fromIterables(dataColor, dataThickness);\n return Column(\n children: dataColor\n .map((e) => Divider(\n color: e,\n indent:data[e],\n endIndent: data[e]*2,\n height: data[e],\n thickness: data[e]/10,\n ))\n .toList(),\n );\n }\n}\n"},{"id":null,"widgetId":205,"name":"TabPageSelector基本使用","priority":1,"subtitle":"【controller】 : 控制器 【TabController】\n【indicatorSize】: 指示器大小 【double】\n【selectedColor】: 选中色 【Color】\n【color】: 颜色 【Color】","code":"import 'package:flutter/material.dart';\nclass TabPageSelectorDemo extends StatefulWidget {\n @override\n _TabPageSelectorDemoState createState() => _TabPageSelectorDemoState();\n}\n\nclass _TabPageSelectorDemoState extends State\n with SingleTickerProviderStateMixin {\n final tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼'];\n TabController _tabController;\n\n @override\n void initState() {\n super.initState();\n _tabController = TabController(vsync: this, length: tabs.length);\n }\n\n @override\n void dispose() {\n _tabController.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: Stack(\n alignment: Alignment.center,\n children: [\n Container(color: Colors.purple, child: _buildTableBarView()),\n Positioned(\n bottom: 10,\n child: buildTabPageSelector(),\n ),\n ],\n ),\n );\n }\n\n Widget buildTabPageSelector() => TabPageSelector(\n controller: _tabController,\n color: Colors.white,\n indicatorSize: 10,\n selectedColor: Colors.orangeAccent,\n );\n\n Widget _buildTableBarView() => TabBarView(\n controller: _tabController,\n children: tabs\n .map((e) => Center(\n child: Text(\n e,\n style: TextStyle(color: Colors.white, fontSize: 20),\n )))\n .toList());\n}\n"},{"id":null,"widgetId":142,"name":"BottomSheet基本使用","priority":1,"subtitle":" \n【builder】 : 组件构造器 【WidgetBuilder】\n【backgroundColor】 : 背景色 【Color】\n【elevation】 : 影深 【double】\n【shape】 : 形状 【ShapeBorder】\n【onClosing】 : 关闭回调 【Function()】","code":"import 'package:flutter/material.dart';\nclass CustomBottomSheet extends StatefulWidget {\n @override\n _CustomBottomSheetState createState() => _CustomBottomSheetState();\n}\n\nclass _CustomBottomSheetState extends State {\n bool opened = false;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: FlatButton(\n color: Colors.blue,\n onPressed: () {\n opened = !opened;\n opened\n ? Scaffold.of(context)\n .showBottomSheet((_) => _buildBottomSheet())\n : Navigator.of(context).pop();\n },\n child: Text(\n '点我显隐BottomSheet',\n style: TextStyle(color: Colors.white),\n )));\n }\n\n Widget _buildBottomSheet() => BottomSheet(\n enableDrag: true,\n elevation: 4,\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.only(\n topRight: Radius.circular(60),\n topLeft: Radius.circular(60),\n )\n ),\n backgroundColor: Colors.transparent,\n onClosing: () => print('onClosing'),\n builder: (_) => (Container(\n height: 250,\n decoration: BoxDecoration(\n image: DecorationImage(\n image: AssetImage('assets/images/sabar_bar.jpg'),\n fit: BoxFit.cover),\n borderRadius: BorderRadius.only(\n topRight: Radius.circular(60),\n topLeft: Radius.circular(60),\n )),\n )));\n}\n"},{"id":null,"widgetId":17,"name":"CheckBoxListTile的选中效果","priority":2,"subtitle":"【selected】: 是否选中 【bool】","code":"import 'package:flutter/material.dart';\nclass SelectCheckBoxListTile extends StatefulWidget {\n @override\n _SelectCheckBoxListTileState createState() => _SelectCheckBoxListTileState();\n}\n\nclass _SelectCheckBoxListTileState extends State {\n var _selected = false;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n margin: EdgeInsets.all(10),\n color: Colors.grey.withAlpha(22),\n child: CheckboxListTile(\n value: _selected,\n selected: _selected,\n checkColor: Colors.yellow,\n activeColor: Colors.orangeAccent,\n secondary: Image.asset(\"assets/images/icon_head.png\"),\n title: Text(\"张风捷特烈\"),\n subtitle: Text(\"@万花过尽知无物\"),\n onChanged: (v) => setState(() => _selected = !_selected),\n ),\n );\n }\n}"},{"id":null,"widgetId":17,"name":"CheckBoxListTile的密排属性","priority":3,"subtitle":"【dense】: 是否密排 【bool】","code":"import 'package:flutter/material.dart';\nclass DenseCheckBoxListTile extends StatefulWidget {\n @override\n _DenseCheckBoxListTileState createState() => _DenseCheckBoxListTileState();\n}\n\nclass _DenseCheckBoxListTileState extends State {\n var _selected = false;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n margin: EdgeInsets.all(10),\n color: Colors.grey.withAlpha(22),\n child: CheckboxListTile(\n value: _selected,\n dense: true,\n checkColor: Colors.yellow,\n activeColor: Colors.orangeAccent,\n secondary: Image.asset(\"assets/images/icon_head.png\"),\n title: Text(\"张风捷特烈\"),\n subtitle: Text(\"@万花过尽知无物\"),\n onChanged: (v) => setState(() => _selected = !_selected),\n ),\n );\n }\n}"},{"id":null,"widgetId":17,"name":"CheckBoxListTile的基本表现如下","priority":1,"subtitle":"【secondary】: 左侧组件 【Widget】\n【checkColor】: ✔️颜色 【Color】\n【activeColor】: 选中时外框颜色 【Color】\n【title】: 中间上组件 【Widget】\n【subtitle】: 中间下组件 【Widget】\n【onChanged】: 选中事件 【Function(bool)】","code":"import 'package:flutter/material.dart';\nclass CustomCheckBoxListTile extends StatefulWidget {\n @override\n _CustomCheckBoxListTileState createState() => _CustomCheckBoxListTileState();\n}\n\nclass _CustomCheckBoxListTileState extends State {\n var _selected = false;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n margin: EdgeInsets.all(10),\n color: Colors.grey.withAlpha(22),\n child: CheckboxListTile(\n value: _selected,\n checkColor: Colors.yellow,\n activeColor: Colors.orangeAccent,\n secondary: Image.asset(\"assets/images/icon_head.png\"),\n title: Text(\"张风捷特烈\"),\n subtitle: Text(\"@万花过尽知无物\"),\n onChanged: (v) => setState(() => _selected = !_selected),\n ),\n );\n }\n}"},{"id":null,"widgetId":203,"name":"OrientationBuilder基本使用","priority":1,"subtitle":"【builder】 : 方向组件构造器 【OrientationWidgetBuilder】","code":"import 'package:flutter/material.dart';\nclass OrientationBuilderDemo extends StatefulWidget {\n @override\n _OrientationBuilderDemoState createState() => _OrientationBuilderDemoState();\n}\n\nclass _OrientationBuilderDemoState extends State {\n double _width = 200;\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Container(\n width: _width,\n height: 200,\n child: OrientationBuilder(builder: _builder),\n color: Colors.orange,\n ),\n _buildSlider()\n ],\n );\n }\n\n //根据回调的orientation返回组件\n Widget _builder(BuildContext context, Orientation orientation) {\n switch(orientation){\n case Orientation.portrait:\n return Icon(Icons.phone_android,size: 60,);\n break;\n case Orientation.landscape:\n return RotatedBox(\n quarterTurns: 1,\n child: Icon(Icons.phone_android,size: 60,));\n break;\n default: return Container();\n }\n }\n\n Widget _buildSlider() =>Slider(\n value: _width,\n max: 350.0,\n min: 80.0,\n divisions: 17,\n onChanged: (v)=> setState(() => _width= v),\n );\n}\n"},{"id":null,"widgetId":132,"name":"CupertinoActionSheetAction基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【isDefaultAction】 : 是否默认选中 【bool】\n【onPressed】 : 点击事件 【Function()】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport '../../../dialogs/dialog_about.dart';\nclass CustomCupertinoActionSheetAction extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Container(\n margin: EdgeInsets.all(5),\n color: Colors.grey.withAlpha(33),\n child: CupertinoActionSheetAction(\n isDefaultAction: true,\n onPressed: () => DialogAbout.show(context),\n child: Text('张风捷特烈')),\n ),\n Container(\n color: Colors.grey.withAlpha(33),\n margin: EdgeInsets.all(5),\n child: CupertinoActionSheetAction(\n isDefaultAction: false,\n onPressed: () => DialogAbout.show(context),\n child: Text('百里·巫缨')),\n ),\n ],\n );\n }\n}\n"},{"id":null,"widgetId":125,"name":"AnimatedIcon基本使用","priority":1,"subtitle":" \n【icon】 : 动画图标数据 【AnimatedIcons】\n【size】 : 大小 【double】\n【color】 : 颜色 【Color】\n【progress】 : 动画 【Animation】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomAnimatedIcon extends StatefulWidget {\n @override\n _CustomAnimatedIconState createState() => _CustomAnimatedIconState();\n}\n\nclass _CustomAnimatedIconState extends State\n with SingleTickerProviderStateMixin {\n AnimationController _ctrl;\n\n @override\n void initState() {\n _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 1));\n _ctrl.forward();\n super.initState();\n }\n\n @override\n void dispose() {\n _ctrl.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onTap: () {\n setState(() {\n _ctrl.reset();\n _ctrl.forward();\n });\n },\n child: Wrap(\n runSpacing: 30,\n children: _buildChildren(),\n ),\n );\n }\n\n final data = {\n Colors.orange: AnimatedIcons.menu_arrow,\n Colors.blue: AnimatedIcons.ellipsis_search,\n Colors.red: AnimatedIcons.close_menu,\n Colors.green: AnimatedIcons.arrow_menu,\n Colors.cyanAccent: AnimatedIcons.play_pause,\n Colors.purple: AnimatedIcons.pause_play,\n };\n\n List _buildChildren() => data.keys\n .map((e) => AnimatedIcon(\n size: 50,\n color: e,\n icon: data[e],\n progress: _ctrl,\n ))\n .toList();\n}\n"},{"id":null,"widgetId":130,"name":"AboutDialog基本使用","priority":1,"subtitle":" \n【applicationIcon】 : 左上图标 【Widget】\n【applicationVersion】 : 版本号 【String】\n【applicationName】 : 应用名 【String】\n【applicationLegalese】 : 应用律术 【String】\n【children】 : 子组件列表 【List】","code":"import 'package:flutter/material.dart';\nclass CustomAboutDialog extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Stack(\n children: [\n _buildAboutDialog(),\n Positioned(top: 50, right: 20, child: _buildRaisedButton(context)),\n ],\n );\n }\n\n Widget _buildRaisedButton(BuildContext context) => RaisedButton(\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.all(Radius.circular(10))),\n color: Colors.blue,\n onPressed: () {\n showDialog(context: context, builder: (ctx) => _buildAboutDialog());\n },\n child: Text(\n 'Just Show It',\n style: TextStyle(color: Colors.white),\n ),\n );\n\n AboutDialog _buildAboutDialog() {\n return AboutDialog(\n applicationIcon: FlutterLogo(),\n applicationVersion: 'v0.0.1',\n applicationName: 'Flutter Unit',\n applicationLegalese: 'Copyright© 2018-2020 张风捷特烈',\n children: [\n Container(\n margin: EdgeInsets.only(top: 20),\n width: 80,\n height: 80,\n child: Image.asset('assets/images/icon_head.png')),\n Container(\n margin: EdgeInsets.only(top: 10),\n alignment: Alignment.center,\n child: Text(\n 'The King Of Coder.',\n style: TextStyle(color: Colors.white, fontSize: 20, shadows: [\n Shadow(\n color: Colors.blue, offset: Offset(.5, .5), blurRadius: 3)\n ]),\n ))\n ],\n );\n }\n}\n"},{"id":null,"widgetId":8,"name":"FadeInImage.assetNetwork加载网络图片","priority":1,"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】,","code":"import 'package:flutter/material.dart';\nclass CustomFadeInImage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var placeholder = \"assets/images/icon_head.png\";\n var img =\n \"https://user-gold-cdn.xitu.io/2017/8/24/\"\n \"d324efef8cbee6468a018aad7ab2ba6b?imageView2/\"\n \"1/w/180/h/180/q/85/format/webp/interlace/1\";\n return FadeInImage.assetNetwork(\n placeholder: placeholder,\n image: img,\n width: 100,\n height: 100,\n fit: BoxFit.cover,\n repeat:ImageRepeat.noRepeat,\n alignment: Alignment.center,\n fadeInDuration: Duration(seconds: 5),\n fadeInCurve: Curves.easeInCubic,\n );\n }\n}\n"},{"id":null,"widgetId":36,"name":"Placeholder的fallback属性","priority":2,"subtitle":" 当所在区域无宽高约束时,占位组件的宽高。\"\n【fallbackHeight】: 高 【double】\n【fallbackWidth】: 宽 【double】","code":"import 'package:flutter/material.dart';\nclass FallbackPlaceholder extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return UnconstrainedBox(\n child: Placeholder(\n color: Colors.blue,\n strokeWidth: 2,\n fallbackHeight: 100,\n fallbackWidth: 150,\n ),\n );\n }\n}\n"},{"id":null,"widgetId":36,"name":"Placeholder基础属性","priority":1,"subtitle":"【color】: 颜色 【Color】\n【strokeWidth】: 线粗 【double】","code":"import 'package:flutter/material.dart';\nclass CustomPlaceholder extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 100,\n height: 100*0.618,\n child: Placeholder(\n color: Colors.orangeAccent,\n strokeWidth: 2,\n ),\n );\n }\n}"},{"id":null,"widgetId":30,"name":"IconButton属性","priority":1,"subtitle":"【child】: 子组件 【Widget】\n【icon】: 内边距 【Widget】\n【tooltip】: 长按提示文字 【String】\n【highlightColor】: 长按高亮色 【Color】\n【splashColor】: 水波纹色 【Color】\n【onPressed】: 点击事件 【Function】","code":"import 'package:flutter/material.dart';\nclass CustomIconButton extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Padding(\n padding: const EdgeInsets.all(8.0),\n child: IconButton(\n padding: EdgeInsets.only(),\n onPressed: () {},\n icon: Icon(Icons.android, size: 40, color: Colors.green),\n tooltip: \"android\",\n highlightColor: Colors.orangeAccent,\n splashColor: Colors.blue,\n ),\n );\n }\n}\n"},{"id":null,"widgetId":14,"name":"可以接受选中事件","priority":2,"subtitle":"【selected】: 是否选中 【bool】\n【onSelected】: 选中事件 【Function(bool)】","code":"import 'package:flutter/material.dart';\nclass SelectInputChip extends StatefulWidget {\n @override\n _SelectInputChipState createState() => _SelectInputChipState();\n}\n\nclass _SelectInputChipState extends State {\n bool _select = false;\n\n @override\n Widget build(BuildContext context) {\n return InputChip(\n selected: _select,\n padding: EdgeInsets.all(5),\n labelPadding: EdgeInsets.all(3),\n label: Text(\"This is a InputChip.\"),\n backgroundColor: Colors.grey.withAlpha(66),\n avatar: Image.asset(\"assets/images/icon_head.png\"),\n selectedColor: Colors.orangeAccent.withAlpha(88),\n selectedShadowColor: Colors.blue,\n shadowColor: Colors.orangeAccent,\n elevation: 3,\n onDeleted: () => Navigator.of(context).pushNamed('AboutMePage'),\n onSelected: (bool value) {\n setState(() {\n _select = value;\n });\n print(\"onSelected\");\n },\n );\n }\n}"},{"id":null,"widgetId":14,"name":"可以接受点击、删除事件","priority":1,"subtitle":"【onPressed】: 点击事件 【Function()】\n【onDeleted】: 删除事件 【Function()】","code":"import 'package:flutter/material.dart';\nclass PressInputChip extends StatefulWidget {\n @override\n _PressInputChipState createState() => _PressInputChipState();\n}\n\nclass _PressInputChipState extends State {\n bool _delete = false;\n\n @override\n Widget build(BuildContext context) {\n return InputChip(\n padding: EdgeInsets.all(5),\n labelPadding: EdgeInsets.all(3),\n label: Text(\n !_delete ?\n \"This is a InputChip.\" :\n \"You are clicked delete icon.\"),\n backgroundColor: Colors.grey.withAlpha(66),\n avatar: Image.asset(\"assets/images/icon_head.png\"),\n selectedColor: Colors.orangeAccent.withAlpha(88),\n selectedShadowColor: Colors.blue,\n shadowColor: Colors.orangeAccent,\n elevation: 3,\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage'),\n onDeleted: () => setState(() => _delete = !_delete));\n }\n}"},{"id":null,"widgetId":2,"name":"文字对齐方式","priority":4,"subtitle":"【textAlign】: 对齐方式 【TextAlign】\n下面依次是:left、right、center、justify、start、end,","code":"import 'package:flutter/material.dart';\nclass TextAlignText extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n runSpacing: 10,\n children: TextAlign.values\n .map((e) => Container(\n width: 120,\n color: Colors.cyanAccent.withAlpha(33),\n height: 120 * 0.618,\n child: Text(\n \" 张风捷特烈 toly \" * 3,\n textAlign: e,\n ),\n ))\n .toList(),\n );\n }\n}"},{"id":null,"widgetId":2,"name":"文字的基本样式","priority":1,"subtitle":"【入参】 : 文字 【String】\n【style】: 文字样式 【TextStyle】\n【color】: 文字样式 【Color】\n【fontSize】: 文字大小 【double】\n【fontWeight】: 字重 【FontWeight】\n【fontStyle】: 字体样式 【fontStyle】\n【letterSpacing】: 字距 【double】","code":"import 'package:flutter/material.dart';\nclass CustomText extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var style = TextStyle(\n color: Colors.blue,\n fontSize: 20,\n fontWeight: FontWeight.bold,\n fontStyle: FontStyle.italic,\n letterSpacing: 10,\n );\n return Container(\n width: 200,\n color: Colors.cyanAccent.withAlpha(33),\n height: 200 * 0.618 * 0.618,\n child: Text(\"toly-张风捷特烈-1994`\", style: style),\n );\n }\n}"},{"id":null,"widgetId":2,"name":"文字方向与最大行数","priority":5,"subtitle":"【maxLines】 : 最大行数 【int】\n【【textDirection】 : 文字方向 【TextDirection】\n下面依次是:rtl、ltr,","code":"import 'package:flutter/material.dart';\nclass TextDirectionText extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 40,\n runSpacing: 10,\n children: TextDirection.values\n .map((e) => Container(\n width: 120,\n color: Colors.cyanAccent.withAlpha(33),\n height: 120 * 0.618,\n child: Text(\n \" 张风捷特烈 toly \" * 10,\n textDirection: e,\n maxLines: 3,\n overflow: TextOverflow.ellipsis,\n ),\n ))\n .toList(),\n );\n }\n}"},{"id":null,"widgetId":2,"name":"文字装饰线","priority":3,"subtitle":"【fontFamily】 : 文字字体 【String】\n【decoration】: 装饰线 【TextDecoration】\n【decorationColor】: 装饰线颜色 【Color】\n【decorationThickness】: 装饰线粗 【double】\n【decorationStyle】: 装饰线样式 【TextDecorationStyle】","code":"import 'package:flutter/material.dart';\nclass DecorationText extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Text(\n \"19940328\",\n style: TextStyle(\n fontSize: 50,\n fontWeight: FontWeight.bold,\n decoration: TextDecoration.underline,\n decorationThickness: 3,\n decorationStyle: TextDecorationStyle.wavy,\n decorationColor: Colors.blue,\n fontStyle: FontStyle.italic,\n fontFamily: \"DancingScript\",\n letterSpacing: 10),\n );\n }\n}"},{"id":null,"widgetId":2,"name":"是否包裹与越界效果","priority":6,"subtitle":"【softWrap】 : 是否换行 【bool】\n【overflow】 : 越界效果 【TextOverflow】\n下面softWrap=false; overflow依次是:clip、fade、ellipsis、visible,","code":"import 'package:flutter/material.dart';\nclass SoftWrapText extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n runSpacing: 10,\n children: TextOverflow.values\n .map((e) => Container(\n width: 150,\n color: Colors.cyanAccent.withAlpha(33),\n height: 150 * 0.618 * 0.618,\n child: Text(\" 张风捷特烈 toly \" * 5,\n overflow: e,\n softWrap: false),\n ))\n .toList(),\n );\n }\n}\n"},{"id":null,"widgetId":2,"name":"文字阴影","priority":2,"subtitle":"【shadows】 : 文字 【List】\n【backgroundColor】: 背景颜色 【Color】","code":"import 'package:flutter/material.dart';\nclass ShadowText extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Text(\n \"张风捷特烈\",\n style: TextStyle(\n fontSize: 50,\n color: Colors.white,\n backgroundColor: Colors.black,\n shadows: [\n Shadow(\n color: Colors.cyanAccent,\n offset: Offset(1, 1),\n blurRadius: 10),\n Shadow(\n color: Colors.blue,\n offset: Offset(-0.1, 0.1),\n blurRadius: 10),\n ]),\n );\n }\n}"},{"id":null,"widgetId":6,"name":"用于显示一个图标","priority":1,"subtitle":"【入参】 :图标数据 【IconData】\n【size】 : 大小 【double】\n【color】: 颜色 【Color】","code":"import 'package:flutter/material.dart';\nclass CustomIcon extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n Icon(\n Icons.send,\n color: Colors.orange,\n size: 60,\n ),\n Icon(\n Icons.android,\n color: Colors.green,\n size: 100,\n ),\n ],\n );\n }\n}"},{"id":null,"widgetId":6,"name":"使用自定义图标","priority":2,"subtitle":"可在iconfont网站中下载图标字体进行使用","code":"import 'package:flutter/material.dart';\nimport '../../../../app/style/TolyIcon.dart';\nclass MyIcon extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 20,\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n TolyIcon.icon_search,\n TolyIcon.icon_star,\n TolyIcon.icon_layout,\n TolyIcon.icon_star_ok\n ]\n .map((e) => Icon(\n e,\n color: Colors.green,\n size: 60,\n ))\n .toList(),\n );\n }\n}\n\n"},{"id":null,"widgetId":169,"name":"文字样式-TextTheme","priority":1,"subtitle":"后代组件可以通过CupertinoTheme.of获取主题的数据进行使用。","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass TextCupertinoTheme extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var queryData = CupertinoTheme.of(context).textTheme;\n var styles = {\n \"tabLabelTextStyle: \": queryData.tabLabelTextStyle,\n \"actionTextStyle: \": queryData.actionTextStyle,\n \"navActionTextStyle: \": queryData.navActionTextStyle,\n \"textStyle: \": queryData.textStyle,\n \"navTitleTextStyle: \": queryData.navTitleTextStyle,\n \"pickerTextStyle: \": queryData.pickerTextStyle,\n \"dateTimePickerTextStyle: \": queryData.dateTimePickerTextStyle,\n \"navLargeTitleTextStyle: \": queryData.navLargeTitleTextStyle,\n };\n\n return Container(\n child: Column(\n children: styles.keys.map((e) => buildItem(e, styles[e])).toList(),\n ),\n );\n }\n\n Widget buildItem(String e, TextStyle style) => Column(\n children: [\n Padding(\n padding: const EdgeInsets.all(8.0),\n child: Row(\n mainAxisAlignment: MainAxisAlignment.spaceBetween,\n children: [\n Text(\n e,\n style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),\n ),\n Text(\n \"@toly\",\n style: style,\n )\n ],\n ),\n ),\n Divider(\n height: 1,\n )\n ],\n );\n}"},{"id":null,"widgetId":169,"name":"CupertinoThemeData的使用","priority":2,"subtitle":" \n和Theme一样可以通过指定的属性,让它们在后代中共享,不过属性较少。注意如果需要使用主题,不能在当前的context中获取。","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoTheme extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return CupertinoTheme(\n data: CupertinoThemeData(\n primaryColor: Colors.blue,\n primaryContrastingColor: Colors.green\n ),\n child: _ChildUseTheme());\n }\n}\n\nclass _ChildUseTheme extends StatelessWidget {\n const _ChildUseTheme({\n Key key,\n }) : super(key: key);\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n Container(\n width: 50,\n height: 50,\n color: CupertinoTheme.of(context).primaryContrastingColor,\n ),\n Container(\n width: 150,\n child: Slider(value: 0.8, onChanged: (v) => {})),\n Container( width: 150,child: Divider(color:CupertinoTheme.of(context).primaryContrastingColor,thickness: 1,))\n ]);\n }\n}"},{"id":null,"widgetId":25,"name":"FlatButton点击事件","priority":1,"subtitle":"【color】: 颜色 【Color】\n【splashColor】: 水波纹颜色 【Color】\n【child】: 子组件 【Widget】\n【textColor】: 子组件文字颜色 【Color】\n【highlightColor】: 长按高亮色 【Color】\n【padding】: 内边距 【EdgeInsetsGeometry】\n【onPressed】: 点击事件 【Function】","code":"import 'package:flutter/material.dart';\nclass CustomFlatButton extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return FlatButton(\n onPressed: ()=>{},\n padding: EdgeInsets.all(8),\n splashColor: Colors.green,\n child: Text(\"FlatButton\"),\n textColor: Color(0xffFfffff),\n color: Colors.blue,\n highlightColor: Color(0xffF88B0A),\n );\n }\n}\n"},{"id":null,"widgetId":133,"name":"SimpleDialogOption基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【onPressed】 : 点击事件 【Function()】","code":"import 'package:flutter/material.dart';\nclass CustomSimpleDialogOption extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Container(\n alignment: Alignment.center,\n width: double.infinity,\n height: 50,\n margin: EdgeInsets.all(5),\n color: Colors.grey.withAlpha(33),\n child: SimpleDialogOption(\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage'),\n child: Text('张风捷特烈')),\n ),\n Container(\n height: 50,\n alignment: Alignment.center,\n width: double.infinity,\n color: Colors.grey.withAlpha(33),\n margin: EdgeInsets.all(5),\n child: SimpleDialogOption(\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage'),\n child: Text('百里·巫缨')),\n ),\n ],\n );\n }\n}\n"},{"id":null,"widgetId":31,"name":"BackButton属性","priority":1,"subtitle":"【color】: 颜色 【Color】\n【onPressed】: 点击事件 【Function】\n onPressed为空会退出当前栈","code":"import 'package:flutter/material.dart';\nclass CustomBackButton extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var data = [Colors.red,Colors.yellow,Colors.blue,Colors.green];\n return Wrap(\n spacing: 10,\n children: data.map((e)=>BackButton(\n color: e,\n )).toList()\n );\n }\n}\n"},{"id":null,"widgetId":202,"name":"Builder的使用","priority":1,"subtitle":"【builder】 : 组件构造器 【WidgetBuilder】\n同一个类中使用`XXX.of(context)`获取某类状态对象方法会存在`上下文滞后`的错误,使用Builder解决。","code":"import 'package:flutter/material.dart';\nclass BuilderDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: Scaffold(\n appBar: AppBar(\n title: Text('Builder'),\n ),\n floatingActionButton: Builder(\n builder: (ctx) => FloatingActionButton(\n onPressed: () {\n Scaffold.of(ctx)\n .showSnackBar(SnackBar(content: Text('hello builder')));\n },\n child: Icon(Icons.add),\n ),\n ),\n ),\n );\n }\n\n\n}\n"},{"id":null,"widgetId":18,"name":"SwitchListTile的密排属性","priority":3,"subtitle":"【dense】: 是否密排 【bool】","code":"import 'package:flutter/material.dart';\nclass DenseSwitchListTile extends StatefulWidget {\n @override\n _DenseSwitchListTileState createState() => _DenseSwitchListTileState();\n}\n\nclass _DenseSwitchListTileState extends State {\n var _value=false;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n margin: EdgeInsets.all(10),\n color: Colors.grey.withAlpha(22),\n child: SwitchListTile(\n value: _value,\n dense: true,\n selected: _value,\n activeColor: Colors.orangeAccent,\n secondary: Image.asset(\"assets/images/icon_head.png\"),\n title: Text(\"张风捷特烈\"),\n subtitle: Text(\"@万花过尽知无物\"),\n onChanged: (v) => setState(() => _value = !_value),\n ),\n );\n }\n}"},{"id":null,"widgetId":18,"name":"SwitchListTile的选中效果","priority":2,"subtitle":"【selected】: 是否选中 【bool】\n【inactiveThumbImage】: 未选中时圆圈图片 【ImageProvider】\n【activeThumbImage】: 选中时圆圈图片 【ImageProvider】","code":"import 'package:flutter/material.dart';\nclass SelectSwitchListTile extends StatefulWidget {\n @override\n _SelectSwitchListTileState createState() => _SelectSwitchListTileState();\n}\n\nclass _SelectSwitchListTileState extends State {\n var _value=false;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n margin: EdgeInsets.all(10),\n color: Colors.grey.withAlpha(22),\n child: SwitchListTile(\n value: _value,\n selected: _value,\n activeColor: Colors.orangeAccent,\n secondary: Image.asset(\"assets/images/icon_head.png\"),\n inactiveThumbImage: AssetImage(\"assets/images/pica.gif\"),\n activeThumbImage: AssetImage(\"assets/images/icon_head.png\"),\n title: Text(\"张风捷特烈\"),\n subtitle: Text(\"@万花过尽知无物\"),\n onChanged: (v) => setState(() => _value = !_value),\n ),\n );\n }\n}"},{"id":null,"widgetId":18,"name":"SwitchListTile的基本表现如下","priority":1,"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)】","code":"import 'package:flutter/material.dart';\nclass CustomSwitchListTile extends StatefulWidget {\n @override\n _CustomSwitchListTileState createState() => _CustomSwitchListTileState();\n}\n\nclass _CustomSwitchListTileState extends State {\n var _value=false;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n margin: EdgeInsets.all(10),\n color: Colors.grey.withAlpha(22),\n child: SwitchListTile(\n value: _value,\n inactiveThumbColor:Colors.cyanAccent ,\n inactiveTrackColor: Colors.blue.withAlpha(88),\n activeColor: Colors.orangeAccent,\n activeTrackColor: Colors.orange,\n secondary: Image.asset(\"assets/images/icon_head.png\"),\n title: Text(\"张风捷特烈\"),\n subtitle: Text(\"@万花过尽知无物\"),\n onChanged: (v) => setState(() => _value = !_value),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":102,"name":"DataTable的sort","priority":2,"subtitle":" \n【sortColumnIndex】 : 列号 【int】\n【columnSpacing】 : 列间距 【double】\n【sortAscending】 : 是否顺序 【bool】","code":"import 'package:flutter/material.dart';\nclass _BeanOp {\n final int id;\n final String name;\n final String type;\n bool select;\n\n _BeanOp(this.id, this.name, this.type, this.select);\n\n @override\n String toString() {\n return '_BeanOp{id: $id, name: $name, type: $type, select: $select}';\n }\n}\n\nclass SortDataTable extends StatefulWidget {\n @override\n _SortDataTableState createState() => _SortDataTableState();\n}\n\nclass _SortDataTableState extends State {\n var data = [\n _BeanOp(101, 'DataTable', 'StatelessWidget', false),\n _BeanOp(44, 'RangeSlider', 'StatefulWidget', false),\n _BeanOp(2, 'Text', 'StatelessWidget', false),\n _BeanOp(1, 'Image', 'StatefulWidget', false),\n ];\n\n bool _sortAscending = false;\n var selectData = <_BeanOp>[];\n\n @override\n Widget build(BuildContext context) {\n return DataTable(\n columnSpacing: 20,\n sortColumnIndex: 1,\n sortAscending: _sortAscending,\n columns: [\n DataColumn(\n label: Container(\n child: Checkbox(\n value: selectData.length == data.length,\n onChanged: _onSelectAll,\n ),\n ),\n ),\n DataColumn(label: Text('id'), numeric: false, onSort: _onSortId),\n DataColumn(label: Text('名称')),\n DataColumn(label: Text('类型')),\n ],\n rows: data\n .map((e) => DataRow(selected: false, cells: [\n DataCell(Checkbox(\n value: e.select,\n onChanged: (v) => _onSelectOne(v, e),\n )),\n DataCell(Text('${e.id}')),\n DataCell(Text('${e.name}'),\n showEditIcon: true, onTap: () {}),\n DataCell(Text('${e.type}')),\n ]))\n .toList());\n }\n\n _onSortId(int index, bool ascending) {\n setState(() {\n _sortAscending = ascending;\n data.sort(\n (a, b) => ascending ? a.id.compareTo(b.id) : b.id.compareTo(a.id));\n });\n }\n\n _onSelectOne(bool selected, _BeanOp e) {\n setState(() {\n if (selected) {\n //选中\n selectData.add(e);\n } else {\n selectData.remove(e);\n }\n e.select = selected;\n print(selectData);\n });\n }\n\n _onSelectAll(bool select) {\n setState(() {\n if (select) {\n data.forEach((e) => e.select = true);\n selectData = data.map((e) => e).toList();\n } else {\n data.forEach((e) => e.select = false);\n selectData = [];\n }\n });\n }\n}"},{"id":null,"widgetId":102,"name":"DataTable基本使用","priority":1,"subtitle":" \n【columns】 : 列 【List】\n【rows】 : 行 【List】","code":"import 'package:flutter/material.dart';\nclass _Bean {\n final int id;\n final String name;\n final String type;\n\n _Bean(this.id, this.name, this.type);\n}\n\nclass CustomDataTable extends StatelessWidget {\n final data = [\n _Bean(101, 'DataTable', 'StatelessWidget'),\n _Bean(44, 'RangeSlider', 'StatefulWidget'),\n _Bean(2, 'Text', 'StatelessWidget'),\n _Bean(1, 'Image', 'StatefulWidget'),\n ];\n\n final columns = ['id', '名称', '类型'];\n\n @override\n Widget build(BuildContext context) {\n return DataTable(\n columns: columns.map((e) => DataColumn(label: Text(e))).toList(),\n rows: data\n .map((e) => DataRow(cells: [\n DataCell(Text('${e.id}')),\n DataCell(Text('${e.name}')),\n DataCell(Text('${e.type}')),\n ]))\n .toList());\n }\n}"},{"id":null,"widgetId":4,"name":"样式用于显示文字","priority":2,"subtitle":"【style】 : 样式-3种枚举 【FlutterLogoStyle】\n【textColor】: 文字颜色 【Color】","code":"import 'package:flutter/material.dart';\nclass FlutterLogoWithText extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var data = {\n FlutterLogoStyle.horizontal:Colors.blue,\n FlutterLogoStyle.markOnly:Colors.red,\n FlutterLogoStyle.stacked:Colors.green,\n };\n\n return Wrap(\n spacing: 20,\n children: data.keys.map((e) => FlutterLogo(\n size: 80,\n style: e,\n textColor: data[e],\n ))\n .toList(),\n );\n }\n}"},{"id":null,"widgetId":4,"name":"用于显示一个FlutterLogo","priority":1,"subtitle":"【size】 : 大小 【double】\n【colors】: 颜色 【MaterialColor】","code":"import 'package:flutter/material.dart';\nclass CustomFlutterLogo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n\n var data = {\n Colors.blue:50.0,\n Colors.red:60.0,\n Colors.green:70.0,\n Colors.yellow:80.0,\n };\n return Wrap(\n children: data.keys\n .map((e) => FlutterLogo(\n size: data[e],\n colors: e,\n ))\n .toList(),\n );\n }\n}"},{"id":null,"widgetId":193,"name":"AboutListTile基本使用","priority":1,"subtitle":" \n【icon】 : 左图标 【Widget】\n【applicationIcon】 : 左上图标 【Widget】\n【applicationVersion】 : 版本号 【String】\n【applicationName】 : 应用名 【String】\n【applicationLegalese】 : 应用律术 【String】\n【aboutBoxChildren】 : 弹框内容组件 【List】","code":"import 'package:flutter/material.dart';\nclass AboutListTileDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return AboutListTile(\n icon: Icon(Icons.info),\n applicationIcon: FlutterLogo(),\n applicationName: 'Flutter Unit',\n applicationVersion: 'v0.0.1',\n applicationLegalese: 'Copyright© 2018-2020 张风捷特烈',\n aboutBoxChildren: [\n Padding(\n padding: const EdgeInsets.all(10.0),\n child: Text(\n ' FlutterUnit是【张风捷特烈】的开源项目,'\n '收录Flutter的200+组件,并附加详细介绍以及操作交互,'\n '希望帮助广大编程爱好者入门Flutter。'\n '更多知识可以关注掘金账号、公众号【编程之王】。',\n style: TextStyle(color: Color(0xff999999), fontSize: 16),\n textAlign: TextAlign.justify,\n ),\n ),\n ],\n );\n }\n}"},{"id":null,"widgetId":5,"name":"用于显示一个角标","priority":1,"subtitle":"【message】 : 显示的文字信息 【String】\n【location】 : 位置*4 【BannerLocation】\n【color】: 角标颜色 【Color】\n【child】: 孩子 【Widget】\n【textStyle】: 文字样式 【TextStyle】","code":"import 'package:flutter/material.dart';\nclass CustomBanner extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var data = {\n BannerLocation.topStart: Colors.red,\n BannerLocation.topEnd: Colors.blue,\n BannerLocation.bottomStart: Colors.green,\n BannerLocation.bottomEnd: Colors.yellow,\n };\n\n return Wrap(\n spacing: 10,\n runSpacing: 10,\n children: data.keys.map((e) =>\n Container(\n color: Color(0xffD8F5FF),\n width: 150,\n height: 150 * 0.618,\n child: Banner(\n message: \"Flutter 1.12发布\",\n location: e,\n color: data[e],\n child: Padding(\n padding: EdgeInsets.all(20),\n child: FlutterLogo(colors: Colors.blue,\n style: FlutterLogoStyle.horizontal,)),\n ),\n )).toList());\n }\n}\n"},{"id":null,"widgetId":127,"name":"AlertDialog基本使用","priority":1,"subtitle":" \n【title】 : 顶部组件 【Widget】\n【content】 : 内容组件 【Widget】\n【titleTextStyle】 : 顶部文字样式 【TextStyle】\n【contentTextStyle】 : 内容文字样式 【TextStyle】\n【titlePadding】 : 顶部内边距 【EdgeInsetsGeometry】\n【contentPadding】 : 内容内边距 【EdgeInsetsGeometry】\n【actions】 : 右下角组件列表 【List】\n【backgroundColor】 : 右下角组件列表 【背景色】\n【elevation】 : 右下角组件列表 【背景色】\n【shape】 : 影深 【double】","code":"import 'package:flutter/material.dart';\nclass CustomAlertDialog extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildRaisedButton(context),\n _buildAlertDialog(),\n ],\n );\n }\n\n Widget _buildRaisedButton(BuildContext context) => RaisedButton(\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.all(Radius.circular(10))),\n color: Colors.blue,\n onPressed: () {\n showDialog(context: context, builder: (ctx) => _buildAlertDialog());\n },\n child: Text(\n 'Just Show It !',\n style: TextStyle(color: Colors.white),\n ),\n );\n\n Widget _buildAlertDialog() {\n return AlertDialog(\n title: _buildTitle(),\n titleTextStyle: TextStyle(fontSize: 20, color: Colors.black),\n titlePadding: EdgeInsets.only(\n top: 5,\n left: 20,\n ),\n contentPadding: EdgeInsets.symmetric(horizontal: 5),\n backgroundColor: Colors.white,\n content: _buildContent(),\n actions: [\n Icon(Icons.android, color: Colors.blue,),\n Icon(Icons.add, color: Colors.blue,),\n Icon(Icons.g_translate, color: Colors.blue,),\n Icon(Icons.games, color: Colors.blue,),\n ],\n elevation: 4,\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.all(Radius.circular(10))),\n );\n }\n\n Widget _buildTitle() {\n return Row(\n //标题\n children: [\n Image.asset(\n \"assets/images/icon_head.png\",\n width: 30,\n height: 30,\n ),\n SizedBox(width: 10,),\n Expanded(\n child: Text(\n \"关于\",\n style: TextStyle(fontSize: 18),\n )),\n CloseButton()\n ],\n );\n }\n\n Widget _buildContent() {\n return Column(\n mainAxisSize: MainAxisSize.min,\n children: [\n Padding(\n padding: const EdgeInsets.all(10.0),\n child: Text(\n ' FlutterUnit是【张风捷特烈】的开源项目,'\n '收录Flutter的200+组件,并附加详细介绍以及操作交互,'\n '希望帮助广大编程爱好者入门Flutter。'\n '更多知识可以关注掘金账号、公众号【编程之王】。',\n style: TextStyle(color: Color(0xff999999), fontSize: 16),\n textAlign: TextAlign.justify,\n ),\n ),\n ],\n );\n }\n}"},{"id":null,"widgetId":71,"name":"Offstage基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【offstage】 : 是否消失 【bool】","code":"import 'package:flutter/material.dart';\nclass CustomOffstage extends StatefulWidget {\n @override\n _CustomOffstageState createState() => _CustomOffstageState();\n}\n\nclass _CustomOffstageState extends State {\n bool _off = false;\n\n @override\n Widget build(BuildContext context) {\n var radBox = Container(\n height: 50,\n width: 60,\n color: Colors.red,\n child: Switch(\n value: _off,\n onChanged: (v) => setState(() => _off = v)),\n );\n\n return Container(\n width: 250,\n height: 200,\n child: Row(\n children: [radBox, _buildOffStage(), radBox],\n ),\n );\n }\n\n Widget _buildOffStage() => Offstage(\n offstage: _off,\n child: Container(\n alignment: Alignment.center,\n height: 100,\n width: 100,\n color: Colors.blue,\n child: Text(\n \"Offstage\",\n style: TextStyle(fontSize: 20),\n ),\n ));\n}\n"},{"id":null,"widgetId":67,"name":"ClipRect基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【clipBehavior】 : 裁剪行为 【Clip】\n【clipper】 : 裁剪器 【CustomClipper】","code":"import 'package:flutter/material.dart';\nclass CustomClipRect extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return ClipRect(\n child: SizedBox(\n height: 100,\n width: 100,\n child: Image.asset(\n \"assets/images/wy_300x200.jpg\",\n fit: BoxFit.cover,),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":279,"name":"返回按钮基本使用","priority":1,"subtitle":"【clipper】 : 裁剪器 【CustomClipper】\n【clipBehavior】 : 裁剪行为 【Clip】\n【child】 : 子组件 【Widget】\n【elevation】 : 阴影深 【double】\n【shadowColor】 : 阴影颜色 【Color】\n【color】: 颜色 【Color】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass PhysicalShapeDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 200,\n height: 200,\n child: PhysicalShape(\n shadowColor: Colors.orange,\n elevation: 3,\n child: Image.asset(\n 'assets/images/caver.jpeg',\n fit: BoxFit.cover,\n ),\n clipBehavior: Clip.hardEdge,\n clipper: ShapeBorderClipper(\n shape: CircleBorder(side: BorderSide.none),\n ),\n color: Colors.deepPurpleAccent),\n );\n }\n}\n"},{"id":null,"widgetId":72,"name":"RotatedBox基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【quarterTurns】 : 旋转多少个90° 【int】","code":"import 'package:flutter/material.dart';\nclass CustomRotatedBox extends StatefulWidget {\n @override\n _CustomRotatedBoxState createState() => _CustomRotatedBoxState();\n}\n\nclass _CustomRotatedBoxState extends State {\n int _quarterTurns = 0;\n\n @override\n Widget build(BuildContext context) {\n return RotatedBox(\n quarterTurns: _quarterTurns,\n child: GestureDetector(\n onTap: () => setState(() => _quarterTurns++),\n child: Icon(\n Icons.android,\n size: 60,\n color: Colors.blue,\n )),\n );\n }\n}\n"},{"id":null,"widgetId":79,"name":"LimitedBox基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【maxHeight】 : 最大高 【double】\n【maxWidth】 : 最大宽 【double】","code":"import 'package:flutter/material.dart';\nclass CustomLimitedBox extends StatefulWidget {\n @override\n _CustomLimitedBoxState createState() => _CustomLimitedBoxState();\n}\n\nclass _CustomLimitedBoxState extends State {\n var _text = '';\n\n @override\n Widget build(BuildContext context) {\n var child = Container(\n alignment: Alignment.center,\n color: Colors.cyanAccent,\n width: 50,\n height: 50,\n child: Text(\"Static\"),\n );\n\n var box = LimitedBox(\n maxHeight: 60,\n maxWidth: 100,\n child: Container(color: Colors.orange, child: Text(_text)),\n );\n return Column(\n children: [\n Container(\n color: Colors.grey.withAlpha(22),\n width: 300,\n height: 100,\n child: Row(\n children: [child, UnconstrainedBox(child: box), child],\n ),\n ),\n _buildInput()\n ],\n );\n }\n\n Widget _buildInput() {\n return Padding(\n padding: const EdgeInsets.all(18.0),\n child: TextField(\n decoration: InputDecoration(\n border: OutlineInputBorder(),\n hintText: '请输入',\n ),\n onChanged: (v) {\n setState(() {\n _text = v;\n });\n },\n ),\n );\n }\n}\n"},{"id":null,"widgetId":83,"name":"OverflowBox基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【minWidth】 : 最小宽 【double】\n【minHeight】 : 最小高 【double】\n【maxHeight】 : 最大高 【double】\n【maxWidth】 : 最大宽 【double】\n【alignment】 : 对齐方式 【AlignmentGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomOverflowBox extends StatefulWidget {\n @override\n _CustomOverflowBoxState createState() => _CustomOverflowBoxState();\n}\n\nclass _CustomOverflowBoxState extends State {\n var _text = '';\n\n @override\n Widget build(BuildContext context) {\n var box = OverflowBox(\n alignment: Alignment.center,\n minHeight: 50,\n minWidth: 50,\n maxWidth: 200,\n maxHeight: 120,\n child: Container(\n color: Colors.orange,\n child: Text(_text),\n ),\n// child: Text(\"张风\"),\n );\n return Column(\n children: [\n Container(\n color: Colors.grey.withAlpha(33),\n width: 100,\n height: 100,\n child: box),\n _buildInput()\n ],\n );\n }\n\n Widget _buildInput() {\n return Padding(\n padding: const EdgeInsets.all(18.0),\n child: TextField(\n decoration: InputDecoration(\n border: OutlineInputBorder(),\n hintText: '请输入',\n ),\n onChanged: (v) {\n setState(() {\n _text = v;\n });\n },\n ),\n );\n }\n}\n"},{"id":null,"widgetId":166,"name":"CustomPaint绘线贝塞尔曲线","priority":2,"subtitle":" \n Flutter也支持贝塞尔曲线等复杂绘制。","code":"import 'dart:ui';\nimport 'package:flutter/material.dart';\nclass PlayBezier3Page extends StatefulWidget {\n @override\n _PlayBezier3PageState createState() => _PlayBezier3PageState();\n}\n\nclass _PlayBezier3PageState extends State {\n List _pos = [];\n int selectPos;\n\n @override\n void initState() {\n _initPoints();\n super.initState();\n }\n\n void _initPoints() {\n _pos = List();\n _pos.add(Offset(0, 0));\n _pos.add(Offset(60, -60));\n _pos.add(Offset(-90, -90));\n _pos.add(Offset(-120, -40));\n }\n\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n width: MediaQuery.of(context).size.width,\n child: RepaintBoundary(\n child: CustomPaint(\n painter: BezierPainter(pos: _pos, selectPos: selectPos),\n ),\n ),\n\n );\n }\n}\n\nclass BezierPainter extends CustomPainter {\n Paint _gridPaint;\n Path _gridPath;\n\n Paint _mainPaint;\n Path _mainPath;\n int selectPos;\n Paint _helpPaint;\n\n List pos;\n\n BezierPainter({this.pos, this.selectPos}) {\n _gridPaint = Paint()..style = PaintingStyle.stroke;\n _gridPath = Path();\n\n _mainPaint = Paint()\n ..color = Colors.orange\n ..style = PaintingStyle.stroke\n ..strokeWidth = 2;\n _mainPath = Path();\n\n _helpPaint = Paint()\n ..color = Colors.purple\n ..style = PaintingStyle.stroke\n ..strokeWidth = 2\n ..strokeCap = StrokeCap.round;\n }\n\n @override\n void paint(Canvas canvas, Size size) {\n canvas.clipRect(Offset.zero & size);\n canvas.translate(size.width / 2, size.height / 2);\n _drawGrid(canvas, size); //绘制格线\n _drawAxis(canvas, size); //绘制轴线\n\n _mainPath.moveTo(pos[0].dx, pos[0].dy);\n _mainPath.cubicTo(pos[1].dx, pos[1].dy, pos[2].dx, pos[2].dy, pos[3].dx, pos[3].dy);\n canvas.drawPath(_mainPath, _mainPaint);\n _drawHelp(canvas);\n _drawSelectPos(canvas);\n\n }\n\n @override\n bool shouldRepaint(CustomPainter oldDelegate) => false;\n\n void _drawGrid(Canvas canvas, Size size) {\n _gridPaint\n ..color = Colors.grey\n ..strokeWidth = 0.5;\n _gridPath = _buildGridPath(_gridPath, size);\n canvas.drawPath(_buildGridPath(_gridPath, size), _gridPaint);\n\n canvas.save();\n canvas.scale(1, -1); //沿x轴镜像\n canvas.drawPath(_gridPath, _gridPaint);\n canvas.restore();\n\n canvas.save();\n canvas.scale(-1, 1); //沿y轴镜像\n canvas.drawPath(_gridPath, _gridPaint);\n canvas.restore();\n\n canvas.save();\n canvas.scale(-1, -1); //沿原点镜像\n canvas.drawPath(_gridPath, _gridPaint);\n canvas.restore();\n }\n\n void _drawAxis(Canvas canvas, Size size) {\n canvas.drawPoints(\n PointMode.lines,\n [\n Offset(-size.width / 2, 0),\n Offset(size.width / 2, 0),\n Offset(0, -size.height / 2),\n Offset(0, size.height / 2),\n Offset(0, size.height / 2),\n Offset(0 - 7.0, size.height / 2 - 10),\n Offset(0, size.height / 2),\n Offset(0 + 7.0, size.height / 2 - 10),\n Offset(size.width / 2, 0),\n Offset(size.width / 2 - 10, 7),\n Offset(size.width / 2, 0),\n Offset(size.width / 2 - 10, -7),\n ],\n _gridPaint\n ..color = Colors.blue\n ..strokeWidth = 1.5);\n }\n\n Path _buildGridPath(Path path, Size size, {step = 20.0}) {\n for (int i = 0; i < size.height / 2 / step; i++) {\n path.moveTo(0, step * i);\n path.relativeLineTo(size.width / 2, 0);\n }\n for (int i = 0; i < size.width / 2 / step; i++) {\n path.moveTo(step * i, 0);\n path.relativeLineTo(\n 0,\n size.height / 2,\n );\n }\n return path;\n }\n\n void _drawHelp(Canvas canvas) {\n canvas.drawPoints(PointMode.lines, pos, _helpPaint..strokeWidth = 1);\n canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8);\n }\n\n void _drawSelectPos(Canvas canvas) {\n if (selectPos == null) return;\n canvas.drawCircle(\n pos[selectPos],\n 10,\n _helpPaint\n ..color = Colors.green\n ..strokeWidth = 2);\n }\n}"},{"id":null,"widgetId":166,"name":"CustomPaint绘线图形","priority":1,"subtitle":" \n【painter】 : 绘画器 【CustomPainter】","code":"import 'package:flutter/material.dart';\nclass ClockPage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width,\n height: 100,\n child:RepaintBoundary(\n child: CustomPaint(//使用CustomPaint盛放画布\n painter: ClockPainter(),\n ),\n ),\n )\n ;\n }\n}\n\nclass ClockPainter extends CustomPainter {\n Paint _paint;\n var _radius = 3.0; //小球半径\n Path _path = Path(); //画笔对象\n ClockPainter () {\n _paint = Paint()..color= Color(0xff45d0fd)..isAntiAlias=true;\n _path.addOval(Rect.fromCircle(radius: _radius, center: Offset(0, 0))); //小球路径\n }\n\n @override\n void paint(Canvas canvas, Size size) {\n print(size);\n canvas.clipRect(Offset.zero & size);\n canvas.translate(size.width/2-65*2, 0);\n renderDigit(1, canvas);//渲染数字\n canvas.translate(65, 0);//平移画布\n renderDigit(9, canvas);\n canvas.translate(65, 0); renderDigit(9, canvas);\n canvas.translate(65, 0); renderDigit(4, canvas);\n }\n //渲染数字 num :要显示的数字 canvas :画布\n void renderDigit(int num, Canvas canvas) {\n if (num > 10) { return; }\n for (int i = 0; i < digit[num].length; i++) {\n for (int j = 0; j < digit[num][j].length; j++) {\n if (digit[num][i][j] == 1) {\n canvas.save();\n double rX = j * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心横坐标\n double rY = i * 2 * (_radius + 1) + (_radius + 1); //第(i,j)个点圆心纵坐标\n canvas.translate(rX, rY);\n canvas.drawPath(_path, _paint);\n canvas.restore();\n }\n }\n }\n }\n @override\n bool shouldRepaint(CustomPainter oldDelegate)=> false;\n}\n\nconst digit = [\n [\n [0, 0, 1, 1, 1, 0, 0],\n [0, 1, 1, 0, 1, 1, 0],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [0, 1, 1, 0, 1, 1, 0],\n [0, 0, 1, 1, 1, 0, 0]\n ], //0\n\n [\n [0, 0, 0, 1, 1, 0, 0],\n [0, 1, 1, 1, 1, 0, 0],\n [0, 0, 0, 1, 1, 0, 0],\n [0, 0, 0, 1, 1, 0, 0],\n [0, 0, 0, 1, 1, 0, 0],\n [0, 0, 0, 1, 1, 0, 0],\n [0, 0, 0, 1, 1, 0, 0],\n [0, 0, 0, 1, 1, 0, 0],\n [0, 0, 0, 1, 1, 0, 0],\n [1, 1, 1, 1, 1, 1, 1]\n ], //1\n [\n [0, 1, 1, 1, 1, 1, 0],\n [1, 1, 0, 0, 0, 1, 1],\n [0, 0, 0, 0, 0, 1, 1],\n [0, 0, 0, 0, 1, 1, 0],\n [0, 0, 0, 1, 1, 0, 0],\n [0, 0, 1, 1, 0, 0, 0],\n [0, 1, 1, 0, 0, 0, 0],\n [1, 1, 0, 0, 0, 0, 0],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 1, 1, 1, 1, 1]\n ], //2\n [\n [1, 1, 1, 1, 1, 1, 1],\n [0, 0, 0, 0, 0, 1, 1],\n [0, 0, 0, 0, 1, 1, 0],\n [0, 0, 0, 1, 1, 0, 0],\n [0, 0, 1, 1, 1, 0, 0],\n [0, 0, 0, 0, 1, 1, 0],\n [0, 0, 0, 0, 0, 1, 1],\n [0, 0, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [0, 1, 1, 1, 1, 1, 0]\n ], //3\n\n [\n [0, 0, 0, 0, 1, 1, 0],\n [0, 0, 0, 1, 1, 1, 0],\n [0, 0, 1, 1, 1, 1, 0],\n [0, 1, 1, 0, 1, 1, 0],\n [1, 1, 0, 0, 1, 1, 0],\n [1, 1, 1, 1, 1, 1, 1],\n [0, 0, 0, 0, 1, 1, 0],\n [0, 0, 0, 0, 1, 1, 0],\n [0, 0, 0, 0, 1, 1, 0],\n [0, 0, 0, 1, 1, 1, 1]\n ], //4\n [\n [1, 1, 1, 1, 1, 1, 1],\n [1, 1, 0, 0, 0, 0, 0],\n [1, 1, 0, 0, 0, 0, 0],\n [1, 1, 1, 1, 1, 1, 0],\n [0, 0, 0, 0, 0, 1, 1],\n [0, 0, 0, 0, 0, 1, 1],\n [0, 0, 0, 0, 0, 1, 1],\n [0, 0, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [0, 1, 1, 1, 1, 1, 0]\n ], //5\n [\n [0, 0, 0, 0, 1, 1, 0],\n [0, 0, 1, 1, 0, 0, 0],\n [0, 1, 1, 0, 0, 0, 0],\n [1, 1, 0, 0, 0, 0, 0],\n [1, 1, 0, 1, 1, 1, 0],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [0, 1, 1, 1, 1, 1, 0]\n ], //6\n [\n [1, 1, 1, 1, 1, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [0, 0, 0, 0, 1, 1, 0],\n [0, 0, 0, 0, 1, 1, 0],\n [0, 0, 0, 1, 1, 0, 0],\n [0, 0, 0, 1, 1, 0, 0],\n [0, 0, 1, 1, 0, 0, 0],\n [0, 0, 1, 1, 0, 0, 0],\n [0, 0, 1, 1, 0, 0, 0],\n [0, 0, 1, 1, 0, 0, 0]\n ], //7\n [\n [0, 1, 1, 1, 1, 1, 0],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [0, 1, 1, 1, 1, 1, 0],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [0, 1, 1, 1, 1, 1, 0]\n ], //8\n [\n [0, 1, 1, 1, 1, 1, 0],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [1, 1, 0, 0, 0, 1, 1],\n [0, 1, 1, 1, 0, 1, 1],\n [0, 0, 0, 0, 0, 1, 1],\n [0, 0, 0, 0, 0, 1, 1],\n [0, 0, 0, 0, 1, 1, 0],\n [0, 0, 0, 1, 1, 0, 0],\n [0, 1, 1, 0, 0, 0, 0]\n ], //9\n [\n [0, 0, 0, 0],\n [0, 0, 0, 0],\n [0, 1, 1, 0],\n [0, 1, 1, 0],\n [0, 0, 0, 0],\n [0, 0, 0, 0],\n [0, 1, 1, 0],\n [0, 1, 1, 0],\n [0, 0, 0, 0],\n [0, 0, 0, 0]\n ] //:\n];\n"},{"id":null,"widgetId":292,"name":"IgnorePointer基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【ignoring】 : 是否忽视事件 【bool】\n如下,Switch选中时ignoring为true,按钮事件将被锁定,无法点击。","code":"import 'package:flutter/material.dart';\nclass CustomIgnorePointer extends StatefulWidget {\n @override\n _CustomIgnorePointerState createState() => _CustomIgnorePointerState();\n}\n\nclass _CustomIgnorePointerState extends State {\n bool _ignore = false;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Wrap(\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n GestureDetector(\n onTap: (){\n print('IgnorePointer');\n },\n child: IgnorePointer(\n ignoring: _ignore,\n child: _buildButton(),\n ),\n ),\n _buildSwitch(),\n Text(!_ignore ? '允许点击' : '点击已锁定')\n ],\n ),\n );\n }\n\n Widget _buildButton() => RaisedButton(\n color: Theme.of(context).primaryColor,\n child: Text(\n 'To About',\n style: TextStyle(color: Colors.white),\n ),\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage'));\n\n _buildSwitch() => Switch(\n value: _ignore,\n onChanged: (v) {\n setState(() {\n _ignore = v;\n });\n });\n}\n"},{"id":null,"widgetId":287,"name":"LayoutBuilder基本认识","priority":1,"subtitle":" \n【builder】 : 布局构造器 【LayoutWidgetBuilder】","code":"import 'package:flutter/material.dart';\nclass CustomLayoutBuilder extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n print('CustomLayoutBuild');\n return Container(\n alignment: Alignment.center,\n height: 80,\n width: 150,\n color: Colors.green,\n child: LayoutBuilder(\n builder: (_, zone) {\n return Text(\n '父容器宽:${zone.maxWidth}\\n'\n '父容器高:${zone.maxHeight}',\n style: TextStyle(color: Colors.white, fontSize: 16),\n );\n },\n ),\n );\n }\n}\n"},{"id":null,"widgetId":287,"name":"LayoutBuilder的展开使用","priority":3,"subtitle":" \n使用TextPainter来检测文字的行数,实现展开或收起功能。","code":"import 'package:flutter/material.dart';\nclass SimpleExpandableText extends StatefulWidget {\n\n @override\n createState() => _SimpleExpandableTextState();\n}\n\nclass _SimpleExpandableTextState extends State {\n\n final text = '桃树、杏树、梨树,你不让我,我不让你,都开满了花赶趟儿。'\n '红的像火,粉的像霞,白的像雪。'\n '花里带着甜味儿;闭了眼,树上仿佛已经满是桃儿、杏儿、梨儿。'\n '花下成千成百的蜜蜂嗡嗡地闹着,大小的蝴蝶飞来飞去。'\n '野花遍地是:杂样儿,有名字的,没名字的,散在草丛里,像眼睛,像星星,还眨呀眨的。';\n\n bool expand = false;\n int maxLines =3;\n\n\n final style = TextStyle(fontSize: 15, color: Colors.grey, shadows: [\n Shadow(\n color: Colors.white, offset: Offset(1,1)\n )\n ]);\n\n @override\n build(context) => Container(\n decoration: BoxDecoration(\n color: Colors.cyanAccent.withAlpha(8),\n borderRadius: BorderRadiusDirectional.all(Radius.circular(20))),\n padding: EdgeInsets.all(15),\n child: LayoutBuilder(builder: (context, size) {\n\n final painter = TextPainter(\n text: TextSpan(text: text, style: style),\n maxLines: maxLines,\n textDirection: TextDirection.ltr,\n );\n painter.layout(maxWidth: size.maxWidth);\n if (!painter.didExceedMaxLines)\n return Text(text, style: style);\n\n return Column(\n mainAxisSize: MainAxisSize.min,\n crossAxisAlignment: CrossAxisAlignment.start,\n children: [\n Text(text, maxLines: expand ? null : 3, style: style),\n GestureDetector(\n onTap: () => setState(() {\n expand = !expand;\n }),\n child: Text(\n expand ? '<< 收起' : '展开 >>',\n style: TextStyle(color: Colors.blue),\n ),\n ),\n ],\n );\n }),\n );\n}\n\n"},{"id":null,"widgetId":287,"name":"LayoutBuilder的适应布局","priority":2,"subtitle":" \n可以根据区域的大小进行组件展示设计。\"\n比如在不同的宽度区域显示不同的布局结构。\"\n毕竟很多地方不容易获取父组件区域,使用LayoutBuilder就会非常爽口。","code":"import 'package:flutter/material.dart';\nclass FitByLayoutBuilder extends StatefulWidget {\n @override\n _FitByLayoutBuilderState createState() => _FitByLayoutBuilderState();\n}\n\nclass _FitByLayoutBuilderState extends State {\n double _width = 100;\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Container(\n width: _width,\n child: LayoutBuilder(\n builder: (_, zone) {\n if (zone.maxWidth <= 150) {\n return _buildType1();\n } else {\n return _buildType2(zone);\n }\n },\n ),\n ),\n _buildSlider(),\n ],\n );\n }\n\n Widget _buildSlider() {\n return Slider(\n min: 50,\n max: 300,\n label: \"父宽:${_width.toStringAsFixed(1)}\",\n value: _width,\n onChanged: (v) => setState(() {\n _width = v;\n }));\n }\n\n Widget _buildType1() => Container(\n color: Colors.blue,\n child: Column(\n children: [\n _buildTitle(),\n Padding(\n padding: const EdgeInsets.all(8.0),\n child: _buildContent(),\n ),\n ],\n ),\n );\n\n Widget _buildType2(BoxConstraints zone) => Container(\n height: 100,\n width: zone.maxWidth,\n color: Colors.orange,\n child: Row(\n children: [\n Container(\n margin: EdgeInsets.all(10),\n height: 80,\n width: 30,\n color: Colors.grey,\n ),\n Expanded(child: _buildContent())\n ],\n ),\n );\n\n Widget _buildTitle() => Container(\n margin: EdgeInsets.only(left: 10, right: 10, top: 10),\n color: Colors.grey,\n height: 30,\n );\n\n Widget _buildContent() => Wrap(\n runSpacing: 3,\n children: [\n Container(\n color: Colors.red,\n height: 30,\n ),\n Container(\n color: Colors.yellow,\n height: 30,\n ),\n Container(\n color: Colors.green,\n height: 30,\n ),\n ],\n );\n}\n"},{"id":null,"widgetId":86,"name":"Center基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】","code":"import 'package:flutter/material.dart';\nclass CustomCenter extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n margin: EdgeInsets.all(5),\n width: 200,\n height: 100,\n color: Colors.grey.withAlpha(88),\n child: Center(\n child: Container(\n width: 80,\n height: 60,\n color: Colors.cyanAccent,\n )));\n }\n}\n"},{"id":null,"widgetId":68,"name":"ClipRRect基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【borderRadius】 : 边线半径 【BorderRadius】\n【clipBehavior】 : 裁剪行为 【Clip】\n【clipper】 : 裁剪器 【CustomClipper】","code":"import 'package:flutter/material.dart';\nclass CustomClipRRect extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return ClipRRect(\n borderRadius: BorderRadius.all(Radius.elliptical(35, 30)),\n child: Image.asset(\n \"assets/images/wy_300x200.jpg\",\n width: 150,\n height: 100,\n ),\n );\n }\n}\n"},{"id":null,"widgetId":76,"name":"SizedBox基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【width】 : 宽 【double】\n【height】 : 高 【double】","code":"import 'package:flutter/material.dart';\nclass CustomSizedBox extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var child = Container(\n alignment: Alignment.center,\n color: Colors.cyanAccent,\n width: 50,\n height: 50,\n child: Text(\"Static\"),\n );\n\n var box = SizedBox(\n width: 80,\n height: 40,\n child: Container(\n color: Colors.orange,\n child: Icon(\n Icons.android,\n color: Colors.white,\n )),\n );\n\n return Container(\n color: Colors.grey.withAlpha(22),\n width: 200,\n height: 100,\n child: Row(\n children: [child, box, child],\n ),\n );\n }\n}\n"},{"id":null,"widgetId":77,"name":"AspectRatio基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【aspectRatio】 : 宽高比例 【double】","code":"import 'package:flutter/material.dart';\nclass CustomAspectRatio extends StatefulWidget {\n @override\n _CustomAspectRatioState createState() => _CustomAspectRatioState();\n}\n\nclass _CustomAspectRatioState extends State {\n var _ratio = 0.75;\n\n @override\n Widget build(BuildContext context) {\n var child = Container(\n alignment: Alignment.center,\n color: Colors.cyanAccent,\n width: 50,\n height: 50,\n child: Text(\"Static\"),\n );\n\n var box = AspectRatio(\n aspectRatio: _ratio,\n child: Container(\n color: Colors.orange,\n child: Icon(\n Icons.android,\n color: Colors.white,\n )),\n );\n\n return Column(\n children: [\n _buildSlider(),\n Container(\n color: Colors.grey.withAlpha(22),\n width: 300,\n height: 100,\n child: Row(\n children: [child, box, child],\n ),\n ),\n ],\n );\n }\n\n Widget _buildSlider() => Slider(\n divisions: 20,\n min: 0.1,\n max: 2.0,\n label: _ratio.toStringAsFixed(2),\n value: _ratio,\n onChanged: (v) => setState(() => _ratio = v));\n}\n"},{"id":null,"widgetId":69,"name":"ClipPath基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【clipBehavior】 : 裁剪行为 【Clip】\n【clipper】 : 裁剪器 【CustomClipper】","code":"import 'dart:math';\nimport 'package:flutter/material.dart';\nclass CustomClipPath extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return ClipPath(\n clipper: ShapeBorderClipper(shape: _StarShapeBorder()),\n child: Image.asset(\n \"assets/images/wy_300x200.jpg\",\n width: 150,\n height: 100,\n fit: BoxFit.cover,\n ),\n );\n }\n}\n\nclass _StarShapeBorder extends ShapeBorder {\n final Path _path = Path();\n\n @override\n EdgeInsetsGeometry get dimensions => null;\n\n @override\n Path getInnerPath(Rect rect, {TextDirection textDirection}) {\n return null;\n }\n\n @override\n Path getOuterPath(Rect rect, {TextDirection textDirection}) =>\n nStarPath(20, rect.height / 2, rect.height / 2 * 0.85,\n dx: rect.width / 2, dy: rect.height / 2);\n\n @override\n void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) {}\n\n Path nStarPath(int num, double R, double r, {dx = 0, dy = 0}) {\n double perRad = 2 * pi / num;\n double radA = perRad / 2 / 2;\n double radB = 2 * pi / (num - 1) / 2 - radA / 2 + radA;\n _path.moveTo(cos(radA) * R + dx, -sin(radA) * R + dy);\n for (int i = 0; i < num; i++) {\n _path.lineTo(\n cos(radA + perRad * i) * R + dx, -sin(radA + perRad * i) * R + dy);\n _path.lineTo(\n cos(radB + perRad * i) * r + dx, -sin(radB + perRad * i) * r + dy);\n }\n _path.close();\n return _path;\n }\n\n @override\n ShapeBorder scale(double t) {\n return null;\n }\n}\n"},{"id":null,"widgetId":75,"name":"Baseline基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【baseline】 : 基线位置 【double】\n【baselineType】 : 基线类型 【TextBaseline】","code":"import 'package:flutter/material.dart';\nclass CustomBaseline extends StatefulWidget {\n @override\n _CustomBaselineState createState() => _CustomBaselineState();\n}\n\nclass _CustomBaselineState extends State {\n double _baseline=20;\n\n @override\n Widget build(BuildContext context) {\n\n var childBox = Text(\n '你好,Flutter',\n style: TextStyle(fontSize: 20,fontFamily: \"Menlo\"),\n );\n\n\n var baseline = Baseline(\n child: childBox,\n baseline: _baseline,\n baselineType: TextBaseline.alphabetic\n );\n\n return Column(\n children: [\n _buildSlider(),\n Container(\n width: 100/0.618,\n height: 100,\n color: Colors.grey.withAlpha(22),\n child: baseline,\n ),\n ],\n );\n }\n\n Widget _buildSlider() => Slider(\n divisions: 20,\n min: 0,\n max: 60,\n label: _baseline.toString(),\n value: _baseline,\n onChanged: (v) => setState(() => _baseline = v));\n}\n"},{"id":null,"widgetId":277,"name":"线性渐变着色","priority":2,"subtitle":" \n通过LinearGradient#createShader创建线性渐变着色器\n着色器相关知识详见【绘制专辑】","code":"import 'package:flutter/material.dart';\nclass LinearShaderMask extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 20,\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n ShaderMask(\n shaderCallback: _buildShader,\n child: Image.asset(\n 'assets/images/icon_head.png',\n height: 70,\n width: 70,\n ),\n ),\n ShaderMask(\n shaderCallback: _buildShader,\n child: Text(\n '张风捷特烈',\n style: TextStyle(fontSize: 40, color: Colors.white),\n ),\n ),\n ShaderMask(\n shaderCallback: _buildShader,\n child: Container(\n height: 100,\n color: Colors.white,\n width: 50,\n ),\n ),\n ],\n );\n }\n\n final colors = [Colors.red, Colors.yellow, Colors.blue];\n\n Shader _buildShader(Rect bounds) => LinearGradient(\n begin: Alignment.centerLeft,\n end: Alignment.centerRight,\n tileMode: TileMode.mirror,\n colors: colors)\n .createShader(bounds);\n}\n"},{"id":null,"widgetId":277,"name":"径向渐变着色","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【shaderCallback】 : 着色器回调 【ShaderCallback】\n【blendMode】 : 混色模式 【BlendMode】\n 通过RadialGradient#createShader创建径向渐变着色器。","code":"import 'package:flutter/material.dart';\nclass RadialShaderMask extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 20,\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n ShaderMask(\n shaderCallback: _buildShader,\n child: Image.asset(\n 'assets/images/icon_head.png',\n height: 70,\n width: 70,\n ),\n ),\n ShaderMask(\n shaderCallback: _buildShader,\n child: Text(\n '张风捷特烈',\n style: TextStyle(fontSize: 40, color: Colors.white),\n ),\n ),\n ShaderMask(\n shaderCallback: _buildShader,\n child: Container(\n height: 100,\n color: Colors.white,\n width: 50,\n ),\n ),\n ],\n );\n }\n\n final colors = [Colors.red, Colors.yellow, Colors.blue];\n\n Shader _buildShader(Rect bounds) => RadialGradient(\n center: Alignment.topLeft,\n radius: 1.0,\n tileMode: TileMode.mirror,\n colors: colors)\n .createShader(bounds);\n}"},{"id":null,"widgetId":88,"name":"ColorFiltered基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【colorFilter】 : 滤色器 【ColorFilter】","code":"import 'package:flutter/material.dart';\nimport '../../../../app/utils/color_utils.dart';\nclass CustomColorFiltered extends StatefulWidget {\n @override\n _CustomColorFilteredState createState() => _CustomColorFilteredState();\n}\n\nclass _CustomColorFilteredState extends State {\n Color _color = Colors.blue.withAlpha(88);\n\n @override\n Widget build(BuildContext context) {\n _color = ColorUtils.randomColor();\n return Column(\n children: [\n Wrap(spacing: 10, runSpacing: 10, children: [\n _buildRandomColor(),\n ...BlendMode.values\n .map((mode) => Column(\n children: [\n _buildChild(mode),\n SizedBox(\n height: 10,\n ),\n Text(\n mode.toString().split('.')[1],\n style: TextStyle(fontSize: 10),\n )\n ],\n ))\n .toList()\n ]),\n ],\n );\n ;\n }\n\n Widget _buildChild(m) => Container(\n width: 58,\n height: 58,\n child: ColorFiltered(\n child: Image(image: AssetImage(\"assets/images/icon_head.png\")),\n colorFilter: ColorFilter.mode(_color, m)),\n );\n\n Widget _buildRandomColor() => GestureDetector(\n onTap: () => setState(() {}),\n child: Container(\n alignment: Alignment.center,\n width: 60,\n height: 60,\n decoration: BoxDecoration(color: _color, shape: BoxShape.circle),\n child: Text('点我'),\n ),\n );\n}\n"},{"id":null,"widgetId":298,"name":"IntrinsicHeight基本使用","priority":1,"subtitle":"【child】 : 子组件 【Widget】\n如示例:左侧高可变动,中间高固定,右侧高取前两者的最高值。","code":"import 'package:flutter/material.dart';\nclass IntrinsicHeightDemo extends StatefulWidget {\n @override\n _IntrinsicHeightDemoState createState() => _IntrinsicHeightDemoState();\n}\n\nclass _IntrinsicHeightDemoState extends State {\n var _height =120.0;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Column(\n children: [\n buildChild(_height),\n SizedBox(height: 10),\n _buildSlider()\n ],\n ),\n );\n }\n\n Widget buildChild(double leftHeight) {\n return IntrinsicHeight(\n child: Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n crossAxisAlignment: CrossAxisAlignment.start,\n children: [\n Container(\n height: leftHeight,\n width: 120,\n color: Colors.yellow,\n alignment: Alignment.center,\n child: Text(\"height:${leftHeight.toStringAsFixed(1)}\"),\n ),\n Container(\n color: Colors.blue,\n width: 150,\n height: 80,\n alignment: Alignment.center,\n child: Text(\"固定高\"),\n ),\n Container(\n color: Colors.red,\n width: 60,\n alignment: Alignment.center,\n child: Text(\"最高\"),\n )\n ],\n ),\n );\n }\n\n Widget _buildSlider() =>Slider(\n value: _height,\n max: 200.0,\n min: 30.0,\n divisions: 17,\n onChanged: (v)=> setState(() => _height= v),\n );\n}\n"},{"id":null,"widgetId":70,"name":"DecoratedBox底线装饰","priority":5,"subtitle":"通过UnderlineTabIndicator对象可指定底线,","code":"import 'package:flutter/material.dart';\nclass UnderlineTabIndicatorDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return DecoratedBox(\n decoration: UnderlineTabIndicator(\n insets: EdgeInsets.symmetric(horizontal: 5, vertical: -5),\n borderSide: BorderSide(color: Colors.orange, width: 2)),\n child: Icon(\n Icons.ac_unit,\n color: Colors.blue,\n size: 40,\n ),\n );\n }\n}"},{"id":null,"widgetId":70,"name":"DecoratedBox基本使用","priority":1,"subtitle":" \n【decoration】 : 装饰对象 【Decoration】\n【position】 : 前景色(左)/后景色(右) 【DecorationPosition】","code":"import 'package:flutter/material.dart';\nclass BoxDecorationDemo extends StatelessWidget {\n final rainbow = const [\n 0xffff0000,\n 0xffFF7F00,\n 0xffFFFF00,\n 0xff00FF00,\n 0xff00FFFF,\n 0xff0000FF,\n 0xff8B00FF\n ];\n\n @override\n Widget build(BuildContext context) {\n return DecoratedBox(\n position: DecorationPosition.background,\n decoration: BoxDecoration(\n gradient: LinearGradient(\n stops: [0.0, 1 / 6, 2 / 6, 3 / 6, 4 / 6, 5 / 6, 1.0],\n colors: rainbow.map((e) => Color(e)).toList()),\n borderRadius: BorderRadius.only(\n topLeft: Radius.circular(20), bottomRight: Radius.circular(20)),\n boxShadow: [\n const BoxShadow(\n color: Colors.orangeAccent,\n offset: Offset(1, 1),\n blurRadius: 10,\n spreadRadius: 1),\n ]),\n child: Icon(\n Icons.android,\n size: 80,\n color: Colors.black.withAlpha(123),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":70,"name":"FlutterLogoDecoration装饰","priority":6,"subtitle":"通过FlutterLogoDecoration对象可指定Flutter图标装饰(并没有什么太大的作用),","code":"import 'package:flutter/material.dart';\nclass FlutterLogoDecorationDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return DecoratedBox(\n decoration: FlutterLogoDecoration(\n darkColor: Colors.orange,\n lightColor: Colors.deepPurpleAccent,\n style: FlutterLogoStyle.stacked),\n child: SizedBox(\n width: 100,\n height: 100,\n ),\n );\n }\n}\n"},{"id":null,"widgetId":70,"name":"DecoratedBox形状装饰","priority":4,"subtitle":"通过ShapeDecoration对象可指定边线形状,","code":"import 'package:flutter/material.dart';\nclass ShapeDecorationDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return DecoratedBox(\n decoration: ShapeDecoration(\n shadows: [\n const BoxShadow(\n color: Colors.orangeAccent,\n offset: Offset(0, 0),\n blurRadius: 2,\n spreadRadius: 1),\n ],\n image: DecorationImage(\n fit: BoxFit.cover,\n image: AssetImage(\n 'assets/images/wy_200x300.jpg',\n )),\n shape: CircleBorder(\n side: BorderSide(width: 1.0, color: Colors.orangeAccent),\n )),\n child: SizedBox(\n height: 100,\n width: 100,\n child: Icon(\n Icons.ac_unit,\n color: Colors.white,\n size: 40,\n ),\n ),\n );\n }\n}"},{"id":null,"widgetId":70,"name":"DecoratedBox形状和图片装饰","priority":2,"subtitle":" \n【shape】 : 形状 【BoxShape】\n【image】 : 背景图片 【DecorationImage】,","code":"import 'package:flutter/material.dart';\nclass ShapeImageDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return DecoratedBox(\n decoration: BoxDecoration(\n shape: BoxShape.circle,\n image: DecorationImage(\n fit: BoxFit.cover,\n image: AssetImage(\n 'assets/images/wy_200x300.jpg',\n ))),\n child: SizedBox(\n height: 80,\n width: 80,\n child: Icon(\n Icons.ac_unit,\n color: Colors.white,\n size: 40,\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":70,"name":"DecoratedBox边线装饰","priority":3,"subtitle":"【border】 : 边线 【BoxBorder】,","code":"import 'package:flutter/material.dart';\nclass BorderDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return DecoratedBox(\n position: DecorationPosition.foreground,\n decoration: BoxDecoration(\n border: Border(\n bottom: BorderSide(color: Colors.orange, width: 2),\n top: BorderSide(color: Colors.orange, width: 2)),\n ),\n child: SizedBox(\n height: 80,\n width: 100,\n child: Image.asset(\n 'assets/images/wy_200x300.jpg',\n fit: BoxFit.cover,\n ),\n ),\n );\n }\n}"},{"id":null,"widgetId":85,"name":"Align其他用法","priority":2,"subtitle":" \n由于Alignment对象可指定在父容器中宽高的分率位置\n可以使用Align实现一些复杂的排布需求,比如按指定的数学方程变化位置","code":"import 'dart:math';\nimport 'package:flutter/material.dart';\nclass Ball extends StatelessWidget {\n Ball({\n Key key,\n this.radius = 15,\n this.color = Colors.blue,\n }) : super(key: key);\n final double radius; //半径\n final Color color; //颜色\n\n @override\n Widget build(BuildContext context) {\n return Container(\n width: radius * 2,\n height: radius * 2,\n decoration: BoxDecoration(\n shape: BoxShape.circle,\n color: color,\n ),\n );\n }\n}\n\nclass SinLayout extends StatefulWidget {\n SinLayout({\n Key key,\n }) : super(key: key);\n\n @override\n _SinLayoutState createState() => _SinLayoutState();\n}\n\nclass _SinLayoutState extends State {\n var _x = 0.0; //Alignment坐标系上的x坐标\n\n @override\n Widget build(BuildContext context) {\n var item = Container(\n width: 300,\n height: 120,\n color: Colors.black.withAlpha(10),\n child: Align(\n child: Ball(\n color: Colors.orangeAccent,\n ),\n alignment: Alignment(_x, f(_x * pi)),\n ),\n );\n\n var slider = Slider(\n max: 180,\n min: -180,\n divisions: 360,\n label: \"${_x.toStringAsFixed(2)}π\",\n value: _x * 180,\n onChanged: (v) => setState(() => _x = v / 180));\n return Column(\n mainAxisSize: MainAxisSize.min,\n children: [slider, item],\n );\n }\n\n double f(x) {\n //映射函数 -- 可随意指定\n double y = sin(x);\n return y;\n }\n}\n"},{"id":null,"widgetId":278,"name":"BackdropFilter基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【filter】 : 过滤器 【ImageFilter】\nImageFilter.blur可以实现高斯模糊,指定x,y模糊因子。","code":"import 'dart:ui';\nimport 'package:flutter/material.dart';\nclass CustomBackdropFilter extends StatefulWidget {\n @override\n _CustomBackdropFilterState createState() => _CustomBackdropFilterState();\n}\n\nclass _CustomBackdropFilterState extends State {\n double _sigmaX = 1.2;\n double _sigmaY = 1.2;\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Stack(\n children: [\n _buildImage(),\n Positioned.fill(\n child: ClipRect(\n child: BackdropFilter(\n filter: ImageFilter.blur(sigmaX: _sigmaX, sigmaY: _sigmaY),\n child: Container(\n color: Colors.black.withAlpha(0),\n ),\n ),\n ),\n )\n ],\n ),\n _buildSliders()\n ],\n );\n }\n\n Widget _buildImage() {\n return Wrap(\n spacing: 20,\n children: [\n Container(\n height: 150,\n width: 150,\n child: Image.asset(\n 'assets/images/sabar.jpg',\n fit: BoxFit.cover,\n ),\n ),\n Container(\n height: 150,\n width: 150,\n child: Image.asset(\n 'assets/images/wy_200x300.jpg',\n fit: BoxFit.cover,\n ),\n ),\n ],\n );\n }\n\n Widget _buildSliders() => Column(\n children: [\n Slider(\n min: 0,\n max: 4,\n value: _sigmaX,\n divisions: 360,\n label: 'x:' + _sigmaX.toStringAsFixed(1),\n onChanged: (v) {\n setState(() {\n _sigmaX = v;\n });\n }),\n Slider(\n min: 0,\n max: 4,\n value: _sigmaY,\n divisions: 360,\n label: 'beta:' + _sigmaY.toStringAsFixed(1),\n onChanged: (v) {\n setState(() {\n _sigmaY = v;\n });\n })\n ],\n );\n}\n"},{"id":null,"widgetId":85,"name":"Align基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【alignment】 : 对齐方式 【AlignmentGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomAlign extends StatelessWidget {\n final alignments = [\n Alignment.topLeft,\n Alignment.topCenter,\n Alignment.topRight,\n Alignment.centerLeft,\n Alignment.center,\n Alignment.centerRight,\n Alignment.bottomLeft,\n Alignment.bottomCenter,\n Alignment.bottomRight,\n ];\n\n final alignmentsInfo = [\n \"topLeft\",\n \"topCenter\",\n \"topRight\",\n \"centerLeft\",\n \"center\",\n \"centerRight\",\n \"bottomLeft\",\n \"bottomCenter\",\n \"bottomRight\",\n ];\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: alignments\n .toList()\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 100,\n height: 60,\n color: Colors.grey.withAlpha(88),\n child: Align(\n child: Container(\n width: 30,\n height: 30,\n color: Colors.cyanAccent,\n ),\n alignment: mode)),\n Text(alignmentsInfo[alignments.indexOf(mode)])\n ]))\n .toList());\n }\n}"},{"id":null,"widgetId":297,"name":"IntrinsicWidth基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n如示例:上面宽可变动,中间宽固定,下面宽取前两者的最高值。","code":"import 'package:flutter/material.dart';\nclass IntrinsicWidthDemo extends StatefulWidget {\n @override\n _IntrinsicWidthDemoState createState() => _IntrinsicWidthDemoState();\n}\n\nclass _IntrinsicWidthDemoState extends State {\n var _height =120.0;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Column(\n children: [\n buildChild(_height),\n SizedBox(height: 10),\n _buildSlider()\n ],\n ),\n );\n }\n\n Widget buildChild(double leftWidth) {\n return IntrinsicWidth(\n child: Column(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n crossAxisAlignment: CrossAxisAlignment.start,\n children: [\n Container(\n height: 50,\n width: leftWidth,\n color: Colors.yellow,\n alignment: Alignment.center,\n child: Text(\"width:${leftWidth.toStringAsFixed(1)}\"),\n ),\n Container(\n color: Colors.blue,\n width: 150,\n height: 60,\n alignment: Alignment.center,\n child: Text(\"固定宽\"),\n ),\n Container(\n color: Colors.red,\n height: 40,\n alignment: Alignment.center,\n child: Text(\"最宽\"),\n )\n ],\n ),\n );\n }\n\n Widget _buildSlider() =>Slider(\n value: _height,\n max: 200.0,\n min: 80.0,\n divisions: 17,\n onChanged: (v)=> setState(() => _height= v),\n );\n}\n"},{"id":null,"widgetId":82,"name":"FractionallySizedBox基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【widthFactor】 : 宽分率 【double】\n【heightFactor】 : 高分率 【double】\n【alignment】 : 对齐方式 【AlignmentGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomFractionallySizedBox extends StatefulWidget {\n @override\n _CustomFractionallySizedBoxState createState() =>\n _CustomFractionallySizedBoxState();\n}\n\nclass _CustomFractionallySizedBoxState\n extends State {\n var _hf = 0.5;\n var _wf = 0.4;\n\n @override\n Widget build(BuildContext context) {\n var box = FractionallySizedBox(\n widthFactor: _wf,\n heightFactor: _hf,\n alignment: Alignment.center,\n child: Container(color: Colors.orange),\n );\n return Column(\n children: [\n Container(\n color: Colors.grey.withAlpha(22),\n width: 200,\n height: 100,\n child: box),\n _buildSlider()\n ],\n );\n }\n\n Widget _buildSlider() => Column(\n children: [\n Slider(\n divisions: 20,\n min: 0.0,\n max: 2,\n label: '宽分率:' + _wf.toStringAsFixed(1),\n value: _wf,\n onChanged: (v) => setState(() => _wf = v)),\n Slider(\n divisions: 20,\n min: 0.0,\n max: 2,\n label: '高分率:' + _hf.toStringAsFixed(1),\n value: _hf,\n onChanged: (v) => setState(() => _hf = v)),\n ],\n );\n}\n"},{"id":null,"widgetId":73,"name":"Opacity基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【opacity】 : 透明度0~1 【double】","code":"import 'package:flutter/material.dart';\nclass CustomOpacity extends StatefulWidget {\n @override\n _CustomOpacityState createState() => _CustomOpacityState();\n}\n\nclass _CustomOpacityState extends State {\n var _opacity = 0.2;\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [_buildSlider(), _buildOpacity()],\n );\n }\n // 创建Opacity\n Widget _buildOpacity() => Opacity(\n opacity: _opacity,\n child: Image.asset(// 图片\n 'assets/images/icon_head.png',\n width: 100,\n ),\n );\n Widget _buildSlider() => Slider(\n divisions: 20,\n label: _opacity.toString(),\n value: _opacity,\n onChanged: (v) => setState(() => _opacity = v));\n}\n"},{"id":null,"widgetId":264,"name":"保存Widget成为图片","priority":2,"subtitle":"通过RenderRepaintBoundary可以获取子组件的Image信息,从而获取字节保存为图片文件。","code":"import 'dart:io';\nimport 'dart:typed_data';\nimport 'dart:ui';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/rendering.dart';\nimport 'package:path_provider/path_provider.dart';\nimport 'dart:ui' as ui;\nimport 'node1_base.dart';\nclass RepaintBoundarySave extends StatelessWidget {\n final GlobalKey _globalKey = GlobalKey();\n\n @override\n Widget build(BuildContext context) {\n return Stack(\n children: [\n RepaintBoundary(\n key: _globalKey,\n child: TempPlayBezier3Page(),\n ),\n Positioned(right: -10, child: _buildButton3(context))\n ],\n );\n }\n\n Widget _buildButton3(context) => MaterialButton(\n child: Icon(\n Icons.save_alt,\n size: 15,\n color: Colors.white,\n ),\n color: Colors.green,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onPressed: () async {\n var bits = await _widget2Image(_globalKey);\n var dir = await getApplicationSupportDirectory();\n var file = File(dir.path + \"/save_img.png\");\n var f = await file.writeAsBytes(bits);\n Scaffold.of(context).showSnackBar(SnackBar(\n backgroundColor: Theme.of(context).primaryColor,\n content: Text('保存成功后! 路径为:${f.path}'),\n ));\n });\n\n Future _widget2Image(GlobalKey key) async {\n RenderRepaintBoundary boundary = key.currentContext.findRenderObject();\n //获得 ui.image\n ui.Image img = await boundary.toImage();\n //获取图片字节\n var byteData = await img.toByteData(format: ui.ImageByteFormat.png);\n Uint8List bits = byteData.buffer.asUint8List();\n return bits;\n }\n}\n"},{"id":null,"widgetId":264,"name":"RepaintBoundary基本使用","priority":1,"subtitle":"【child】 : 子组件 【Widget】\n比如上面的绘制视图,即使shouldRepaint为false,在滑动中会也会不断执行paint方法,使用RepaintBoundary可以避免不必要的绘制。","code":"import 'dart:ui';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass RepaintBoundaryDemo extends StatelessWidget{\n @override\n Widget build(BuildContext context) {\n return RepaintBoundary(\n child: TempPlayBezier3Page(),\n );\n }\n}\n\nclass TempPlayBezier3Page extends StatefulWidget {\n @override\n _TempPlayBezier3PageState createState() => _TempPlayBezier3PageState();\n}\n\nclass _TempPlayBezier3PageState extends State {\n List _pos = [];\n int selectPos;\n\n @override\n void initState() {\n _initPoints();\n super.initState();\n }\n\n void _initPoints() {\n _pos = List();\n _pos.add(Offset(0, 0));\n _pos.add(Offset(60, -60));\n _pos.add(Offset(-90, -90));\n _pos.add(Offset(-120, -40));\n }\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n width: MediaQuery.of(context).size.width,\n child: CustomPaint(\n painter: TempBezierPainter(pos: _pos, selectPos: selectPos),\n ),\n );\n }\n}\n\nclass TempBezierPainter extends CustomPainter {\n Paint _gridPaint;\n Path _gridPath;\n\n Paint _mainPaint;\n Path _mainPath;\n int selectPos;\n Paint _helpPaint;\n\n List pos;\n\n TempBezierPainter({this.pos, this.selectPos}) {\n _gridPaint = Paint()..style = PaintingStyle.stroke;\n _gridPath = Path();\n\n _mainPaint = Paint()\n ..color = Colors.orange\n ..style = PaintingStyle.stroke\n ..strokeWidth = 2;\n _mainPath = Path();\n\n _helpPaint = Paint()\n ..color = Colors.purple\n ..style = PaintingStyle.stroke\n ..strokeWidth = 2\n ..strokeCap = StrokeCap.round;\n }\n\n @override\n void paint(Canvas canvas, Size size) {\n print('----------Paint-------');\n canvas.clipRect(Offset.zero & size);\n canvas.translate(size.width / 2, size.height / 2);\n _drawGrid(canvas, size); //绘制格线\n _drawAxis(canvas, size); //绘制轴线\n\n _mainPath.moveTo(pos[0].dx, pos[0].dy);\n _mainPath.cubicTo(\n pos[1].dx, pos[1].dy, pos[2].dx, pos[2].dy, pos[3].dx, pos[3].dy);\n canvas.drawPath(_mainPath, _mainPaint);\n _drawHelp(canvas);\n _drawSelectPos(canvas);\n }\n\n @override\n bool shouldRepaint(CustomPainter oldDelegate) => false;\n\n void _drawGrid(Canvas canvas, Size size) {\n _gridPaint\n ..color = Colors.grey\n ..strokeWidth = 0.5;\n _gridPath = _buildGridPath(_gridPath, size);\n canvas.drawPath(_buildGridPath(_gridPath, size), _gridPaint);\n\n canvas.save();\n canvas.scale(1, -1); //沿x轴镜像\n canvas.drawPath(_gridPath, _gridPaint);\n canvas.restore();\n\n canvas.save();\n canvas.scale(-1, 1); //沿y轴镜像\n canvas.drawPath(_gridPath, _gridPaint);\n canvas.restore();\n\n canvas.save();\n canvas.scale(-1, -1); //沿原点镜像\n canvas.drawPath(_gridPath, _gridPaint);\n canvas.restore();\n }\n\n void _drawAxis(Canvas canvas, Size size) {\n canvas.drawPoints(\n PointMode.lines,\n [\n Offset(-size.width / 2, 0),\n Offset(size.width / 2, 0),\n Offset(0, -size.height / 2),\n Offset(0, size.height / 2),\n Offset(0, size.height / 2),\n Offset(0 - 7.0, size.height / 2 - 10),\n Offset(0, size.height / 2),\n Offset(0 + 7.0, size.height / 2 - 10),\n Offset(size.width / 2, 0),\n Offset(size.width / 2 - 10, 7),\n Offset(size.width / 2, 0),\n Offset(size.width / 2 - 10, -7),\n ],\n _gridPaint\n ..color = Colors.blue\n ..strokeWidth = 1.5);\n }\n\n Path _buildGridPath(Path path, Size size, {step = 20.0}) {\n for (int i = 0; i < size.height / 2 / step; i++) {\n path.moveTo(0, step * i);\n path.relativeLineTo(size.width / 2, 0);\n }\n for (int i = 0; i < size.width / 2 / step; i++) {\n path.moveTo(step * i, 0);\n path.relativeLineTo(\n 0,\n size.height / 2,\n );\n }\n return path;\n }\n\n void _drawHelp(Canvas canvas) {\n canvas.drawPoints(PointMode.lines, pos, _helpPaint..strokeWidth = 1);\n canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8);\n }\n\n void _drawSelectPos(Canvas canvas) {\n if (selectPos == null) return;\n canvas.drawCircle(\n pos[selectPos],\n 10,\n _helpPaint\n ..color = Colors.green\n ..strokeWidth = 2);\n }\n}\n"},{"id":null,"widgetId":81,"name":"UnConstrainedBox基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【constrainedAxis】 : 仍受约束的轴*2 【Axis】\n【alignment】 : 对齐方式 【AlignmentGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomUnConstrainedBox extends StatefulWidget {\n @override\n _CustomUnConstrainedBoxState createState() => _CustomUnConstrainedBoxState();\n}\n\nclass _CustomUnConstrainedBoxState extends State {\n var _value = false;\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 20,\n children: [_buildUnconstrainedBox(), _buildConstrainedAxis()],\n );\n }\n\n Widget _buildUnconstrainedBox() {\n var child = Container(\n color: Colors.cyanAccent,\n width: 60,\n height: 60,\n child: Switch(\n value: _value,\n onChanged: (v) {\n setState(() {\n _value = v;\n });\n },\n ),\n );\n\n return Column(\n children: [\n Container(\n color: Colors.grey.withAlpha(22),\n width: 150,\n height: 100,\n child: _value\n ? UnconstrainedBox(alignment: Alignment.center, child: child)\n : child,\n ),\n Text(_value ? \"已解除约束\" : \"子组件受约束\")\n ],\n );\n }\n\n Widget _buildConstrainedAxis() {\n return Column(\n children: [\n Container(\n color: Colors.grey.withAlpha(22),\n width: 150,\n height: 100,\n child: UnconstrainedBox(\n alignment: Alignment.center,\n constrainedAxis: Axis.vertical,\n child: Container(\n color: Colors.cyanAccent,\n width: 60,\n height: 60,\n )),\n ),\n Text(\"竖直方向仍约束\")\n ],\n );\n }\n}\n"},{"id":null,"widgetId":201,"name":"AnimatedSize基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【duration】 : 动画时长 【Duration】\n【alignment】 : 对齐方式 【AlignmentGeometry】\n【curve】 : 动画曲线 【Duration】\n【vsync】 : vsync 【TickerProvider】","code":"import 'package:flutter/material.dart';\nclass CustomAnimatedSize extends StatefulWidget {\n @override\n _CustomAnimatedSizeState createState() => _CustomAnimatedSizeState();\n}\n\nclass _CustomAnimatedSizeState extends State\n with SingleTickerProviderStateMixin {\n final double start = 100;\n final double end = 200;\n\n double _width;\n\n @override\n void initState() {\n _width = start;\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildSwitch(),\n Container(\n color: Colors.grey.withAlpha(22),\n width: 200,\n height: 100,\n alignment: Alignment.center,\n child: AnimatedSize(\n vsync: this,\n duration: Duration(seconds: 1),\n curve: Curves.fastOutSlowIn,\n alignment: Alignment(0, 0),\n child: Container(\n height: 40,\n width: _width,\n alignment: Alignment.center,\n color: Colors.blue,\n child: Text(\n '张风捷特烈',\n style: TextStyle(color: Colors.white),\n ),\n ),\n ),\n ),\n ],\n );\n }\n\n Widget _buildSwitch() => Switch(\n value: _width == end,\n onChanged: (v) {\n setState(() {\n _width = v ? end : start;\n });\n });\n}\n"},{"id":null,"widgetId":89,"name":"FadeTransition基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【opacity】 : 动画 【Animation】","code":"import 'package:flutter/material.dart';\nclass CustomFadeTransition extends StatefulWidget {\n @override\n _CustomFadeTransitionState createState() => _CustomFadeTransitionState();\n}\n\nclass _CustomFadeTransitionState extends State\n with SingleTickerProviderStateMixin {\n AnimationController _ctrl;\n\n @override\n void initState() {\n _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 2));\n _ctrl.forward();\n super.initState();\n }\n\n @override\n void dispose() {\n _ctrl.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onTap: () {\n setState(() {\n _ctrl.reset();\n _ctrl.forward();\n });\n },\n child: Container(\n color: Colors.grey.withAlpha(22),\n width: 100,\n height: 100,\n child: FadeTransition(\n opacity: CurvedAnimation(parent: _ctrl, curve: Curves.linear),\n child: Icon(Icons.android, color: Colors.green, size: 60),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":285,"name":"CustomSingleChildLayout的偏移使用","priority":2,"subtitle":" \n可以利用代理的偏移能力,对子组件进行偏移定位。","code":"import 'package:flutter/material.dart';\nclass OffSetWidgetDemo extends StatelessWidget {\n final data = [\n {\n 'offset': Offset(20, 20),\n 'direction': Direction.topLeft,\n },\n {\n 'offset': Offset(20, -15),\n 'direction': Direction.topRight,\n },\n {\n 'offset': Offset(-15, 20),\n 'direction': Direction.bottomLeft,\n },\n {\n 'offset': Offset(-15, 20),\n 'direction': Direction.bottomLeft,\n },\n {\n 'offset': Offset(15, 20),\n 'direction': Direction.bottomLeft,\n },\n {\n 'offset': Offset(-15, -15),\n 'direction': Direction.topRight,\n },\n ];\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 20,\n runSpacing: 20,\n children: data\n .map((e) => Container(\n width: 150,\n height: 100,\n alignment: Alignment.topRight,\n color: Colors.grey.withAlpha(11),\n child: OffSetWidget(\n offset: e['offset'],\n direction: e['direction'],\n child: Icon(\n Icons.android,\n size: 30,\n color: Colors.green,\n ),\n )))\n .toList());\n }\n}\n\nclass OffSetWidget extends StatelessWidget {\n final Offset offset;\n final Widget child;\n final Direction direction;\n\n OffSetWidget(\n {this.offset = Offset.zero,\n this.child,\n this.direction = Direction.topLeft});\n\n @override\n Widget build(BuildContext context) {\n return CustomSingleChildLayout(\n delegate: _OffSetDelegate(offset: offset, direction: direction),\n child: child,\n );\n }\n}\n\nenum Direction { topLeft, topRight, bottomLeft, bottomRight }\n\nclass _OffSetDelegate extends SingleChildLayoutDelegate {\n final Offset offset;\n final Direction direction;\n\n _OffSetDelegate(\n {this.offset = Offset.zero, this.direction = Direction.topLeft});\n\n @override\n bool shouldRelayout(_OffSetDelegate oldDelegate) =>\n offset != oldDelegate.offset;\n\n @override\n Offset getPositionForChild(Size size, Size childSize) {\n var w = size.width;\n var h = size.height;\n var wc = childSize.width;\n var hc = childSize.height;\n\n switch (direction) {\n case Direction.topLeft:\n return offset;\n case Direction.topRight:\n return offset.translate(w - wc - offset.dx * 2, 0);\n case Direction.bottomLeft:\n return offset.translate(0, h - hc - offset.dy * 2);\n case Direction.bottomRight:\n return offset.translate(w - wc - offset.dx * 2, h - hc - offset.dy * 2);\n }\n return offset;\n }\n}\n"},{"id":null,"widgetId":285,"name":"CustomSingleChildLayout基本使用","priority":1,"subtitle":" \n【delegate】 : 代理 【SingleChildLayoutDelegate】","code":"import 'package:flutter/material.dart';\nclass CustomSingleChildLayoutDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n print('-------CustomSingleChildLayoutDemo------');\n return Container(\n width: 300,\n height: 200,\n color: Colors.grey.withAlpha(11),\n child: CustomSingleChildLayout(\n delegate: _TolySingleChildLayoutDelegate(),\n child: Container(\n color: Colors.orange,\n ),\n ),\n );\n }\n}\n\nclass _TolySingleChildLayoutDelegate extends SingleChildLayoutDelegate {\n @override\n bool shouldRelayout(SingleChildLayoutDelegate oldDelegate) {\n return true;\n }\n\n @override\n Size getSize(BoxConstraints constraints) {\n print('----getSize:----constraints:$constraints----');\n return super.getSize(constraints);\n }\n\n @override\n Offset getPositionForChild(Size size, Size childSize) {\n print('----getPositionForChild: size:$size----childSize:$childSize----');\n return Offset(size.width / 2, 0);\n }\n\n @override\n BoxConstraints getConstraintsForChild(BoxConstraints constraints) {\n print('----getConstraintsForChild:----constraints:$constraints----');\n return BoxConstraints(\n maxWidth: constraints.maxWidth / 2,\n maxHeight: constraints.maxHeight / 2,\n minHeight: constraints.maxHeight / 4,\n minWidth: constraints.maxWidth / 4,\n );\n }\n}"},{"id":null,"widgetId":74,"name":"Padding单独边距边距","priority":2,"subtitle":" \nEdgeInsets.only用来限定相同的四边边距","code":"import 'package:flutter/material.dart';\nclass PaddingOnly extends StatelessWidget {\n\n @override\n Widget build(BuildContext context) {\n return Container(\n color: Colors.grey.withAlpha(22),\n width: 200,\n height: 150,\n child: Padding(\n padding: EdgeInsets.only(top:10,left: 10),\n child: _buildChild(),\n ),\n );\n }\n\n Widget _buildChild() {\n return Container(\n alignment: Alignment.center,\n color: Colors.cyanAccent,\n width: 100,\n height: 100,\n child: Text(\"孩子\"),\n );\n }\n}"},{"id":null,"widgetId":74,"name":"Padding方向边距","priority":3,"subtitle":" \nEdgeInsets.symmetric用来限定水平和竖直方向的边距","code":"import 'package:flutter/material.dart';\nclass PaddingSymmetric extends StatelessWidget {\n\n @override\n Widget build(BuildContext context) {\n return Container(\n color: Colors.grey.withAlpha(22),\n width: 200,\n height: 150,\n child: Padding(\n padding: EdgeInsets.symmetric(vertical: 30,horizontal: 10),\n child: _buildChild(),\n ),\n );\n }\n\n Widget _buildChild() {\n return Container(\n alignment: Alignment.center,\n color: Colors.cyanAccent,\n width: 100,\n height: 100,\n child: Text(\"孩子\"),\n );\n }\n}"},{"id":null,"widgetId":74,"name":"Padding四面等边距","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【padding】 : 内四边距 【EdgeInsetsGeometry】\"\nEdgeInsets.all用来限定相同的四边边距","code":"import 'package:flutter/material.dart';\nclass PaddingAll extends StatelessWidget {\n\n @override\n Widget build(BuildContext context) {\n return Container(\n color: Colors.grey.withAlpha(22),\n width: 200,\n height: 150,\n child: Padding(\n padding: EdgeInsets.all(20),\n child: _buildChild(),\n ),\n );\n }\n\n Widget _buildChild() {\n return Container(\n alignment: Alignment.center,\n color: Colors.cyanAccent,\n width: 100,\n height: 100,\n child: Text(\"孩子\"),\n );\n }\n}"},{"id":null,"widgetId":66,"name":"ClipOval基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【clipBehavior】 : 裁剪行为 【Clip】\n【clipper】 : 裁剪器 【CustomClipper】","code":"import 'package:flutter/material.dart';\nclass CustomClipOval extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 20,\n children: [\n ClipOval(\n\n child: Image.asset(\n \"assets/images/wy_300x200.jpg\",\n width: 150,\n height: 100,\n ),\n ),\n ClipOval(\n child: Image.asset(\n \"assets/images/wy_300x200.jpg\",\n width: 100,\n height: 100,\n fit: BoxFit.cover,\n ),\n ),\n ],\n );\n }\n}"},{"id":null,"widgetId":263,"name":"FractionalTranslation基本使用","priority":1,"subtitle":"【translation】 : 偏移分度值 【Offset】\n【child】: 子组件 【Widget】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass FractionalTranslationDemo extends StatefulWidget {\n @override\n _FractionalTranslationDemoState createState() =>\n _FractionalTranslationDemoState();\n}\n\nclass _FractionalTranslationDemoState extends State {\n var dx = 0.0;\n var dy = 0.0;\n\n @override\n Widget build(BuildContext context) {\n print(dx);\n return Column(\n mainAxisSize: MainAxisSize.min,\n children: [\n Container(\n width: 200,\n height: 100,\n alignment: Alignment.topLeft,\n color: Colors.grey.withAlpha(33),\n child: FractionalTranslation(\n translation: Offset(dx, dy),\n child: Icon(\n Icons.android,\n color: Colors.green,\n ),\n ),\n ),\n _buildSliderX(),\n _buildSliderY()\n ],\n );\n }\n\n Widget _buildSliderX() => Slider(\n min: -2.0,\n max: 10.0,\n value: dx,\n divisions: 100,\n label: 'dx:${dx.toStringAsFixed(1)}',\n onChanged: (v) => setState(() => dx = v),\n );\n\n\n Widget _buildSliderY() => Slider(\n min: -2.0,\n max: 6.0,\n value: dy,\n divisions: 100,\n label: 'dy:${dy.toStringAsFixed(1)}',\n onChanged: (v) => setState(() => dy = v),\n );\n}\n"},{"id":null,"widgetId":80,"name":"BoxConstraints基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【minWidth】 : 最小宽 【double】\n【minHeight】 : 最小高 【double】\n【maxHeight】 : 最大高 【double】\n【maxWidth】 : 最大宽 【double】","code":"import 'package:flutter/material.dart';\nclass CustomConstrainedBox extends StatefulWidget {\n @override\n _CustomConstrainedBoxState createState() => _CustomConstrainedBoxState();\n}\n\nclass _CustomConstrainedBoxState extends State {\n var _text = '';\n\n @override\n Widget build(BuildContext context) {\n var child = Container(\n alignment: Alignment.center,\n color: Colors.cyanAccent,\n width: 40,\n height: 40,\n child: Text(\"Static\"),\n );\n\n var box = ConstrainedBox(\n constraints: BoxConstraints(\n minHeight: 50,\n minWidth: 20,\n maxHeight: 80,\n maxWidth: 150,\n ),\n child: Container(color: Colors.orange, child: Text(_text)),\n );\n return Column(\n children: [\n Container(\n color: Colors.grey.withAlpha(22),\n width: 300,\n height: 100,\n child: Row(\n children: [child, UnconstrainedBox(child: box), child],\n ),\n ),\n _buildInput(),\n ],\n );\n }\n\n Widget _buildInput() {\n return Padding(\n padding: const EdgeInsets.all(18.0),\n child: TextField(\n decoration: InputDecoration(\n border: OutlineInputBorder(),\n hintText: '请输入',\n ),\n onChanged: (v) {\n setState(() {\n _text = v;\n });\n },\n ),\n );\n }\n}\n"},{"id":null,"widgetId":87,"name":"FittedBox基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【fit】 : 适应模式 【BoxFit】\n【alignment】 : 对齐方式 【AlignmentGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomFittedBox extends StatefulWidget {\n @override\n _CustomFittedBoxState createState() => _CustomFittedBoxState();\n}\n\nclass _CustomFittedBoxState extends State {\n double _childW = 20;\n double _childH = 30;\n\n final rainbow = [\n 0xffff0000,\n 0xffFF7F00,\n 0xffFFFF00,\n 0xff00FF00,\n 0xff00FFFF,\n 0xff0000FF,\n 0xff8B00FF\n ];\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Wrap(\n spacing: 10,\n runSpacing: 10,\n children: BoxFit.values\n .map((mode) => Column(\n children: [\n _buildChild(mode),\n SizedBox(\n height: 10,\n ),\n Text(mode.toString().split('.')[1])\n ],\n ))\n .toList()),\n _buildSlider()\n ],\n );\n }\n\n Widget _buildChild(BoxFit m) {\n return Container(\n color: Colors.grey.withAlpha(44),\n width: 80,\n height: 60,\n child: FittedBox(\n fit: m,\n child: Container(\n width: _childW,\n height: _childH,\n decoration: BoxDecoration(\n //添加渐变色\n gradient: LinearGradient(\n stops: [0.0, 1 / 6, 2 / 6, 3 / 6, 4 / 6, 5 / 6, 1.0],\n colors: rainbow.map((e) => Color(e)).toList()),\n ),\n ),\n ),\n );\n }\n\n Widget _buildSlider() => Column(\n children: [\n Slider(\n min: 10,\n max: 150,\n divisions: 100,\n label: '子宽度:' + _childW.toStringAsFixed(1),\n value: _childW,\n onChanged: (v) => setState(() => _childW = v)),\n Slider(\n min: 10,\n max: 150,\n divisions: 100,\n label: '子高度:' + _childH.toStringAsFixed(1),\n value: _childH,\n onChanged: (v) => setState(() => _childH = v)),\n ],\n );\n}\n"},{"id":null,"widgetId":78,"name":"平移变换translationValues","priority":2,"subtitle":" \n平移x由R0C3数控制,入参为数值,表示平移长度\n平移y由R1C3数控制,入参为数值,表示平移长度\n平移z由R2C3数控制,入参为数值,表示平移长度","code":"import 'package:flutter/material.dart';\nimport 'matrix4_shower.dart';\nclass TranslationTransform extends StatefulWidget {\n @override\n _TranslationTransformState createState() => _TranslationTransformState();\n}\n\nclass _TranslationTransformState extends State {\n Matrix4 _m4;\n double _x = 0;\n double _y = 0;\n double _z = 0;\n\n @override\n void initState() {\n _m4 = Matrix4.identity();\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [_buildTransform(), Matrix4Shower(_m4)],\n ),\n _buildSliders()\n ],\n );\n }\n\n Widget _buildTransform() {\n _m4 = Matrix4.translationValues(_x, _y, _z);\n return Transform(\n transform: _m4,\n child: Container(\n color: Colors.cyanAccent,\n width: 100,\n height: 100,\n child: Image.asset(\n 'assets/images/wy_300x200.jpg',\n fit: BoxFit.cover,\n )),\n );\n }\n\n Widget _buildSliders() => Column(\n children: [\n Slider(\n min: -100,\n max: 100,\n value: _x,\n divisions: 360,\n label: 'x:${_x.toStringAsFixed(1)}',\n onChanged: (v) {\n setState(() {\n _x = v;\n });\n }),\n Slider(\n min: -100,\n max: 100,\n value: _y,\n divisions: 360,\n label: 'y:${_y.toStringAsFixed(1)}',\n onChanged: (v) {\n setState(() {\n _y = v;\n });\n }),\n Slider(\n min: -100,\n max: 100,\n value: _z,\n divisions: 360,\n label: 'z:${_z.toStringAsFixed(1)}',\n onChanged: (v) {\n setState(() {\n _z = v;\n });\n })\n ],\n );\n}\n"},{"id":null,"widgetId":78,"name":"斜切变换skew","priority":1,"subtitle":" \n斜切x由R0C1数控制,入参为弧度值,表示斜切角度\n斜切y由R1C0数控制,入参为弧度值,表示斜切角度","code":"import 'dart:math';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'matrix4_shower.dart';\nclass SkewTransform extends StatefulWidget {\n @override\n _SkewTransformState createState() => _SkewTransformState();\n}\n\nclass _SkewTransformState extends State {\n Matrix4 _m4;\n double _alpha = 0;\n double _beta = 0;\n\n @override\n void initState() {\n _m4 = Matrix4.identity();\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [_buildTransform(), Matrix4Shower(_m4)],\n ),\n _buildSliders()\n ],\n );\n }\n\n Widget _buildTransform() {\n _m4 = Matrix4.skew(_alpha, _beta);\n return Transform(\n transform: _m4,\n child: Container(\n color: Colors.cyanAccent,\n width: 100,\n height: 100,\n child: Image.asset(\n 'assets/images/wy_300x200.jpg',\n fit: BoxFit.cover,\n )),\n );\n }\n\n Widget _buildSliders() => Column(\n children: [\n Slider(\n min: -pi,\n max: pi,\n value: _alpha,\n divisions: 360,\n label: 'alpha:' + (_alpha * 180 / pi).toStringAsFixed(1) + \"°\",\n onChanged: (v) {\n setState(() {\n _alpha = v;\n });\n }),\n Slider(\n min: -pi,\n max: pi,\n value: _beta,\n divisions: 360,\n label: 'beta:' + (_beta * 180 / pi).toStringAsFixed(1) + \"°\",\n onChanged: (v) {\n setState(() {\n _beta = v;\n });\n })\n ],\n );\n}"},{"id":null,"widgetId":78,"name":"透视变换rotation","priority":5,"subtitle":" \n由R3C1、R3C2、R3C3控制透视","code":"import 'dart:math';\nimport 'package:flutter/material.dart';\nclass R3C2 extends StatefulWidget {\n @override\n _R3C2State createState() => _R3C2State();\n}\n\nclass _R3C2State extends State {\n Matrix4 _m4;\n double _value = 0;\n double _rad = 0;\n\n @override\n Widget build(BuildContext context) {\n _m4 = Matrix4.identity()\n// ..setEntry(3, 0, _value) // x\n// ..setEntry(3, 1, _value)// y\n ..setEntry(3, 2, _value) // z\n ..rotateY(_rad)\n// ..rotateX(_rad)\n ;\n return Column(\n children: [\n Transform(\n transform: _m4,\n child: Container(\n color: Colors.cyanAccent,\n width: 100,\n height: 100,\n child: Image.asset(\n 'assets/images/wy_300x200.jpg',\n fit: BoxFit.cover,\n )),\n ),\n _buildSliders()\n ],\n );\n }\n\n Widget _buildSliders() => Column(\n children: [\n Slider(\n min: -0.01,\n max: 0.01,\n value: _value,\n divisions: 360,\n label: 'x:${_value.toStringAsFixed(5)}',\n onChanged: (v) {\n setState(() {\n _value = v;\n });\n }),\n Slider(\n min: -pi,\n max: pi,\n value: _rad,\n divisions: 360,\n label: '角度:' + (_rad * 180 / pi).toStringAsFixed(1) + \"°\",\n onChanged: (v) {\n setState(() {\n _rad = v;\n });\n }),\n ],\n );\n}"},{"id":null,"widgetId":78,"name":"旋转变换rotation","priority":4,"subtitle":" \nx旋转由R1C1、R1C2、R2C1、R2C2控制,入参表示弧度\ny旋转由R0C0、R0C2、R2C0、R2C2控制,入参表示弧度\nz旋转由R0C0、R0C1、R1C0、R1C1控制,","code":"import 'dart:math';\nimport 'package:flutter/material.dart';\nimport 'matrix4_shower.dart';\nclass RotateTransform extends StatefulWidget {\n @override\n _RotateTransformState createState() => _RotateTransformState();\n}\n\nclass _RotateTransformState extends State {\n Matrix4 _m4;\n double _x = 0;\n int _rotateFlag = 1;\n\n @override\n void initState() {\n _m4 = Matrix4.identity();\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [_buildTransform(), Matrix4Shower(_m4)],\n ),\n _buildSliders()\n ],\n );\n }\n\n Widget _buildTransform() {\n if (_rotateFlag == 1) {\n _m4 = Matrix4.rotationX(_x);\n } else if (_rotateFlag == 2) {\n _m4 = Matrix4.rotationY(_x);\n } else {\n _m4 = Matrix4.rotationZ(_x);\n }\n\n return Transform(\n transform: _m4,\n child: Container(\n color: Colors.cyanAccent,\n width: 100,\n height: 100,\n child: Image.asset(\n 'assets/images/wy_300x200.jpg',\n fit: BoxFit.cover,\n )),\n );\n }\n\n final Map map = {\n 1: 'rotationX',\n 2: 'rotationY',\n 3: 'rotationZ',\n };\n\n Widget _buildSliders() => Column(\n children: [\n Wrap(\n children: map.keys.map((key) => _buildChild(key)).toList(),\n ),\n Slider(\n min: -pi,\n max: pi,\n value: _x,\n divisions: 360,\n label: 'x:${_x.toStringAsFixed(1)}',\n onChanged: (v) {\n setState(() {\n _x = v;\n });\n }),\n ],\n );\n\n Padding _buildChild(int key) {\n return Padding(\n padding: const EdgeInsets.all(4.0),\n child: FilterChip(\n selectedColor: Colors.orange.withAlpha(55),\n selectedShadowColor: Colors.blue,\n shadowColor: Colors.orangeAccent,\n pressElevation: 5,\n elevation: 3,\n avatar: CircleAvatar(child: Text(key.toString())),\n label: Text(map[key]),\n selected: _rotateFlag == key,\n onSelected: (bool value) {\n print(map[key]);\n setState(() {\n _x = 0;\n if (value) {\n _rotateFlag = key;\n }\n });\n },\n ),\n );\n }\n}\n"},{"id":null,"widgetId":78,"name":"缩放变换diagonal3Values","priority":3,"subtitle":" \n缩放x由R0C0数控制,入参为数值,表示缩放分率\n缩放y由R1C2数控制,入参为数值,表示缩放分率\n缩放z由R2C2数控制,入参为数值,表示缩放分率","code":"import 'package:flutter/material.dart';\nimport 'matrix4_shower.dart';\nclass ScaleTransform extends StatefulWidget {\n @override\n _ScaleTransformState createState() => _ScaleTransformState();\n}\n\nclass _ScaleTransformState extends State {\n Matrix4 _m4;\n double _x = 1.0;\n double _y = 1.0;\n double _z = 1.0;\n\n @override\n void initState() {\n _m4 = Matrix4.identity();\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [_buildTransform(), Matrix4Shower(_m4)],\n ),\n _buildSliders()\n ],\n );\n }\n\n Widget _buildTransform() {\n _m4 = Matrix4.diagonal3Values(_x, _y, _z);\n return Transform(\n transform: _m4,\n child: Container(\n color: Colors.cyanAccent,\n width: 100,\n height: 100,\n child: Image.asset(\n 'assets/images/wy_300x200.jpg',\n fit: BoxFit.cover,\n )),\n );\n }\n\n Widget _buildSliders() => Column(\n children: [\n Slider(\n min: -2,\n max: 2,\n value: _x,\n divisions: 360,\n label: 'x:${_x.toStringAsFixed(1)}',\n onChanged: (v) {\n setState(() {\n _x = v;\n });\n }),\n Slider(\n min: -2,\n max: 2,\n value: _y,\n divisions: 360,\n label: 'y:${_y.toStringAsFixed(1)}',\n onChanged: (v) {\n setState(() {\n _y = v;\n });\n }),\n Slider(\n min: -2,\n max: 2,\n value: _z,\n divisions: 360,\n label: 'z:${_z.toStringAsFixed(1)}',\n onChanged: (v) {\n setState(() {\n _z = v;\n });\n })\n ],\n );\n}"},{"id":null,"widgetId":295,"name":"AbsorbPointer基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【absorbing】 : 是否吸收事件 【bool】\n如下,Switch选中时absorbing为true,按钮事件将被吸收,无法点击。","code":"import 'package:flutter/material.dart';\nclass CustomAbsorbPointer extends StatefulWidget {\n @override\n _CustomAbsorbPointerState createState() => _CustomAbsorbPointerState();\n}\n\nclass _CustomAbsorbPointerState extends State {\n bool _absorbing = false;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Wrap(\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n GestureDetector(\n onTap: (){\n print('AbsorbPointer');\n },\n child: AbsorbPointer(\n absorbing: _absorbing,\n child: _buildButton(),\n ),\n ),\n _buildSwitch(),\n Text(!_absorbing ? '允许点击' : '事件已被吸收')\n ],\n ),\n );\n }\n\n Widget _buildButton() => RaisedButton(\n color: Theme.of(context).primaryColor,\n child: Text(\n 'To About',\n style: TextStyle(color: Colors.white),\n ),\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage'));\n\n _buildSwitch() => Switch(\n value: _absorbing,\n onChanged: (v) {\n setState(() {\n _absorbing = v;\n });\n });\n}\n"},{"id":null,"widgetId":84,"name":"SizedOverflowBox基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【size】 : 尺寸偏移 【Size】\n【alignment】 : 对齐方式 【AlignmentGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomSizedOverflowBox extends StatefulWidget {\n\n @override\n _CustomSizedOverflowBoxState createState() => _CustomSizedOverflowBoxState();\n}\n\nclass _CustomSizedOverflowBoxState extends State {\n double _x = 50;\n double _y = 44;\n\n @override\n Widget build(BuildContext context) {\n var box = SizedOverflowBox(\n alignment: Alignment.bottomRight,\n size: Size(_x, _y),\n child: Container(width: 30, height: 50, color: Colors.orange),\n );\n return Column(\n children: [\n Container(\n alignment: Alignment.topLeft,\n color: Colors.grey.withAlpha(88),\n width: 250,\n height: 60,\n child: box),\n _buildSlider()\n ],\n );\n }\n\n Widget _buildSlider() =>\n Column(\n children: [\n Slider(\n divisions: 100,\n min: 0,\n max: 250,\n label: 'x:' + _x.toStringAsFixed(1),\n value: _x,\n onChanged: (v) => setState(() => _x = v)),\n Slider(\n divisions: 100,\n min: 0,\n max: 100,\n label: 'y:' + _y.toStringAsFixed(1),\n value: _y,\n onChanged: (v) => setState(() => _y = v)),\n\n ],\n );\n}\n"},{"id":null,"widgetId":192,"name":"SliverOpacity基本使用","priority":1,"subtitle":" \n【opacity】 : 透明度 【double】\n【sliver】 : 子组件 【Function()】","code":"import 'package:flutter/material.dart';\nclass SliverOpacityDemo extends StatelessWidget {\n final data = List.generate(128, (i) => Color(0xFF6600FF - 2 * i));\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 300,\n child: CustomScrollView(\n slivers: [\n _buildSliverAppBar(),\n SliverPadding(\n padding: EdgeInsets.only(top: 10),\n sliver: SliverOpacity(opacity: 0.2, sliver: _buildSliverGrid()))\n ],\n ),\n );\n }\n\n Widget _buildSliverGrid() => SliverGrid.extent(\n childAspectRatio: 1 / 0.618,\n maxCrossAxisExtent: 180,\n crossAxisSpacing: 5,\n mainAxisSpacing: 5,\n children: data\n .map((e) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 60,\n color: e,\n child: Text(\n colorString(e),\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 Widget _buildSliverAppBar() {\n return SliverAppBar(\n expandedHeight: 190.0,\n leading: _buildLeading(),\n title: Text('张风捷特烈'),\n actions: _buildActions(),\n elevation: 5,\n pinned: true,\n backgroundColor: Colors.orange,\n flexibleSpace: FlexibleSpaceBar(\n //伸展处布局\n titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距\n collapseMode: CollapseMode.parallax, //视差效果\n background: Image.asset(\n \"assets/images/caver.jpeg\",\n fit: BoxFit.cover,\n ),\n ),\n );\n }\n\n Widget _buildLeading() => Container(\n margin: EdgeInsets.all(10),\n child: Image.asset('assets/images/icon_head.png'));\n\n List _buildActions() => [\n IconButton(\n onPressed: () {},\n icon: Icon(\n Icons.star_border,\n color: Colors.white,\n ),\n )\n ];\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":183,"name":"CustomScrollView基本使用","priority":1,"subtitle":" \n【slivers】 : 子组件列表 【List】\n【reverse】 : 是否反向 【bool】\n【scrollDirection】 : 滑动方向 【Axis】\n【controller】 : 控制器 【ScrollController】","code":"import 'package:flutter/material.dart';\nclass CustomScrollViewDemo 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: 300,\n child: CustomScrollView(\n anchor: 0,\n scrollDirection: Axis.vertical,\n reverse: false,\n slivers: [_buildSliverAppBar(), _buildSliverFixedExtentList()],\n ),\n );\n }\n\n Widget _buildSliverFixedExtentList() => SliverFixedExtentList(\n itemExtent: 60,\n delegate: SliverChildBuilderDelegate(\n (_, int index) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 50,\n color: data[index],\n child: Text(\n colorString(data[index]),\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 childCount: data.length),\n );\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n\n _buildSliverAppBar() {\n return SliverAppBar(\n expandedHeight: 190.0,\n leading: Container(\n margin: EdgeInsets.all(10),\n child: Image.asset('assets/images/icon_head.png')),\n flexibleSpace: FlexibleSpaceBar(\n //伸展处布局\n titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距\n collapseMode: CollapseMode.parallax, //视差效果\n title: Text(\n '张风捷特烈',\n style: TextStyle(color: Colors.black, //标题\n shadows: [\n Shadow(color: Colors.blue, offset: Offset(1, 1), blurRadius: 2)\n ]),\n ),\n background: Image.asset(\n \"assets/images/caver.jpeg\", fit: BoxFit.cover,\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":191,"name":"SliverPadding基本使用","priority":1,"subtitle":" \n【sliver】 : 子组件 【Widget】\n【padding】 : 内边距 【EdgeInsetsGeometry】","code":"import 'package:flutter/material.dart';\nclass SliverPaddingDemo extends StatelessWidget {\n final data = List.generate(128, (i) => Color(0xFF6600FF - 2 * i));\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 300,\n child: CustomScrollView(\n slivers: [_buildSliverAppBar(), SliverPadding(\n padding: EdgeInsets.only(top: 10),\n sliver\n : _buildSliverGrid())],\n ),\n );\n }\n\n Widget _buildSliverGrid() => SliverGrid.extent(\n childAspectRatio: 1 / 0.618,\n maxCrossAxisExtent: 180,\n crossAxisSpacing: 5,\n mainAxisSpacing: 5,\n children: data\n .map((e) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 60,\n color: e,\n child: Text(\n colorString(e),\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 Widget _buildSliverAppBar() {\n return SliverAppBar(\n expandedHeight: 190.0,\n leading: _buildLeading(),\n title: Text('张风捷特烈'),\n actions: _buildActions(),\n elevation: 5,\n pinned: true,\n backgroundColor: Colors.orange,\n flexibleSpace: FlexibleSpaceBar(\n //伸展处布局\n titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距\n collapseMode: CollapseMode.parallax, //视差效果\n background: Image.asset(\n \"assets/images/caver.jpeg\",\n fit: BoxFit.cover,\n ),\n ),\n );\n }\n\n Widget _buildLeading() => Container(\n margin: EdgeInsets.all(10),\n child: Image.asset('assets/images/icon_head.png'));\n\n List _buildActions() => [\n IconButton(\n onPressed: () {},\n icon: Icon(\n Icons.star_border,\n color: Colors.white,\n ),\n )\n ];\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":188,"name":"SliverList基本使用","priority":1,"subtitle":" \nSliverGrid.count 指定轴向数量构造\nSliverGrid.extent 指定轴向长度构造\n属性特征同GridView,可详见之","code":"import 'package:flutter/material.dart';\nclass SliverGirdDemo extends StatelessWidget {\n final data = List.generate(128, (i) => Color(0xFF6600FF - 2 * i));\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 300,\n child: CustomScrollView(\n slivers: [_buildSliverAppBar(), _buildSliverList()],\n ),\n );\n }\n\n Widget _buildSliverList() => SliverGrid.extent(\n childAspectRatio: 1 / 0.618,\n maxCrossAxisExtent: 180,\n crossAxisSpacing: 5,\n mainAxisSpacing: 5,\n children: data\n .map((e) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 60,\n color: e,\n child: Text(\n colorString(e),\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 Widget _buildSliverAppBar() {\n return SliverAppBar(\n expandedHeight: 190.0,\n leading: _buildLeading(),\n title: Text('张风捷特烈'),\n actions: _buildActions(),\n elevation: 5,\n pinned: true,\n backgroundColor: Colors.orange,\n flexibleSpace: FlexibleSpaceBar(\n //伸展处布局\n titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距\n collapseMode: CollapseMode.parallax, //视差效果\n background: Image.asset(\n \"assets/images/caver.jpeg\",\n fit: BoxFit.cover,\n ),\n ),\n );\n }\n\n Widget _buildLeading() => Container(\n margin: EdgeInsets.all(10),\n child: Image.asset('assets/images/icon_head.png'));\n\n List _buildActions() => [\n IconButton(\n onPressed: () {},\n icon: Icon(\n Icons.star_border,\n color: Colors.white,\n ),\n )\n ];\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":184,"name":"SliverAppBar基本使用","priority":1,"subtitle":" \n【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","code":"import 'package:flutter/material.dart';\nclass SliverAppBarDemo extends StatefulWidget {\n @override\n _SliverAppBarDemoState createState() => _SliverAppBarDemoState();\n}\n\nclass _SliverAppBarDemoState extends State {\n bool _floating = false;\n bool _pinned = false;\n bool _snap = false;\n\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 Column(\n children: [\n _buildTool(),\n Container(\n height: 300,\n child: CustomScrollView(\n slivers: [\n _buildSliverAppBar(),\n _buildSliverFixedExtentList()\n ],\n ),\n ),\n ],\n );\n }\n\n Widget _buildSliverAppBar() {\n print(_floating);\n return SliverAppBar(\n expandedHeight: 190.0,\n leading: _buildLeading(),\n title: Text('张风捷特烈'),\n actions: _buildActions(),\n elevation: 5,\n floating: _floating,\n pinned: _pinned,\n snap: _snap,\n backgroundColor: Colors.orange,\n flexibleSpace: FlexibleSpaceBar(//伸展处布局\n titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距\n collapseMode: CollapseMode.parallax, //视差效果\n background: Image.asset(\n \"assets/images/caver.jpeg\",\n fit: BoxFit.cover,\n ),\n ),\n );\n }\n\n Widget _buildLeading() => Container(\n margin: EdgeInsets.all(10),\n child: Image.asset('assets/images/icon_head.png'));\n\n List _buildActions() => [\n IconButton(\n onPressed: () {},\n icon: Icon(\n Icons.star_border,\n color: Colors.white,\n ),\n )\n ];\n\n Widget _buildSliverFixedExtentList() => SliverFixedExtentList(\n itemExtent: 60,\n delegate: SliverChildBuilderDelegate(\n (_, int index) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 50,\n color: data[index],\n child: Text(\n colorString(data[index]),\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 childCount: data.length),\n );\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n\n Widget _buildTool() {\n return Row(\n mainAxisAlignment: MainAxisAlignment.center,\n children: [\n Wrap(\n direction: Axis.vertical,\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n Text('floating'),\n Switch(\n value: _floating,\n onChanged: (v) {\n if(_snap&&!v){\n _snap =false;\n }\n setState(() => _floating = v);\n }),\n ],\n ),\n Wrap(\n direction: Axis.vertical,\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n Text('pinned'),\n Switch(\n value: _pinned,\n onChanged: (v) => setState(() => _pinned = v)),\n ],\n ) ,Wrap(\n direction: Axis.vertical,\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n Text('snap'),\n Switch(\n value: _snap,\n onChanged: (v) {\n if(_floating){\n setState(() => _snap = v);\n }\n\n }),\n ],\n )\n ],\n );\n }\n}\n"},{"id":null,"widgetId":185,"name":"SliverList基本使用","priority":1,"subtitle":" \n【delegate】 : 孩子代理 【SliverChildDelegate】","code":"import 'package:flutter/material.dart';\nclass SliverListDemo 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: 300,\n child: CustomScrollView(\n slivers: [_buildSliverAppBar(), _buildSliverList()],\n ),\n );\n }\n\n Widget _buildSliverList() => SliverList(\n delegate: SliverChildBuilderDelegate(\n (_, int index) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 60,\n color: data[index],\n child: Text(\n colorString(data[index]),\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 childCount: data.length),\n );\n\n Widget _buildSliverAppBar() {\n return SliverAppBar(\n expandedHeight: 190.0,\n leading: _buildLeading(),\n title: Text('张风捷特烈'),\n actions: _buildActions(),\n elevation: 5,\n pinned: true,\n backgroundColor: Colors.orange,\n flexibleSpace: FlexibleSpaceBar(\n //伸展处布局\n titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距\n collapseMode: CollapseMode.parallax, //视差效果\n background: Image.asset(\n \"assets/images/caver.jpeg\",\n fit: BoxFit.cover,\n ),\n ),\n );\n }\n\n Widget _buildLeading() => Container(\n margin: EdgeInsets.all(10),\n child: Image.asset('assets/images/icon_head.png'));\n\n List _buildActions() => [\n IconButton(\n onPressed: () {},\n icon: Icon(\n Icons.star_border,\n color: Colors.white,\n ),\n )\n ];\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":307,"name":"SliverOverlapAbsorber基本使用","priority":1,"subtitle":" \n【sliver】 : 子组件 【Widget】\n【handle】 : *处理器 【SliverOverlapAbsorberHandle】\n如果不使用SliverOverlapAbsorber和SliverOverlapInjector组件,NestedScrollView的内容会和头部栏重叠。","code":"import 'package:flutter/material.dart';\nclass SliverOverlapAbsorberDemo extends StatelessWidget {\n final _tabs = ['风神传', '封妖志', \"幻将录\", \"永恒传说\"];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width,\n height: MediaQuery.of(context).size.height - 200,\n child: Scaffold(\n body: DefaultTabController(\n length: _tabs.length,\n child: NestedScrollView(\n headerSliverBuilder:\n (BuildContext context, bool innerBoxIsScrolled) {\n return [\n SliverOverlapAbsorber(\n handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),\n sliver: SliverAppBar(\n title: const Text('旷古奇书'),\n pinned: true,\n elevation: 6, //影深\n expandedHeight: 220.0,\n forceElevated: innerBoxIsScrolled, //为true时展开有阴影\n flexibleSpace: FlexibleSpaceBar(\n background: Image.asset(\n \"assets/images/wy_300x200_filter.jpg\",\n fit: BoxFit.cover,\n ),\n ),\n bottom: TabBar(\n tabs: _tabs\n .map((String name) => Tab(text: name,))\n .toList(),\n ),\n ),\n ),\n ];\n },\n body: _buildTabBarView(),\n ),\n ),\n ));\n }\n\n Widget _buildTabBarView() {\n return TabBarView(\n children: _tabs.map((String name) {\n return SafeArea(\n top: false,\n bottom: false,\n child: Builder(\n builder: (BuildContext context) {\n return CustomScrollView(\n key: PageStorageKey(name),\n slivers: [\n SliverOverlapInjector(\n handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),\n ),\n SliverPadding(\n padding: const EdgeInsets.all(8.0),\n sliver: SliverFixedExtentList(\n itemExtent: 48.0,\n delegate: SliverChildBuilderDelegate(\n (BuildContext context, int index) {\n return ListTile(\n title: Text('《$name》 第 $index章'),\n );\n },\n childCount: 50,\n ),\n ),\n ),\n ],\n );\n },\n ),\n );\n }).toList(),\n );\n }\n}\n"},{"id":null,"widgetId":186,"name":"SliverFixedExtentList基本使用","priority":1,"subtitle":" \n【itemExtent】 : 主轴方向强迫长度 【double】\n【delegate】 : 孩子代理 【SliverChildDelegate】","code":"import 'package:flutter/material.dart';\nclass SliverFixedExtentListDemo extends StatefulWidget {\n @override\n _SliverFixedExtentListDemoState createState() => _SliverFixedExtentListDemoState();\n}\n\nclass _SliverFixedExtentListDemoState extends State {\n final data = [\n Colors.orange[50],\n Colors.orange[100],\n Colors.orange[200],\n Colors.orange[300],\n Colors.orange[400],\n Colors.orange[500],\n Colors.orange[600],\n Colors.orange[700],\n Colors.orange[800],\n Colors.orange[900],\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 300,\n child: CustomScrollView(\n slivers: [_buildSliverAppBar(), _buildSliverList()],\n ),\n );\n }\n\n Widget _buildSliverList() => SliverFixedExtentList(\n itemExtent: 50,\n delegate: SliverChildBuilderDelegate(\n (_, int index) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 60,\n color: data[index],\n child: Text(\n colorString(data[index]),\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 childCount: data.length),\n );\n\n Widget _buildSliverAppBar() {\n return SliverAppBar(\n expandedHeight: 190.0,\n leading: _buildLeading(),\n title: Text('张风捷特烈'),\n actions: _buildActions(),\n elevation: 5,\n pinned: true,\n backgroundColor: Colors.orange,\n flexibleSpace: FlexibleSpaceBar(\n //伸展处布局\n titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距\n collapseMode: CollapseMode.parallax, //视差效果\n background: Image.asset(\n \"assets/images/caver.jpeg\",\n fit: BoxFit.cover,\n ),\n ),\n );\n }\n\n Widget _buildLeading() => Container(\n margin: EdgeInsets.all(10),\n child: Image.asset('assets/images/icon_head.png'));\n\n List _buildActions() => [\n IconButton(\n onPressed: () {},\n icon: Icon(\n Icons.star_border,\n color: Colors.white,\n ),\n )\n ];\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":189,"name":"SliverToBoxAdapter基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】","code":"import 'package:flutter/material.dart';\nclass SliverToBoxAdapterDemo 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: 300,\n child: CustomScrollView(\n slivers: [\n _buildSliverAppBar(),\n _buildCommonWidget(),\n _buildSliverList()\n ],\n ),\n );\n }\n\n Widget _buildCommonWidget() => SliverToBoxAdapter(\n child: Container(\n padding: EdgeInsets.symmetric(horizontal: 10),\n color: Colors.grey.withAlpha(22),\n child: ListTile(\n leading: Image.asset(\"assets/images/icon_head.png\"),\n title: Text(\"以梦为马\"),\n subtitle: Text(\"海子\"),\n selected: true,\n contentPadding: EdgeInsets.all(5),\n trailing: Icon(Icons.more_vert),\n ),\n ),\n );\n\n Widget _buildSliverList() => SliverList(\n delegate: SliverChildBuilderDelegate(\n (_, int index) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 60,\n color: data[index],\n child: Text(\n colorString(data[index]),\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 childCount: data.length),\n );\n\n Widget _buildSliverAppBar() {\n return SliverAppBar(\n expandedHeight: 190.0,\n leading: _buildLeading(),\n title: Text('张风捷特烈'),\n actions: _buildActions(),\n elevation: 2,\n pinned: true,\n backgroundColor: Colors.orange,\n flexibleSpace: FlexibleSpaceBar(\n //伸展处布局\n titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距\n collapseMode: CollapseMode.parallax, //视差效果\n background: Image.asset(\n \"assets/images/caver.jpeg\",\n fit: BoxFit.cover,\n ),\n ),\n );\n }\n\n Widget _buildLeading() => Container(\n margin: EdgeInsets.all(10),\n child: Image.asset('assets/images/icon_head.png'));\n\n List _buildActions() => [\n IconButton(\n onPressed: () {},\n icon: Icon(\n Icons.star_border,\n color: Colors.white,\n ),\n )\n ];\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":308,"name":"SliverOverlapInjector基本使用","priority":1,"subtitle":" \n【sliver】 : 子组件 【Widget】\n【handle】 : *处理器 【SliverOverlapAbsorberHandle】\n如果不使用SliverOverlapAbsorber和SliverOverlapInjector组件,NestedScrollView的内容会和头部栏重叠。","code":"import 'package:flutter/material.dart';\nclass SliverOverlapInjectorDemo extends StatelessWidget {\n final _tabs = ['风神传', '封妖志', \"幻将录\", \"永恒传说\"];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width,\n height: MediaQuery.of(context).size.height - 200,\n child: Scaffold(\n body: DefaultTabController(\n length: _tabs.length,\n child: NestedScrollView(\n headerSliverBuilder:\n (BuildContext context, bool innerBoxIsScrolled) {\n return [\n SliverOverlapAbsorber(\n handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),\n sliver: SliverAppBar(\n title: const Text('旷古奇书'),\n pinned: true,\n elevation: 6, //影深\n expandedHeight: 220.0,\n forceElevated: innerBoxIsScrolled, //为true时展开有阴影\n flexibleSpace: FlexibleSpaceBar(\n background: Image.asset(\n \"assets/images/wy_300x200_filter.jpg\",\n fit: BoxFit.cover,\n ),\n ),\n bottom: TabBar(\n tabs: _tabs\n .map((String name) => Tab(text: name,))\n .toList(),\n ),\n ),\n ),\n ];\n },\n body: _buildTabBarView(),\n ),\n ),\n ));\n }\n\n Widget _buildTabBarView() {\n return TabBarView(\n children: _tabs.map((String name) {\n return SafeArea(\n top: false,\n bottom: false,\n child: Builder(\n builder: (BuildContext context) {\n return CustomScrollView(\n key: PageStorageKey(name),\n slivers: [\n SliverOverlapInjector(\n handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),\n ),\n SliverPadding(\n padding: const EdgeInsets.all(8.0),\n sliver: SliverFixedExtentList(\n itemExtent: 48.0,\n delegate: SliverChildBuilderDelegate(\n (BuildContext context, int index) {\n return ListTile(\n title: Text('《$name》 第 $index章'),\n );\n },\n childCount: 50,\n ),\n ),\n ),\n ],\n );\n },\n ),\n );\n }).toList(),\n );\n }\n}\n"},{"id":null,"widgetId":187,"name":"SliverFillViewport基本使用","priority":1,"subtitle":" \n【viewportFraction】 : 视口分率 【double】\n【delegate】 : 孩子代理 【SliverChildDelegate】","code":"import 'package:flutter/material.dart';\nclass SliverFillViewportDemo extends StatefulWidget {\n @override\n _SliverFillViewportDemoState createState() => _SliverFillViewportDemoState();\n}\n\nclass _SliverFillViewportDemoState extends State {\n final data = [\n Colors.orange[50],\n Colors.orange[100],\n Colors.orange[200],\n Colors.orange[300],\n Colors.orange[400],\n Colors.orange[500],\n Colors.orange[600],\n Colors.orange[700],\n Colors.orange[800],\n Colors.orange[900],\n ];\n var _viewportFraction = 0.5;\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildTool(),\n Container(\n height: 300,\n child: CustomScrollView(\n slivers: [_buildSliverAppBar(), _buildSliverList()],\n ),\n ),\n ],\n );\n }\n\n Widget _buildSliverList() => SliverFillViewport(\n viewportFraction: _viewportFraction,\n delegate: SliverChildBuilderDelegate(\n (_, int index) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 60,\n color: data[index],\n child: Text(\n colorString(data[index]),\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 childCount: data.length),\n );\n\n Widget _buildSliverAppBar() {\n return SliverAppBar(\n expandedHeight: 190.0,\n leading: _buildLeading(),\n title: Text('张风捷特烈'),\n actions: _buildActions(),\n elevation: 5,\n pinned: true,\n backgroundColor: Colors.orange,\n flexibleSpace: FlexibleSpaceBar(\n //伸展处布局\n titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距\n collapseMode: CollapseMode.parallax, //视差效果\n background: Image.asset(\n \"assets/images/caver.jpeg\",\n fit: BoxFit.cover,\n ),\n ),\n );\n }\n\n Widget _buildLeading() => Container(\n margin: EdgeInsets.all(10),\n child: Image.asset('assets/images/icon_head.png'));\n\n List _buildActions() => [\n IconButton(\n onPressed: () {},\n icon: Icon(\n Icons.star_border,\n color: Colors.white,\n ),\n )\n ];\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n\n _buildTool() {\n return Slider(\n value: _viewportFraction,\n min: 0.01,\n divisions: 20,\n label: _viewportFraction.toStringAsFixed(1),\n max: 2.0,\n onChanged: (v) => setState(() => _viewportFraction = v));\n }\n}\n"},{"id":null,"widgetId":196,"name":"FlexibleSpaceBar基本使用","priority":1,"subtitle":" \n【title】 : 标题组件 【Widget】\n【titlePadding】 : 标题间距 【EdgeInsetsGeometry】\n【collapseMode】 : 折叠模式 【CollapseMode】\n【stretchModes】 : 延伸模式 【List】\n【background】 : 背景组件 【Widget】\n【centerTitle】 : 是否居中 【bool】","code":"import 'package:flutter/material.dart';\nclass FlexibleSpaceBarDemo extends StatelessWidget {\n\n final data = [\n Colors.blue[50],\n Colors.blue[100],\n Colors.blue[200],\n Colors.blue[300],\n Colors.blue[400],\n Colors.blue[500],\n Colors.blue[600],\n Colors.blue[700],\n Colors.blue[800],\n Colors.blue[900],\n ];\n\n @override\n Widget build(BuildContext context) {\n return\n Container(\n height: 300,\n child: CustomScrollView(\n slivers: [\n _buildSliverAppBar(),\n _buildSliverFixedExtentList()\n ],\n ),\n );\n }\n\n Widget _buildSliverAppBar() {\n return SliverAppBar(\n expandedHeight: 190.0,\n leading: _buildLeading(),\n actions: _buildActions(),\n pinned: true,\n backgroundColor: Colors.blue,\n flexibleSpace: FlexibleSpaceBar(//伸展处布局\n centerTitle: false,\n title: Text('张风捷特烈',style: TextStyle(shadows: [\n Shadow(color: Colors.blue, offset: Offset(1, 1), blurRadius: 2)\n ]),),\n titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距\n collapseMode: CollapseMode.parallax, //视差效果\n stretchModes: [StretchMode.blurBackground,StretchMode.zoomBackground],\n background: Image.asset(\n \"assets/images/caver.jpeg\",\n fit: BoxFit.cover,\n ),\n ),\n );\n }\n\n Widget _buildLeading() => Container(\n margin: EdgeInsets.all(10),\n child: Image.asset('assets/images/icon_head.png'));\n\n List _buildActions() => [\n IconButton(\n onPressed: () {},\n icon: Icon(\n Icons.star_border,\n color: Colors.white,\n ),\n )\n ];\n\n Widget _buildSliverFixedExtentList() => SliverFixedExtentList(\n itemExtent: 60,\n delegate: SliverChildBuilderDelegate(\n (_, int index) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 50,\n color: data[index],\n child: Text(\n colorString(data[index]),\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 childCount: data.length),\n );\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":190,"name":"SliverPersistentHeader基本使用","priority":1,"subtitle":" \n【delegate】 : 代理 【SliverPersistentHeaderDelegate】\n【floating】 : 是否浮动 【bool】\n【pinned】 : 是否顶部停留 【bool】","code":"import 'dart:math';\nimport 'package:flutter/material.dart';\nclass SliverPersistentHeaderDemo 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: 500,\n child: CustomScrollView(\n slivers: [\n _buildSliverAppBar(),\n _buildPersistentHeader('袅缈岁月,青丝银发',Color(0xffe7fcc9)),\n _buildCommonWidget(),\n _buildPersistentHeader('以梦为马,不负韶华',Color(0xffcca4ff)),\n _buildSliverList()\n ],\n ),\n );\n }\n\n Widget _buildCommonWidget() => SliverToBoxAdapter(\n child: Container(\n padding: EdgeInsets.symmetric(horizontal: 10),\n color: Colors.grey.withAlpha(22),\n child: ListTile(\n leading: Image.asset(\"assets/images/icon_head.png\"),\n title: Text(\"以梦为马\"),\n subtitle: Text(\"海子\"),\n selected: true,\n contentPadding: EdgeInsets.all(5),\n trailing: Icon(Icons.more_vert),\n ),\n ),\n );\n Widget _buildPersistentHeader(String text,Color color) => SliverPersistentHeader(\n pinned: true,\n delegate: _SliverDelegate(\n minHeight: 40.0,\n maxHeight: 100.0,\n child: Container(\n color: color,\n child: Center(\n child: Text(text, style: TextStyle(\n fontSize: 18,\n shadows: [Shadow(color: Colors.white, offset: Offset(1, 1))]),\n ),\n )),\n ));\n\n Widget _buildSliverList() => SliverList(\n delegate: SliverChildBuilderDelegate(\n (_, int index) => Container(\n alignment: Alignment.center,\n width: 100,\n height: 60,\n color: data[index],\n child: Text(\n colorString(data[index]),\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 childCount: data.length),\n );\n\n Widget _buildSliverAppBar() {\n return SliverAppBar(\n expandedHeight: 190.0,\n leading: _buildLeading(),\n title: Text('张风捷特烈'),\n actions: _buildActions(),\n elevation: 2,\n pinned: true,\n backgroundColor: Colors.orange,\n flexibleSpace: FlexibleSpaceBar(\n //伸展处布局\n titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距\n collapseMode: CollapseMode.parallax, //视差效果\n background: Image.asset(\n \"assets/images/caver.jpeg\",\n fit: BoxFit.cover,\n ),\n ),\n );\n }\n\n Widget _buildLeading() => Container(\n margin: EdgeInsets.all(10),\n child: Image.asset('assets/images/icon_head.png'));\n\n List _buildActions() => [\n IconButton(\n onPressed: () {},\n icon: Icon(\n Icons.star_border,\n color: Colors.white,\n ),\n )\n ];\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n\n\nclass _SliverDelegate extends SliverPersistentHeaderDelegate {\n _SliverDelegate({\n @required this.minHeight,\n @required this.maxHeight,\n @required this.child,\n });\n\n final double minHeight; //最小高度\n final double maxHeight; //最大高度\n final Widget child; //孩子\n\n @override\n double get minExtent => minHeight;\n\n @override\n double get maxExtent => max(maxHeight, minHeight);\n\n @override\n Widget build(\n BuildContext context, double shrinkOffset, bool overlapsContent) {\n return new SizedBox.expand(child: child);\n }\n\n @override //是否需要重建\n bool shouldRebuild(_SliverDelegate oldDelegate) {\n return maxHeight != oldDelegate.maxHeight ||\n minHeight != oldDelegate.minHeight ||\n child != oldDelegate.child;\n }\n}"},{"id":null,"widgetId":312,"name":"PerformanceOverlay基本使用","priority":1,"subtitle":"使用PerformanceOverlay.allEnabled可以开始所有的监测项。","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass PerformanceOverlayDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return PerformanceOverlay.allEnabled(\n\n );\n }\n}\n\n"},{"id":null,"widgetId":197,"name":"ErrorWidget基本使用","priority":1,"subtitle":" \n入参 : 显示信息 【Object】","code":"import 'package:flutter/material.dart';\nclass ErrorWidgetDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: ErrorWidget(\n 'I am Error ErrorWidget\\n'\n 'But now, there has no error.'\n ),\n );\n }\n}\n"},{"id":null,"widgetId":110,"name":"Table基本使用","priority":1,"subtitle":" \n【children】 : 组件列表 【List】\n【columnWidths】 : 列宽 【Map】\n【defaultColumnWidth】 : 默认列宽 【TableColumnWidth】\n【border】 : 边线 【TableBorder】\n【textDirection】 : 文字方向 【TextDirection】\n【defaultVerticalAlignment】 : 单元格竖直方向对齐模式 【TableCellVerticalAlignment】","code":"import 'package:flutter/material.dart';\nclass CustomTable 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 Center(\n child: Text(\n item.name,\n style: TextStyle(color: Colors.blue),\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"},{"id":null,"widgetId":313,"name":"RawImage基本使用","priority":1,"subtitle":"【image】 : 图片 【ui.Image】\n【width】 : 宽 【int】\n【height】: 高 【int】\n【isAntiAlias】: 是否抗锯齿 【bool】\n【filterQuality】: 过滤质量 【FilterQuality】\n很多属性同Image,详见之.","code":"import 'dart:async';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nimport 'dart:ui' as ui;\nclass RawImageDemo extends StatefulWidget {\n @override\n _RawImageDemoState createState() => _RawImageDemoState();\n}\n\nclass _RawImageDemoState extends State {\n ui.Image _image;\n\n @override\n void initState() {\n super.initState();\n _loadImageFromAssets('assets/images/icon_head.png');\n }\n\n @override\n Widget build(BuildContext context) {\n if (_image == null)\n return Container(\n width: 80,\n height: 80,\n );\n\n return Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [\n Column(\n mainAxisSize: MainAxisSize.min,\n children: [\n RawImage(\n image: _image,\n width: 150,\n height: 150,\n isAntiAlias: true,\n filterQuality: FilterQuality.high,\n ),\n Text('isAntiAlias: true'),\n Text('FilterQuality.high')\n ],\n ),\n Column(\n mainAxisSize: MainAxisSize.min,\n children: [\n RawImage(\n image: _image,\n width: 150,\n height: 150,\n isAntiAlias: false,\n ),\n Text('isAntiAlias: false'),\n Text('FilterQuality.low')\n ],\n ),\n ],\n );\n }\n\n void _loadImageFromAssets(String name) async {\n _image = await loadImageByProvider(AssetImage(name));\n\n setState(() {});\n }\n\n //通过ImageProvider读取Image\n Future loadImageByProvider(\n ImageProvider provider, {\n ImageConfiguration config = ImageConfiguration.empty,\n }) async {\n Completer completer = Completer(); //完成的回调\n ImageStreamListener listener;\n ImageStream stream = provider.resolve(config); //获取图片流\n listener = ImageStreamListener((ImageInfo frame, bool sync) {\n //监听\n final ui.Image image = frame.image;\n completer.complete(image); //完成\n stream.removeListener(listener); //移除监听\n });\n stream.addListener(listener); //添加监听\n return completer.future; //返回\n }\n}\n"},{"id":null,"widgetId":338,"name":"ButtonBarTheme基本使用","priority":1,"subtitle":"可指定ButtonBarThemeData数据属性为【后代】的ButtonBar组件设置默认样式,如对齐方式、样式、边距等。也可以用ButtonBarTheme.of获取ButtonBar的主题属性。","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass ButtonBarThemeDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return ButtonBarTheme(\n child: TempButtonBar(),\n data: ButtonBarTheme.of(context).copyWith(\n alignment: MainAxisAlignment.center,\n buttonPadding: EdgeInsets.symmetric(horizontal: 6),\n overflowDirection: VerticalDirection.up,\n buttonMinWidth: 150,\n buttonHeight: 30,\n buttonTextTheme: ButtonTextTheme.primary));\n }\n}\n\nclass TempButtonBar extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return ButtonBar(\n alignment: MainAxisAlignment.center,\n children: [\n RaisedButton(\n color: Colors.blue, child: Text(\"1.Raised\"), onPressed: () {}),\n OutlineButton(child: Text(\"2.Outline\"), onPressed: () {}),\n FlatButton(\n color: Colors.blue,\n onPressed: () {},\n child: Text(\"3.Flat\"),\n )\n ],\n );\n }\n}\n"},{"id":null,"widgetId":329,"name":"DividerTheme使用","priority":1,"subtitle":" \n属性参数与Divider类似,可以通过DividerTheme.of获取分割线主题数据,\"\n也可以为DividerTheme【后代】的分割线设置默认样式,包括颜色、粗细、高度等。","code":"import 'package:flutter/material.dart';\nclass DividerThemeDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return DividerTheme(\n data: DividerThemeData(\n color: Colors.orange,\n thickness: 2,\n space: 10,\n indent: 10,\n endIndent: 10,\n\n ),\n child: Wrap(\n spacing: 10,\n children: [\n Divider(),\n Divider(),\n Divider(),\n Divider(),\n Divider(),\n Container(\n height: 100,\n child: Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [\n VerticalDivider(),\n VerticalDivider(),\n VerticalDivider(),\n VerticalDivider(),\n VerticalDivider(),\n ],\n ),\n )\n ],\n ),\n );\n }\n}"},{"id":null,"widgetId":334,"name":"ListTileTheme基本使用","priority":1,"subtitle":"可指定ListTileThemeData数据属性为【后代】的ListTile组件设置默认样式,如样式、颜色、装饰、边距等。也可以用ListTileTheme.of获取ListTile的主题属性。","code":"import 'package:flutter/material.dart';\nclass ListTileThemeDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return ListTileTheme(\n dense: false,\n style: ListTileStyle.list,\n selectedColor: Colors.blue,\n contentPadding: EdgeInsets.only(left: 15,right: 15,top: 5,bottom: 5),\n iconColor: Colors.purple,\n textColor: Colors.orange,\n child: _ListTileSimple(),\n );\n }\n}\n\nclass _ListTileSimple extends StatefulWidget {\n @override\n _ListTileSimpleState createState() => _ListTileSimpleState();\n}\n\nclass _ListTileSimpleState extends State<_ListTileSimple> {\n bool _selected = false;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n margin: EdgeInsets.all(10),\n color: Colors.grey.withAlpha(22),\n child: ListTile(\n leading: Image.asset(\"assets/images/icon_head.png\"),\n selected: _selected,\n title: Text(\"以梦为马\"),\n subtitle: Text(\"海子\"),\n trailing: Icon(Icons.more_vert),\n onTap: () => setState(() => _selected = !_selected),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":106,"name":"Expanded基本使用","priority":1,"subtitle":" \n【child】 : 孩子 【Widget】\n【flex】 : 剩余空间分配占比 【int】","code":"import 'package:flutter/material.dart';\nimport '../../../../app/utils/color_utils.dart';\nclass CustomExpended extends StatefulWidget {\n @override\n _CustomExpendedState createState() => _CustomExpendedState();\n}\n\nclass _CustomExpendedState extends State {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Column(\n children: [\n buildRow([0, 0, 0]),\n SizedBox(height: 10,),\n buildRow([0, 0, 1]),\n SizedBox(height: 10,),\n buildRow([1, 1, 1]),\n SizedBox(height: 10,),\n buildRow([2, 3, 3]),\n ],\n ),\n );\n }\n\n Widget buildRow(List num) {\n return Row(\n children: num.map((e) => Expanded(\n flex: e,\n child: Container(\n alignment: Alignment.center,\n width: 50,\n height: 50,\n color: ColorUtils.randomColor(),\n child: Text(\n 'flex=$e',\n style: TextStyle(color: Colors.white),\n ),\n ),\n )).toList());\n }\n}\n"},{"id":null,"widgetId":181,"name":"DropDownButtonHideUnderline使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】,","code":"import 'package:flutter/material.dart';\nclass CustomDropDownButtonHideUnderline extends StatefulWidget {\n @override\n _CustomDropDownButtonHideUnderlineState createState() =>\n _CustomDropDownButtonHideUnderlineState();\n}\n\nclass _CustomDropDownButtonHideUnderlineState\n 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 DropdownButtonHideUnderline(\n child: 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\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":331,"name":"SliderTheme使用","priority":1,"subtitle":" \n可通过SliderTheme.of获取Slider主题数据对象,其中包含大量属性用于对Slider的设定。\"\n可以为ButtonTheme【后代】的按钮组件设置默认样式,包括颜色、形状、尺寸等。","code":"import 'dart:math';\nimport 'package:flutter/material.dart';\nclass SliderThemeDemo extends StatefulWidget {\n @override\n _SliderThemeDemoState createState() => _SliderThemeDemoState();\n}\n\nclass _SliderThemeDemoState extends State {\n var _bliss = 0.5;\n\n @override\n Widget build(BuildContext context) {\n return SliderTheme(\n data: SliderTheme.of(context).copyWith(activeTrackColor: Colors.orange),\n child: Slider(\n min: 0.0,\n max: 200.0,\n divisions: 10,\n label: \"${_bliss.toStringAsFixed(1)}\",\n onChanged: (double value) {\n setState(() {\n _bliss = value;\n });\n },\n value: _bliss,\n ),\n );\n }\n}"},{"id":null,"widgetId":333,"name":"TooltipTheme基本使用","priority":1,"subtitle":"可指定TooltipThemeData数据属性为【后代】的Tooltip组件设置默认样式,如装饰、文字样式、显示时长、边距等。也可以用TooltipTheme.of获取Tooltip的主题属性。","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass TooltipThemeDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return TooltipTheme(\n child: TempTooltip(),\n data: TooltipTheme.of(context).copyWith(\n preferBelow: false,\n padding: EdgeInsets.all(5),\n verticalOffset: 20,\n margin: EdgeInsets.all(2),\n textStyle: TextStyle(\n color: Colors.red,\n shadows: [Shadow(color: Colors.white, offset: Offset(1, 1))]),\n decoration: BoxDecoration(boxShadow: [\n BoxShadow(\n color: Colors.orangeAccent,\n offset: Offset(1, 1),\n blurRadius: 8)\n ])));\n }\n}\n\nclass TempTooltip extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [\n Tooltip(\n message: \"天王盖地虎\",\n child: Icon(Icons.info_outline)),\n Tooltip(\n message: \"宝塔镇河妖\",\n child: Icon(Icons.info_outline)),\n ],\n );\n }\n}\n"},{"id":null,"widgetId":331,"name":"SliderTheme对Slider的样式定制","priority":2,"subtitle":" \n通过thumbShape和valueIndicatorShape可以对Slider进行样式定制。\"\n注: 本例参考flutter-gallery中的SlideDemo","code":"import 'dart:math';\nimport 'package:flutter/material.dart';\nclass DIYSliderTheme extends StatefulWidget {\n @override\n _DIYSliderThemeState createState() => _DIYSliderThemeState();\n}\n\nclass _DIYSliderThemeState extends State {\n var _bliss = 0.5;\n\n @override\n Widget build(BuildContext context) {\n final ThemeData theme = Theme.of(context);\n\n return SliderTheme(\n data: theme.sliderTheme.copyWith(\n activeTrackColor: Colors.deepPurple,\n inactiveTrackColor: Colors.blue.withAlpha(55),\n activeTickMarkColor: theme.colorScheme.onSurface.withOpacity(0.7),\n inactiveTickMarkColor: theme.colorScheme.surface.withOpacity(0.7),\n overlayColor: theme.colorScheme.onSurface.withOpacity(0.12),\n thumbColor: Colors.deepPurple,\n valueIndicatorColor: Colors.deepPurpleAccent,\n thumbShape: _CustomThumbShape(),\n valueIndicatorShape: _CustomValueIndicatorShape(),\n valueIndicatorTextStyle: theme.accentTextTheme.body2\n .copyWith(color: theme.colorScheme.onSurface),\n ),\n child: Slider(\n min: 0.0,\n max: 200.0,\n divisions: 10,\n label: \"${_bliss.toStringAsFixed(1)}\",\n onChanged: (double value) {\n setState(() {\n _bliss = value;\n });\n },\n value: _bliss,\n ),\n );\n }\n}\n\nclass _CustomThumbShape extends SliderComponentShape {\n static const double _thumbSize = 4.0;\n static const double _disabledThumbSize = 3.0;\n\n @override\n Size getPreferredSize(bool isEnabled, bool isDiscrete) {\n return isEnabled\n ? const Size.fromRadius(_thumbSize)\n : const Size.fromRadius(_disabledThumbSize);\n }\n\n static final Animatable sizeTween = Tween(\n begin: _disabledThumbSize,\n end: _thumbSize,\n );\n\n @override\n void paint(PaintingContext context, Offset center,\n {Animation activationAnimation,\n Animation enableAnimation,\n bool isDiscrete,\n TextPainter labelPainter,\n RenderBox parentBox,\n SliderThemeData sliderTheme,\n TextDirection textDirection,\n double value,\n double textScaleFactor,\n Size sizeWithOverflow}) {\n final Canvas canvas = context.canvas;\n final ColorTween colorTween = ColorTween(\n begin: sliderTheme.disabledThumbColor,\n end: sliderTheme.thumbColor,\n );\n final double size = _thumbSize * sizeTween.evaluate(enableAnimation);\n final Path thumbPath = _downTriangle(size, center);\n canvas.drawPath(\n thumbPath, Paint()..color = colorTween.evaluate(enableAnimation));\n }\n}\n\nPath _upTriangle(double size, Offset thumbCenter) =>\n _downTriangle(size, thumbCenter, invert: true);\n\nPath _downTriangle(double size, Offset thumbCenter, {bool invert = false}) {\n final Path thumbPath = Path();\n final double height = sqrt(3.0) / 2.0;\n final double centerHeight = size * height / 3.0;\n final double halfSize = size / 2.0;\n final double sign = invert ? -1.0 : 1.0;\n thumbPath.moveTo(\n thumbCenter.dx - halfSize, thumbCenter.dy + sign * centerHeight);\n thumbPath.lineTo(thumbCenter.dx, thumbCenter.dy - 2.0 * sign * centerHeight);\n thumbPath.lineTo(\n thumbCenter.dx + halfSize, thumbCenter.dy + sign * centerHeight);\n thumbPath.close();\n return thumbPath;\n}\n\nclass _CustomValueIndicatorShape extends SliderComponentShape {\n static const double _indicatorSize = 4.0;\n static const double _disabledIndicatorSize = 3.0;\n static const double _slideUpHeight = 30.0;\n\n @override\n Size getPreferredSize(bool isEnabled, bool isDiscrete) {\n return Size.fromRadius(isEnabled ? _indicatorSize : _disabledIndicatorSize);\n }\n\n static final Animatable sizeTween = Tween(\n begin: _disabledIndicatorSize,\n end: _indicatorSize,\n );\n\n @override\n void paint(PaintingContext context, Offset center,\n {Animation activationAnimation,\n Animation enableAnimation,\n bool isDiscrete,\n TextPainter labelPainter,\n RenderBox parentBox,\n SliderThemeData sliderTheme,\n TextDirection textDirection,\n double value,\n double textScaleFactor,\n Size sizeWithOverflow}) {\n final Canvas canvas = context.canvas;\n final ColorTween enableColor = ColorTween(\n begin: sliderTheme.disabledThumbColor,\n end: sliderTheme.valueIndicatorColor,\n );\n final Tween slideUpTween = Tween(\n begin: 0.0,\n end: _slideUpHeight,\n );\n final double size = _indicatorSize * sizeTween.evaluate(enableAnimation);\n final Offset slideUpOffset =\n Offset(0.0, -slideUpTween.evaluate(activationAnimation));\n final Path thumbPath = _upTriangle(size, center + slideUpOffset);\n final Color paintColor = enableColor\n .evaluate(enableAnimation)\n .withAlpha((255.0 * activationAnimation.value).round());\n canvas.drawPath(\n thumbPath,\n Paint()..color = paintColor,\n );\n canvas.drawLine(\n center,\n center + slideUpOffset,\n Paint()\n ..color = paintColor\n ..style = PaintingStyle.stroke\n ..strokeWidth = 2.0);\n labelPainter.paint(\n canvas,\n center +\n slideUpOffset +\n Offset(-labelPainter.width / 2.0, -labelPainter.height - 4.0));\n }\n}\n"},{"id":null,"widgetId":328,"name":"ChipTheme基本使用","priority":1,"subtitle":"可指定ChipThemeData数据属性为【后代】的Chip类型组件设置默认样式,属性和Chip属性类似,如阴影、颜色、边距、形状、文字样式等。也可以用ChipTheme.of获取Chip的主题数据。","code":"import 'package:flutter/material.dart';\nclass ChipThemeDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return ChipTheme(\n data: ChipTheme.of(context).copyWith(\n selectedColor: Colors.orange.withAlpha(55),\n selectedShadowColor: Colors.blue,\n shadowColor: Colors.orangeAccent,\n pressElevation: 5,\n elevation: 3,\n ),\n child: CustomFilterChip(),\n );\n }\n}\n\nclass CustomFilterChip extends StatefulWidget {\n @override\n _CustomFilterChipState createState() => _CustomFilterChipState();\n}\n\nclass _CustomFilterChipState extends State {\n final Map map = {\n 'A': 'Ant',\n 'B': 'Bug',\n 'C': 'Cat',\n 'D': 'Dog',\n };\n List _selected = [];\n\n @override\n Widget build(BuildContext context) {\n return Column(\n mainAxisAlignment: MainAxisAlignment.center,\n children: [\n Wrap(\n children: map.keys.map((key) => _buildChild(key)).toList(),\n ),\n Container(\n padding: EdgeInsets.all(10),\n child: Text('您已选择: ${_selected.join(', ')}')),\n ],\n );\n }\n\n Padding _buildChild(String key) {\n return Padding(\n padding: const EdgeInsets.all(4.0),\n child: FilterChip(\n avatar: CircleAvatar(child: Text(key)),\n label: Text(map[key]),\n selected: _selected.contains(map[key]),\n onSelected: (bool value) {\n setState(() {\n if (value) {\n _selected.add(map[key]);\n } else {\n _selected.removeWhere((name) => name == map[key]);\n }\n });\n },\n ),\n );\n }\n}\n"},{"id":null,"widgetId":315,"name":"LayoutId使用场景","priority":1,"subtitle":" \n【id】 : 标识id 【Object】\n【child】 : 子组件 【Widget】","code":"import 'dart:io';\nimport 'package:flutter/material.dart';\nclass LayoutIdDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 300,\n height: 150,\n color: Colors.grey.withAlpha(33),\n child: CustomMultiChildLayout(\n delegate: CornerCustomMultiChildLayout(\n padding:EdgeInsets.only(left: 10,top: 5,right: 10,bottom: 5),\n ),\n children: [\n LayoutId(id: CornerType.topLeft, child: Box50(Colors.red)),\n LayoutId(id: CornerType.topRight, child: Box50(Colors.yellow)),\n LayoutId(id: CornerType.bottomLeft, child: Box50(Colors.blue)),\n LayoutId(id: CornerType.bottomRight, child: Box50(Colors.green)),\n ],\n ),\n );\n }\n}\n\n// 50 颜射盒\nclass Box50 extends StatelessWidget {\n final Color color;\n Box50(this.color);\n\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 50,\n height: 50,\n color: color,\n );\n }\n}\n\n\nenum CornerType{\n topLeft,\n topRight,\n bottomLeft,\n bottomRight\n}\n\n\nclass CornerCustomMultiChildLayout extends MultiChildLayoutDelegate{\n final EdgeInsets padding;\n \n CornerCustomMultiChildLayout({this.padding = EdgeInsets.zero});\n\n @override\n void performLayout(Size size) {\n if (hasChild(CornerType.topLeft)) {\n layoutChild(CornerType.topLeft, BoxConstraints.loose(size));\n positionChild(CornerType.topLeft, Offset.zero.translate(padding.left, padding.top));\n }\n if (hasChild(CornerType.topRight)) {\n var childSize = layoutChild(CornerType.topRight, BoxConstraints.loose(size));\n positionChild(CornerType.topRight, Offset(size.width-childSize.width,0).translate(-padding.right, padding.top));\n }\n if (hasChild(CornerType.bottomLeft)) {\n var childSize = layoutChild(CornerType.bottomLeft, BoxConstraints.loose(size));\n positionChild(CornerType.bottomLeft, Offset(0,size.height-childSize.height).translate(padding.left, -padding.bottom));\n }\n if (hasChild(CornerType.bottomRight)) {\n var childSize = layoutChild(CornerType.bottomRight, BoxConstraints.loose(size));\n positionChild(CornerType.bottomRight, Offset(size.width-childSize.width,size.height-childSize.height).translate(-padding.right, -padding.bottom));\n }\n }\n\n @override\n bool shouldRelayout(CornerCustomMultiChildLayout oldDelegate) => oldDelegate.padding!=padding;\n \n}\n\n"},{"id":null,"widgetId":327,"name":"MaterialBannerTheme基本使用","priority":1,"subtitle":"可指定MaterialBannerThemeData数据属性为【后代】的MaterialBanner组件设置默认样式,如背景色、边距、文字样式等。也可以用MaterialBannerTheme.of获取MaterialBanner的主题数据。","code":"import 'package:flutter/material.dart';\nclass MaterialBannerThemeDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return MaterialBannerTheme(\n data: MaterialBannerTheme.of(context).copyWith(\n backgroundColor: Colors.purple,\n padding: EdgeInsetsDirectional.only(start: 16.0, top: 2.0,end: 2),\n leadingPadding:EdgeInsetsDirectional.only(end: 16.0) ,\n contentTextStyle: TextStyle(color: Colors.white),\n ),\n child: _MaterialBannerDemo(),\n );\n }\n}\n\n\nclass _MaterialBannerDemo extends StatelessWidget {\n final info =\n 'A banner displays an important, succinct message, and provides actions for users to address. '\n 'A user action is required for itto be dismissed.';\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [MaterialBanner(\n content: Text(info),\n leading: Icon(Icons.warning, color: Colors.yellow),\n actions: [\n RaisedButton(\n color: Colors.white,\n onPressed: () {},\n child: Text(\n 'I KNOW',\n style: TextStyle(\n color: Colors.purple,\n fontWeight: FontWeight.bold,\n fontSize: 14),\n ),\n ),\n\n RaisedButton(\n color: Colors.white,\n onPressed: () {},\n child: Text(\n 'I IGNORE',\n style: TextStyle(\n color: Colors.purple,\n fontWeight: FontWeight.bold,\n fontSize: 14),\n ),\n ),\n ],\n )],\n );\n }\n}"},{"id":null,"widgetId":109,"name":"Flexible基本使用","priority":1,"subtitle":" \n【child】 : 孩子 【Widget】\n【fit】 : 适应模式*2 【FlexFit】\n【flex】 : 剩余空间分配占比 【int】","code":"import 'package:flutter/material.dart';\nclass CustomFlexible extends StatefulWidget {\n @override\n _CustomFlexibleState createState() => _CustomFlexibleState();\n}\n\nclass _CustomFlexibleState extends State {\n double _width = 300.0;\n bool _loose = false;\n\n @override\n Widget build(BuildContext context) {\n return Column(children: [\n Container(\n color: Colors.grey.withAlpha(33),\n width: _width,\n padding: EdgeInsets.all(8.0),\n child: Row(\n children: [\n Flexible(\n flex: 2,\n child: Container(\n alignment: Alignment.center,\n height: 50,\n color: Colors.red,\n child: Text(\n 'flex=2',\n style: TextStyle(color: Colors.white),\n ),\n ),\n ),\n Flexible(\n flex: 3,\n child: Container(\n alignment: Alignment.center,\n height: 50,\n color: Colors.blue,\n child: Text(\n 'flex=3',\n style: TextStyle(color: Colors.white),\n ),\n ),\n ),\n Flexible(\n flex: 4,\n fit: _loose?FlexFit.loose:FlexFit.tight,\n child: Container(\n constraints: BoxConstraints(maxWidth: 60),\n alignment: Alignment.center,\n height: 50,\n color: Colors.green,\n child: Text(\n 'flex=4 \\nfit:${_loose?'loose':'tight'}',\n style: TextStyle(color: Colors.white),\n ),\n ),\n )\n ],\n )),\n _buildOp()\n ]);\n }\n\n Widget _buildOp() {\n return Row(\n children: [\n Switch(\n value: _loose,\n onChanged: (v) => setState(() => _loose = v)),\n Expanded(\n child: Slider(\n divisions: 10,\n min: 100,\n max: 350,\n value: _width,\n onChanged: (v) => setState(() => _width = v)),\n ),\n ],\n );\n }\n}\n"},{"id":null,"widgetId":180,"name":"ScrollConfiguration基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【behavior】 : 滑动行为 【ScrollBehavior】\n 可以使用ScrollConfiguration让ListView无蓝色阴影","code":"import 'package:flutter/material.dart';\nclass CustomScrollConfiguration extends StatelessWidget {\n final data = [\n Colors.cyan[50],\n Colors.cyan[100],\n Colors.cyan[200],\n Colors.cyan[300],\n Colors.cyan[400],\n Colors.cyan[500],\n Colors.cyan[600],\n Colors.cyan[700],\n Colors.cyan[800],\n Colors.cyan[900],\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: ScrollConfiguration(\n behavior: NoScrollBehavior(), child: _buildListView()),\n );\n }\n\n Widget _buildListView() => 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 String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n\nclass NoScrollBehavior extends ScrollBehavior {\n @override\n Widget buildViewportChrome(\n BuildContext context, Widget child, AxisDirection axisDirection) =>\n child;\n}\n"},{"id":null,"widgetId":108,"name":"Positioned基本使用","priority":1,"subtitle":" \n【child】 : 组件 【Widget】\n【top】 : 到父顶距离 【double】\n【right】 : 到父右距离 【double】\n【left】 : 到父左距离 【double】\n【bottom】 : 到父底距离 【double】","code":"import 'package:flutter/material.dart';\nclass CustomPositioned extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var yellowBox = Container(\n color: Colors.yellow,\n height: 100,\n width: 100,\n );\n\n var redBox = Container(\n color: Colors.red,\n height: 90,\n width: 90,\n );\n\n var greenBox = Container(\n color: Colors.green,\n height: 80,\n width: 80,\n );\n\n var cyanBox = Container(\n color: Colors.cyanAccent,\n height: 70,\n width: 70,\n );\n\n return Container(\n width: 200,\n height: 120,\n color: Colors.grey.withAlpha(33),\n child: Stack(\n children: [\n yellowBox,\n redBox,\n Positioned(top: 20, left: 20, child: greenBox),\n Positioned(\n child: cyanBox,\n bottom: 10,\n right: 10,\n )\n ],\n ));\n }\n}\n"},{"id":null,"widgetId":332,"name":"ToggleButtonsTheme基本使用","priority":1,"subtitle":"可指定ToggleButtonsThemeData数据属性为【后代】的ToggleButtons组件设置默认样式,如边框样式、颜色、装饰等。也可以用ToggleButtonsTheme.of获取ToggleButtons的主题数据。","code":"import 'package:flutter/material.dart';\nclass ToggleButtonsThemeDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return ToggleButtonsTheme(\n data: ToggleButtonsTheme.of(context).copyWith(\n borderWidth: 1,\n borderColor: Colors.orangeAccent,\n selectedBorderColor: Colors.blue,\n splashColor: Colors.purple.withAlpha(66),\n borderRadius: BorderRadius.circular(10),\n selectedColor: Colors.red,\n fillColor: Colors.green.withAlpha(11),\n ),\n child: _ToggleButtonsSimple(),\n );\n }\n}\n\n\nclass _ToggleButtonsSimple extends StatefulWidget {\n @override\n _ToggleButtonsSimpleState createState() => _ToggleButtonsSimpleState();\n}\n\nclass _ToggleButtonsSimpleState extends State<_ToggleButtonsSimple> {\n var _isSelected = [true, false, false];\n\n @override\n Widget build(BuildContext context) {\n return ToggleButtons(\n children: [\n Icon(Icons.skip_previous),\n Icon(Icons.pause),\n Icon(Icons.skip_next),\n ],\n isSelected: _isSelected,\n onPressed: (value) => setState(() {\n _isSelected = _isSelected.map((e) => false).toList();\n _isSelected[value] = true;\n }),\n );\n }\n}"},{"id":null,"widgetId":167,"name":"MediaQuery获取数据信息","priority":1,"subtitle":" \nMediaQuery.of(context)可以获取MediaQueryData","code":"import 'package:flutter/material.dart';\nclass CustomMediaQuery extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var queryData = MediaQuery.of(context);\n var data = {\n \"size\": queryData.size,\n \"devicePixelRatio\": queryData.devicePixelRatio.toStringAsFixed(1),\n \"textScaleFactor\": queryData.textScaleFactor.toStringAsFixed(1),\n \"platformBrightness\": queryData.platformBrightness,\n \"padding\": queryData.padding,\n \"viewInsets\": queryData.viewInsets,\n \"systemGestureInsets\": queryData.padding,\n \"viewPadding\": queryData.padding,\n \"physicalDepth\": queryData.padding,\n \"alwaysUse24HourFormat\": queryData.padding,\n \"accessibleNavigation\": queryData.alwaysUse24HourFormat,\n \"invertColors\": queryData.invertColors,\n \"highContrast\": queryData.highContrast,\n \"disableAnimations\": queryData.disableAnimations,\n \"boldText\": queryData.boldText,\n };\n\n return Container(\n height: 200,\n color: Colors.grey.withAlpha(11),\n child:ListView(\n children: data.keys.map((e) => buildItem(e, data)).toList(),\n ),\n );\n }\n\n Widget buildItem(String e, Map data) => Column(\n children: [\n Padding(\n padding: const EdgeInsets.all(8.0),\n child: Row(\n mainAxisAlignment: MainAxisAlignment.spaceBetween,\n children: [\n Text(\n e,\n style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),\n ),\n Text(\n data[e].toString(),\n style: TextStyle(fontSize: 16, color: Colors.orange),\n )\n ],\n ),\n ),\n Divider(\n height: 1,\n )\n ],\n );\n}\n\n\n"},{"id":null,"widgetId":325,"name":"IconTheme使用","priority":1,"subtitle":" \n可以通过IconTheme.of获取图标主题数据,也可以为IconTheme【后代】的图标组件设置默认样式,包括颜色、透明度、尺寸。","code":"import 'package:flutter/material.dart';\nclass IconThemeDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return IconTheme(\n data: IconThemeData(\n color: Colors.purple,\n opacity: 1.0,\n size: 30\n ),\n child: Wrap(\n spacing: 10,\n children: [\n Icon(Icons.add),\n Icon(Icons.ac_unit),\n Icon(Icons.g_translate),\n Icon(Icons.remove)\n ],\n ),\n );\n }\n}"},{"id":null,"widgetId":324,"name":"DefaultTextStyle使用","priority":1,"subtitle":" \n各属性同Text,详见之。\n其功能是: 设置默认的文字样式应用于【后代组件】,注意后代组件也可以指定自身的样式","code":"import 'package:flutter/material.dart';\nclass DefaultTextStyleDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return DefaultTextStyle(\n style: TextStyle(\n fontSize: 18,\n color: Colors.blue,\n decoration: TextDecoration.underline),\n child: Wrap(\n spacing: 5,\n children: [\n Text(\"Hello,\",),\n FlutterLogo(),\n Text(\"Flutter\",style: TextStyle(color: Colors.red),),\n Text(\"Unit.\"),\n ],\n ),\n );\n }\n}\n"},{"id":null,"widgetId":330,"name":"PopupMenuTheme基本使用","priority":1,"subtitle":"可指定PopupMenuThemeData数据属性为【后代】的PopupMenuButton组件设置默认样式,如形状、影深、颜色、文字样式等。也可以用PopupMenuTheme.of获取PopupMenu的主题数据。","code":"import 'package:flutter/material.dart';\nclass PopupMenuThemeDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return PopupMenuTheme(\n data: PopupMenuTheme.of(context).copyWith(\n color: Colors.orangeAccent,\n elevation: 1,\n textStyle: TextStyle(color: Colors.white),\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 ),\n child: _PopupMenuButtonSimple(),\n );\n }\n}\n\nclass _PopupMenuButtonSimple extends StatefulWidget {\n @override\n _PopupMenuButtonSimpleState createState() => _PopupMenuButtonSimpleState();\n}\n\nclass _PopupMenuButtonSimpleState extends State<_PopupMenuButtonSimple> {\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 onSelected: print,\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: 6,\n children: [\n Icon(\n map[e],\n ),\n Text(e),\n ],\n )))\n .toList();\n }\n}"},{"id":null,"widgetId":326,"name":"ButtonTheme使用","priority":1,"subtitle":" \n属性参数同MaterialButton,可以通过ButtonTheme.of获取按钮主题数据,\"\n也可以为ButtonTheme【后代】的按钮组件设置默认样式,包括颜色、形状、尺寸等。","code":"import 'package:flutter/material.dart';\nclass ButtonThemeDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return ButtonTheme(\n buttonColor: Colors.orange,\n splashColor: Colors.blue,\n minWidth: 40,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n child: Wrap(\n spacing: 10,\n children: [\n RaisedButton(onPressed: (){},child: Icon(Icons.add)),\n FlatButton(onPressed: (){},child: Icon(Icons.add)),\n OutlineButton(onPressed: (){},child: Icon(Icons.add)),\n MaterialButton(onPressed: (){},child: Icon(Icons.add)),\n ],\n ),\n );\n }\n}"},{"id":null,"widgetId":161,"name":"IndexedStack基本使用","priority":1,"subtitle":" \n【children】 : 子组件列表 【Lis】\n【alignment】 : 对齐方式 【AlignmentGeometry】\n【index】 : 当前显示组件 【int】","code":"import 'package:flutter/material.dart';\nclass CustomIndexedStack extends StatefulWidget {\n @override\n _CustomIndexedStackState createState() => _CustomIndexedStackState();\n}\n\nclass _CustomIndexedStackState extends State {\n var _index = 1;\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildSwitch(),\n Container(\n width: 200,\n height: 100,\n color: Colors.grey.withAlpha(33),\n child: IndexedStack(\n index: _index,\n children: [\n Container(\n color: Colors.red,\n width: 80,\n height: 80,\n ),\n Positioned(\n bottom: 10,\n right: 10,\n child: Container(\n color: Colors.blue,\n width: 80,\n height: 80,\n ),\n )\n ],\n ),\n ),\n ],\n );\n }\n\n Widget _buildSwitch() => Switch(\n value: _index == 0,\n onChanged: (v) => setState(() => _index = v ? 0 : 1),\n );\n}\n"},{"id":null,"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个色条并非一次性全部构建。","code":"import 'package:flutter/gestures.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/rendering.dart';\nclass ViewportDemo extends StatelessWidget {\n final data = List.generate(128, (i) => Color(0xFF6600FF - 2 * i));\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 250,\n child: Scrollable(\n axisDirection: AxisDirection.down,\n physics: BouncingScrollPhysics(),\n dragStartBehavior: DragStartBehavior.start,\n viewportBuilder: (ctx, position) => Viewport(\n axisDirection: AxisDirection.down,\n cacheExtent: 200,\n anchor: 0,\n cacheExtentStyle: CacheExtentStyle.pixel,\n offset: position,\n slivers: [_buildSliverList()],\n ),\n ),\n );\n }\n\n Widget _buildSliverList() => SliverList(\n delegate: SliverChildBuilderDelegate(\n (_, int index) =>ColorItem(color: data[index],),\n childCount: data.length),\n );\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n\nclass ColorItem extends StatefulWidget {\n final Color color;\n\n ColorItem({Key key,this.color}) : super(key: key);\n @override\n _ColorItemState createState() => _ColorItemState();\n}\n\nclass _ColorItemState extends State {\n\n @override\n void initState() {\n super.initState();\n print('-----initState----${colorString(widget.color)}-----------');\n }\n\n @override\n Widget build(BuildContext context) {\n return Container(\n margin: EdgeInsets.only(top: 1),\n alignment: Alignment.center,\n width: 100,\n height: 60,\n color: widget.color,\n child: Text(\n colorString(widget.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 }\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":341,"name":"CustomMultiChildLayout基本使用","priority":1,"subtitle":" \n【children】 : 子组件集 【List】\n【delegate】 : 布局代理 【MultiChildLayoutDelegate】","code":"import 'dart:io';\nimport 'package:flutter/material.dart';\nclass CustomMultiChildLayoutDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 300,\n height: 150,\n color: Colors.grey.withAlpha(33),\n child: CustomMultiChildLayout(\n delegate: CornerCustomMultiChildLayout(\n padding:EdgeInsets.only(left: 10,top: 5,right: 10,bottom: 5),\n ),\n children: [\n LayoutId(id: CornerType.topLeft, child: Box50(Colors.red)),\n LayoutId(id: CornerType.topRight, child: Box50(Colors.yellow)),\n LayoutId(id: CornerType.bottomLeft, child: Box50(Colors.blue)),\n LayoutId(id: CornerType.bottomRight, child: Box50(Colors.green)),\n ],\n ),\n );\n }\n}\n\n// 50 颜射盒\nclass Box50 extends StatelessWidget {\n final Color color;\n Box50(this.color);\n\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 50,\n height: 50,\n color: color,\n );\n }\n}\n\n\nenum CornerType{\n topLeft,\n topRight,\n bottomLeft,\n bottomRight\n}\n\n\nclass CornerCustomMultiChildLayout extends MultiChildLayoutDelegate{\n final EdgeInsets padding;\n \n CornerCustomMultiChildLayout({this.padding = EdgeInsets.zero});\n\n @override\n void performLayout(Size size) {\n if (hasChild(CornerType.topLeft)) {\n layoutChild(CornerType.topLeft, BoxConstraints.loose(size));\n positionChild(CornerType.topLeft, Offset.zero.translate(padding.left, padding.top));\n }\n if (hasChild(CornerType.topRight)) {\n var childSize = layoutChild(CornerType.topRight, BoxConstraints.loose(size));\n positionChild(CornerType.topRight, Offset(size.width-childSize.width,0).translate(-padding.right, padding.top));\n }\n if (hasChild(CornerType.bottomLeft)) {\n var childSize = layoutChild(CornerType.bottomLeft, BoxConstraints.loose(size));\n positionChild(CornerType.bottomLeft, Offset(0,size.height-childSize.height).translate(padding.left, -padding.bottom));\n }\n if (hasChild(CornerType.bottomRight)) {\n var childSize = layoutChild(CornerType.bottomRight, BoxConstraints.loose(size));\n positionChild(CornerType.bottomRight, Offset(size.width-childSize.width,size.height-childSize.height).translate(-padding.right, -padding.bottom));\n }\n }\n\n @override\n bool shouldRelayout(CornerCustomMultiChildLayout oldDelegate) => oldDelegate.padding!=padding;\n \n}\n\n"},{"id":null,"widgetId":96,"name":"Column基本使用","priority":1,"subtitle":" \n【children】 : 组件列表 【List】\n【mainAxisAlignment】 : 主轴对齐 【MainAxisAlignment】\n【crossAxisAlignment】 : 交叉轴对齐 【CrossAxisAlignment】\n【textBaseline】 : 文字基线 【TextBaseline】\n【verticalDirection】 : 竖直方向 【VerticalDirection】\n【mainAxisSize】 : 主轴尺寸 【MainAxisSize】","code":"import 'package:flutter/material.dart';\nclass CustomColumn extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildTitle(),\n _buildContent(context),\n ],\n );\n }\n\n Widget _buildTitle() {\n return Container(\n height: 70,\n color: Color(0x4484FFFF),\n child: Row(\n children: [\n Padding(\n child: Icon(\n Icons.add_location,\n size: 30,\n color: Colors.pink,\n ),\n padding: EdgeInsets.only(left: 25, right: 20),\n ),\n Expanded(\n child: Text(\n \"附近\",\n style: TextStyle(fontSize: 18),\n ),\n ),\n Padding(\n child: Icon(Icons.keyboard_arrow_right, color: Colors.black38),\n padding: EdgeInsets.only(right: 25),\n ),\n ],\n ));\n }\n\n Widget _buildContent(ctx) => Container(\n width: MediaQuery.of(ctx).size.width,\n color: Colors.orangeAccent,\n height: 100,\n child: Icon(\n Icons.android,\n size: 50,\n color: Colors.white,\n ),\n );\n}\n"},{"id":null,"widgetId":342,"name":"ListView的基本使用","priority":1,"subtitle":"【mainAxis】 : 主轴方向 【Axis】\n【reverse】: 是否反向 【bool】\n【children】: 子组件集 【List】","code":"import 'package:flutter/gestures.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/rendering.dart';\nclass ListBodyDemo extends StatelessWidget {\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 300,\n child: ListView(\n children: [\n ListBody(\n mainAxis: Axis.vertical,\n reverse: false,\n children: [\n Container(color: Colors.red, height: 50.0,),\n Container(color: Colors.orange, height: 50.0,),\n Container(color: Colors.yellow, height: 50.0,),\n ],\n ),\n Container(color: Colors.green, height: 80.0,),\n ListBody(\n mainAxis: Axis.vertical,\n reverse: false,\n children: [\n Container(color: Colors.blue, height: 50.0,),\n Container(color: Colors.indigo, height: 50.0,),\n Container(color: Colors.purple, height: 50.0,),\n ],\n )\n ]\n ),\n );\n }\n}\n"},{"id":null,"widgetId":95,"name":"Row基本使用","priority":1,"subtitle":" \n【children】 : 组件列表 【List】\n【mainAxisAlignment】 : 主轴对齐 【MainAxisAlignment】\n【crossAxisAlignment】 : 交叉轴对齐 【CrossAxisAlignment】\n【textBaseline】 : 文字基线 【TextBaseline】\n【verticalDirection】 : 竖直方向 【VerticalDirection】\n【mainAxisSize】 : 主轴尺寸 【MainAxisSize】","code":"import 'package:flutter/material.dart';\nclass CustomRow extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 70,\n color: Color(0x4484FFFF),\n child: Row(\n children: [\n Padding(\n child: Icon(\n Icons.add_location,\n size: 30,\n color: Colors.pink,\n ),\n padding: EdgeInsets.only(left: 25, right: 20),\n ),\n Expanded(\n child: Text(\n \"附近\",\n style: TextStyle(fontSize: 18),\n ),\n ),\n Padding(\n child: Icon(Icons.keyboard_arrow_right, color: Colors.black38),\n padding: EdgeInsets.only(right: 25),\n ),\n ],\n ));\n }\n}\n"},{"id":null,"widgetId":98,"name":"Wrap的textDirection属性","priority":4,"subtitle":" \n【textDirection】 : 文字方向 【TextDirection】","code":"import 'package:flutter/material.dart';\nclass TextDirectionWrap extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: TextDirection.values\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 160,\n height: 100,\n color: Colors.grey.withAlpha(88),\n child: _buildItem(mode)),\n Text(mode.toString().split('.')[1])\n ]))\n .toList());\n }\n\n final yellowBox = Container(\n color: Colors.yellow,\n height: 30,\n width: 50,\n );\n\n final redBox = Container(\n color: Colors.red,\n height: 40,\n width: 40,\n );\n final greenBox = Container(\n color: Colors.green,\n height: 40,\n width: 20,\n );\n final blackBox = Container(\n color: Colors.black,\n height: 10,\n width: 10,\n );\n final purpleBox = Container(\n color: Colors.purple,\n height: 20,\n width: 20,\n );\n final orangeBox = Container(\n color: Colors.orange,\n height: 80,\n width: 20,\n );\n final cyanBox = Container(\n color: Colors.cyanAccent,\n height: 10,\n width: 20,\n );\n\n _buildItem(mode) => Wrap(\n textDirection: mode,\n runSpacing: 10,\n spacing: 10,\n children: [\n yellowBox, redBox, greenBox, cyanBox,\n blackBox, purpleBox, orangeBox,\n ],\n );\n}\n"},{"id":null,"widgetId":98,"name":"Wrap的基础用法","priority":1,"subtitle":" \n【children】 : 组件列表 【List】\n【spacing】 : 主轴条目间距 【double】\n【runSpacing】 : 交叉轴条目间距 【double】\n【direction】 : 主轴对齐 【Axis】","code":"import 'package:flutter/material.dart';\nclass DirectionWrap extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: Axis.values\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 160,\n height: 100,\n color: Colors.grey.withAlpha(33),\n child: _buildItem(mode)),\n Text(mode.toString().split('.')[1])\n ]))\n .toList());\n }\n final yellowBox = Container(\n color: Colors.yellow,\n height: 30,\n width: 50,\n );\n\n final redBox = Container(\n color: Colors.red,\n height: 40,\n width: 40,\n );\n final greenBox = Container(\n color: Colors.green,\n height: 40,\n width: 20,\n );\n final blackBox = Container(\n color: Colors.black,\n height: 10,\n width: 10,\n );\n final purpleBox = Container(\n color: Colors.purple,\n height: 20,\n width: 20,\n );\n final orangeBox = Container(\n color: Colors.orange,\n height: 80,\n width: 20,\n );\n final cyanBox = Container(\n color: Colors.cyanAccent,\n height: 10,\n width: 20,\n );\n\n _buildItem(mode) => Wrap(\n direction: mode,\n runSpacing: 10,\n spacing: 10,\n children: [\n yellowBox, redBox, greenBox, cyanBox,\n blackBox, purpleBox, orangeBox,\n ],\n );\n}"},{"id":null,"widgetId":98,"name":"Wrap的verticalDirection属性","priority":5,"subtitle":" \n【verticalDirection】 : 竖直方向 【VerticalDirection】","code":"import 'package:flutter/material.dart';\nclass VerticalDirectionWrap extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: VerticalDirection.values\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 160,\n height: 100,\n color: Colors.grey.withAlpha(88),\n child: _buildItem(mode)),\n Text(mode.toString().split('.')[1])\n ]))\n .toList());\n }\n\n final yellowBox = Container(\n color: Colors.yellow,\n height: 30,\n width: 50,\n );\n\n final redBox = Container(\n color: Colors.red,\n height: 40,\n width: 40,\n );\n final greenBox = Container(\n color: Colors.green,\n height: 40,\n width: 20,\n );\n final blackBox = Container(\n color: Colors.black,\n height: 10,\n width: 10,\n );\n final purpleBox = Container(\n color: Colors.purple,\n height: 20,\n width: 20,\n );\n final orangeBox = Container(\n color: Colors.orange,\n height: 80,\n width: 20,\n );\n final cyanBox = Container(\n color: Colors.cyanAccent,\n height: 10,\n width: 20,\n );\n\n _buildItem(mode) => Wrap(\n verticalDirection: mode,\n direction: Axis.vertical,\n runSpacing: 10,\n spacing: 10,\n children: [\n yellowBox, redBox, greenBox, cyanBox,\n blackBox, purpleBox, orangeBox,\n ],\n );\n}"},{"id":null,"widgetId":98,"name":"Wrap的alignment属性","priority":2,"subtitle":" \n【alignment】 : 主轴对齐 【WrapAlignment】","code":"import 'package:flutter/material.dart';\nclass WrapAlignmentWrap extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: WrapAlignment.values\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 160,\n height: 100,\n color: Colors.grey.withAlpha(88),\n child: _buildItem(mode)),\n Text(mode.toString().split('.')[1])\n ]))\n .toList());\n }\n\n final yellowBox = Container(\n color: Colors.yellow,\n height: 30,\n width: 50,\n );\n\n final redBox = Container(\n color: Colors.red,\n height: 40,\n width: 40,\n );\n final greenBox = Container(\n color: Colors.green,\n height: 40,\n width: 20,\n );\n final blackBox = Container(\n color: Colors.black,\n height: 10,\n width: 10,\n );\n final purpleBox = Container(\n color: Colors.purple,\n height: 20,\n width: 20,\n );\n final orangeBox = Container(\n color: Colors.orange,\n height: 80,\n width: 20,\n );\n final cyanBox = Container(\n color: Colors.cyanAccent,\n height: 10,\n width: 20,\n );\n\n _buildItem(mode) => Wrap(\n alignment: mode,\n runSpacing: 10,\n spacing: 10,\n children: [\n yellowBox, redBox,\n greenBox, cyanBox,\n blackBox, purpleBox,\n orangeBox,\n ],\n );\n}"},{"id":null,"widgetId":98,"name":"Wrap的crossAxisAlignment属性","priority":3,"subtitle":" \n【crossAxisAlignment】 : 交叉轴对齐 【CrossAxisAlignment】","code":"import 'package:flutter/material.dart';\nclass CrossAxisAlignmentWrap extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: WrapCrossAlignment.values\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 160,\n height: 100,\n color: Colors.grey.withAlpha(88),\n child: _buildItem(mode)),\n Text(mode.toString().split('.')[1])\n ]))\n .toList());\n }\n\n final yellowBox = Container(\n color: Colors.yellow,\n height: 30,\n width: 50,\n );\n\n final redBox = Container(\n color: Colors.red,\n height: 40,\n width: 40,\n );\n final greenBox = Container(\n color: Colors.green,\n height: 40,\n width: 20,\n );\n final blackBox = Container(\n color: Colors.black,\n height: 10,\n width: 10,\n );\n final purpleBox = Container(\n color: Colors.purple,\n height: 20,\n width: 20,\n );\n final orangeBox = Container(\n color: Colors.orange,\n height: 80,\n width: 20,\n );\n final cyanBox = Container(\n color: Colors.cyanAccent,\n height: 10,\n width: 20,\n );\n\n _buildItem(mode) => Wrap(\n crossAxisAlignment: mode,\n runSpacing: 10,\n spacing: 10,\n children: [\n yellowBox, redBox,\n greenBox, cyanBox,\n blackBox, purpleBox,\n orangeBox,\n ],\n );\n}"},{"id":null,"widgetId":101,"name":"RichText基本使用","priority":1,"subtitle":" \n【text】 : 文字 【TextSpan】\n 其他属性与Text相同,详见之。","code":"import 'package:flutter/material.dart';\nimport '../../../../app/utils/color_utils.dart';\nclass CustomRichText extends StatelessWidget {\n final str = \" 发光强度简称光强,国际单位是(坎德拉)简写cd。\"\n \"1cd是指光源在指定方向的单位立体角内发出的光通量。\"\n \"光源辐射是均匀时,则光强为I=F/Ω,Ω为立体角,单位为球面度(sr),F为光通量,\"\n \"单位是流明,对于点光源由I=F/4π 。光亮度是表示发光面明亮程度的,\"\n \"指发光表面在指定方向的发光强度与垂直且指定方向的发光面的面积之比,\"\n \"单位是坎德拉/平方米。对于一个漫散射面,尽管各个方向的光强和光通量不同,\"\n \"但各个方向的亮度都是相等的。电视机的荧光屏就是近似于这样的漫散射面,\"\n \"所以从各个方向上观看图像,都有相同的亮度感。\";\n\n @override\n Widget build(BuildContext context) {\n return Padding(\n padding: const EdgeInsets.only(\n left: 10.0,\n right: 10,\n ),\n child: RichText(\n text: TextSpan(\n children: str\n .split(\"\")\n .map((str) => TextSpan(\n text: str,\n style: TextStyle(\n fontSize: 14, color: ColorUtils.randomColor())))\n .toList())),\n );\n }\n}\n"},{"id":null,"widgetId":97,"name":"Stack基本使用","priority":1,"subtitle":" \n【children】 : 组件列表 【List】\n【textDirection】 : 孩子排布方向 【MainAxisAlignment】\n【alignment】 : 对齐方式 【AlignmentGeometry】\n【overflow】 : 溢出模式 【Overflow】\n【fit】 : 适应模式 【StackFit】","code":"import 'package:flutter/material.dart';\nclass CustomStack extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var yellowBox = Container(\n color: Colors.yellow,\n height: 100,\n width: 100,\n );\n\n var redBox = Container(\n color: Colors.red,\n height: 90,\n width: 90,\n );\n\n var greenBox = Container(\n color: Colors.green,\n height: 80,\n width: 80,\n );\n\n var cyanBox = Container(\n color: Colors.cyanAccent,\n height: 70,\n width: 70,\n );\n\n return Container(\n width: 200,\n height: 120,\n color: Colors.grey.withAlpha(33),\n child: Stack(\n textDirection: TextDirection.rtl,\n fit: StackFit.loose,\n alignment: Alignment.topRight,\n overflow: Overflow.clip,\n children: [yellowBox, redBox, greenBox, cyanBox],\n ),\n );\n }\n}"},{"id":null,"widgetId":97,"name":"Stack和Positioned结合使用","priority":2,"subtitle":" \nPositioned组件只能用与Stack中,可以指定左上右下的距离对某个组件进行位置精确安放。","code":"import 'package:flutter/material.dart';\nclass PositionedStack extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var yellowBox = Container(\n color: Colors.yellow,\n height: 100,\n width: 100,\n );\n\n var redBox = Container(\n color: Colors.red,\n height: 90,\n width: 90,\n );\n\n var greenBox = Container(\n color: Colors.green,\n height: 80,\n width: 80,\n );\n\n var cyanBox = Container(\n color: Colors.cyanAccent,\n height: 70,\n width: 70,\n );\n\n return Container(\n width: 200,\n height: 120,\n color: Colors.grey.withAlpha(33),\n child: Stack(\n children: [yellowBox, redBox, greenBox,\n Positioned(\n child: cyanBox,\n bottom: 10,\n right: 10,\n )\n ],\n ));\n }\n}\n"},{"id":null,"widgetId":101,"name":"RichText包含其他组件","priority":2,"subtitle":" \n使用WidgetSpan来承载普通组件,作为RichText的内容","code":"import 'package:flutter/material.dart';\nclass RichTextWithWidget extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 300,\n height: 60,\n alignment: Alignment.center,\n child: Text('暂不支持Web')\n// RichText(\n// text: TextSpan(\n// text: 'hello ',\n// style: TextStyle(color: Colors.black, fontSize: 18),\n// children: [\n// WidgetSpan(\n// child: Image.asset(\n// 'assets/images/icon_head.png',\n// width: 30,\n// ),\n// alignment: PlaceholderAlignment.baseline,\n// baseline: TextBaseline.ideographic),\n// TextSpan(\n// text: ' , welcome to ',\n// style: TextStyle(color: Colors.blue, fontSize: 18),\n// ),\n// WidgetSpan(\n// child: FlutterLogo(),\n// alignment: PlaceholderAlignment.baseline,\n// baseline: TextBaseline.ideographic),\n// TextSpan(\n// text: ' .\\n',\n// ),\n// TextSpan(\n// text: 'focus me on ',\n// style: TextStyle(color: Colors.orange, fontSize: 16),\n// ),\n// TextSpan(\n// text: 'https://github.com/toly1994328',\n// style: TextStyle(\n// color: Colors.blue,\n// fontSize: 18,\n// decoration: TextDecoration.underline),\n// ),\n// TextSpan(\n// text: ' .\\n',\n// ),\n// ],\n// ),\n// ),\n );\n }\n}\n"},{"id":null,"widgetId":94,"name":"Flex垂直方向顺序","priority":4,"subtitle":" \n【verticalDirection】 : 垂直方向顺序 【VerticalDirection】","code":"import 'package:flutter/material.dart';\nclass VerticalDirectionFlex extends StatelessWidget {\n\n final redBox= Container(\n color: Colors.red,\n height: 30,\n width: 40,\n );\n\n final blueBox= Container(\n color: Colors.blue,\n height: 20,\n width: 30,\n );\n\n final greenBox= Container(\n color: Colors.green,\n height: 20,\n width: 20,\n );\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n runSpacing: 5,\n children: VerticalDirection.values\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 160,\n height: 80,\n color: Colors.grey.withAlpha(33),\n child: _buildItem(mode)),\n Text(mode.toString().split('.')[1])\n ]))\n .toList());\n }\n\n _buildItem(mode) => Flex(\n direction: Axis.vertical,\n verticalDirection: mode,\n children: [\n blueBox, redBox, greenBox\n ],\n );\n}\n"},{"id":null,"widgetId":94,"name":"Flex水平方向顺序","priority":5,"subtitle":" \n【textDirection】 : 水平方向顺序 【TextDirection】","code":"import 'package:flutter/material.dart';\nclass TextDirectionFlex extends StatelessWidget {\n\n final redBox= Container(\n color: Colors.red,\n height: 30,\n width: 40,\n );\n\n final blueBox= Container(\n color: Colors.blue,\n height: 20,\n width: 30,\n );\n\n final greenBox= Container(\n color: Colors.green,\n height: 20,\n width: 20,\n );\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n runSpacing: 5,\n children: TextDirection.values\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 160,\n height: 80,\n color: Colors.grey.withAlpha(33),\n child: _buildItem(mode)),\n Text(mode.toString().split('.')[1])\n ]))\n .toList());\n }\n\n _buildItem(mode) => Flex(\n direction: Axis.horizontal,\n textDirection: mode,\n children: [\n blueBox, redBox, greenBox\n ],\n );\n}"},{"id":null,"widgetId":94,"name":"Flex主轴对齐方式","priority":2,"subtitle":" \n【mainAxisAlignment】 : 主轴对齐 【MainAxisAlignment】","code":"import 'package:flutter/material.dart';\nclass MainAxisAlignmentFlex extends StatelessWidget {\n\n final redBox= Container(\n color: Colors.red,\n height: 30,\n width: 40,\n );\n\n final blueBox= Container(\n color: Colors.blue,\n height: 20,\n width: 30,\n );\n\n final greenBox= Container(\n color: Colors.green,\n height: 20,\n width: 20,\n );\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n runSpacing: 5,\n children: MainAxisAlignment.values\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 160,\n height: 80,\n color: Colors.grey.withAlpha(33),\n child: _buildItem(mode)),\n Text(mode.toString().split('.')[1])\n ]))\n .toList());\n }\n\n _buildItem(mode) => Flex(\n direction: Axis.horizontal,\n mainAxisAlignment: mode,\n children: [\n blueBox, redBox, greenBox\n ],\n );\n}"},{"id":null,"widgetId":94,"name":"Flex的排布方向","priority":1,"subtitle":" \n【children】 : 组件列表 【List】\n【direction】 : 方向 【Axis】","code":"import 'package:flutter/material.dart';\nclass DirectionFlex extends StatelessWidget {\n\n final redBox= Container(\n color: Colors.red,\n height: 30,\n width: 40,\n );\n\n final blueBox= Container(\n color: Colors.blue,\n height: 20,\n width: 30,\n );\n\n final greenBox= Container(\n color: Colors.green,\n height: 20,\n width: 20,\n );\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: Axis.values\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 160,\n height: 80,\n color: Colors.grey.withAlpha(33),\n child: _buildItem(mode)),\n Text(mode.toString().split('.')[1])\n ]))\n .toList());\n }\n\n _buildItem(mode) => Flex(\n direction: mode,\n children: [\n blueBox, redBox, greenBox\n ],\n );\n}"},{"id":null,"widgetId":94,"name":"Flex交叉轴对齐方式","priority":3,"subtitle":" \n【crossAxisAlignment】 : 交叉轴对齐 【CrossAxisAlignment】","code":"import 'package:flutter/material.dart';\nclass CrossAxisAlignmentFlex extends StatelessWidget {\n\n final redBox= Container(\n color: Colors.red,\n height: 30,\n width: 40,\n );\n\n final blueBox= Container(\n color: Colors.blue,\n height: 20,\n width: 30,\n );\n\n final greenBox= Container(\n color: Colors.green,\n height: 20,\n width: 20,\n );\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n runSpacing: 5,\n children: CrossAxisAlignment.values\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 160,\n height: 80,\n color: Colors.grey.withAlpha(33),\n child: _buildItem(mode)),\n Text(mode.toString().split('.')[1])\n ]))\n .toList());\n }\n\n _buildItem(mode) => Flex(\n direction: Axis.horizontal,\n crossAxisAlignment: mode,\n textBaseline: TextBaseline.alphabetic,\n children: [\n blueBox, redBox, greenBox\n ],\n );\n}"},{"id":null,"widgetId":99,"name":"Flow圆形排布","priority":1,"subtitle":" \n【children】 : 组件列表 【List】\n【delegate】 : 代理 【FlowDelegate】","code":"import 'dart:math';\nimport 'package:flutter/material.dart';\nclass CircleFlow extends StatelessWidget {\n final data = List.generate(\n 16,\n (index) => index.isEven\n ? \"assets/images/icon_head.png\"\n : \"assets/images/wy_300x200.jpg\");\n\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 300,\n height: 300,\n alignment: Alignment.center,\n child: Flow(\n delegate: _CircleFlowDelegate(),\n children: data\n .map((e) => CircleAvatar(backgroundImage: AssetImage(e)))\n .toList(),\n ),\n );\n }\n}\n\nclass _CircleFlowDelegate extends FlowDelegate {\n @override //绘制孩子的方法\n void paintChildren(FlowPaintingContext context) {\n double radius = context.size.shortestSide / 2;\n print(context.getChildSize(0));\n var count = context.childCount;\n var perRad = 2 * pi / count;\n for (int i = 0; i < count; i++) {\n var cSizeX = context.getChildSize(i).width / 2;\n var cSizeY = context.getChildSize(i).height / 2;\n\n var offsetX = (radius - cSizeX) * cos(i * perRad) + radius;\n var offsetY = (radius - cSizeY) * sin(i * perRad) + radius;\n context.paintChild(i,\n transform: Matrix4.translationValues(\n offsetX - cSizeX, offsetY - cSizeY, 0.0));\n }\n }\n\n @override\n bool shouldRepaint(FlowDelegate oldDelegate) {\n return true;\n }\n}\n"},{"id":null,"widgetId":99,"name":"Flow圆形与动画结合","priority":2,"subtitle":" \n通过动画来更改周围组件的位置实现效果","code":"import 'dart:math';\nimport 'package:flutter/material.dart';\nclass BurstFlow extends StatefulWidget {\n static final data = List.generate(\n 16,\n (index) => index.isEven\n ? \"assets/images/icon_head.png\"\n : \"assets/images/wy_300x200.jpg\");\n static final show = Container(\n width: 300,\n height: 300,\n alignment: Alignment.center,\n child: BurstFlow(\n children: data\n .map((e) => CircleAvatar(backgroundImage: AssetImage(e)))\n .toList(),\n menu: CircleAvatar(\n backgroundImage: AssetImage('assets/images/icon_head.png'),\n )));\n\n final List children;\n final Widget menu;\n\n BurstFlow({@required this.children, @required this.menu});\n\n @override\n _BurstFlowState createState() => _BurstFlowState();\n}\n\nclass _BurstFlowState extends State\n with SingleTickerProviderStateMixin {\n AnimationController _controller;\n double _rad = 0.0;\n bool _closed = true;\n\n @override\n void initState() {\n _controller = AnimationController(\n duration: Duration(milliseconds: 1000), vsync: this)\n ..addListener(() => setState(\n () => _rad = (_closed ? (_controller.value) : 1 - _controller.value)))\n ..addStatusListener((status) {\n if (status == AnimationStatus.completed) {\n _closed = !_closed;\n }\n });\n super.initState();\n }\n\n @override\n void dispose() {\n _controller.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return Flow(\n delegate: _BurstFlowDelegate(_rad),\n children: [\n ...widget.children,\n InkWell(\n onTap: () {\n _controller.reset();\n _controller.forward();\n },\n child: widget.menu)\n ],\n );\n }\n}\n\nclass _BurstFlowDelegate extends FlowDelegate {\n final double rad;\n\n _BurstFlowDelegate(this.rad);\n\n @override //绘制孩子的方法\n void paintChildren(FlowPaintingContext context) {\n double radius = context.size.shortestSide / 2;\n var count = context.childCount - 1;\n var perRad = 2 * pi / count;\n for (int i = 0; i < count; i++) {\n print(i);\n var cSizeX = context.getChildSize(i).width / 2;\n var cSizeY = context.getChildSize(i).height / 2;\n var offsetX = rad * (radius - cSizeX) * cos(i * perRad) + radius;\n var offsetY = rad * (radius - cSizeY) * sin(i * perRad) + radius;\n context.paintChild(i,\n transform: Matrix4.translationValues(\n offsetX - cSizeX, offsetY - cSizeY, 0.0));\n }\n context.paintChild(context.childCount - 1,\n transform: Matrix4.translationValues(\n radius - context.getChildSize(context.childCount - 1).width / 2,\n radius - context.getChildSize(context.childCount - 1).height / 2,\n 0.0));\n }\n\n @override\n bool shouldRepaint(FlowDelegate oldDelegate) {\n return true;\n }\n}\n"},{"id":null,"widgetId":179,"name":"ListWheelScrollView基本使用","priority":1,"subtitle":" \n【children】 : 子组件列表 【List】\n【perspective】 : 透视度 【double】\n【itemExtent】 : item高 【EdgeInsets】\n【onSelectedItemChanged】 : 选中回调 【ValueChanged 】","code":"import 'package:flutter/material.dart';\nclass CustomListWheelScrollView extends StatefulWidget {\n @override\n _CustomListWheelScrollViewState createState() => _CustomListWheelScrollViewState();\n}\n\nclass _CustomListWheelScrollViewState extends State {\n var data = [\n Colors.orange[50],\n Colors.orange[100],\n Colors.orange[200],\n Colors.orange[300],\n Colors.orange[400],\n Colors.orange[500],\n Colors.orange[600],\n Colors.orange[700],\n Colors.orange[800],\n Colors.orange[900],\n ];\n\n Color _color = Colors.blue;\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildCircle(),\n Container(\n height: 150,\n width: 300,\n child: ListWheelScrollView(\n perspective: 0.006,\n itemExtent: 50,\n onSelectedItemChanged: (index){\n print('onSelectedItemChanged:$index');\n setState(() => _color=data[index]);\n },\n children: data.map((color) => _buildItem(color)).toList(),\n ),\n ),\n ],\n );\n }\n\n Widget _buildCircle() => Container(\n margin: EdgeInsets.only(bottom: 5),\n width: 30,\n height: 30,\n decoration: BoxDecoration(\n color: _color,\n shape: BoxShape.circle\n ),\n );\n\n Widget _buildItem(Color color) {\n return Container(\n key: ValueKey(color) ,\n alignment: Alignment.center,\n height: 50,\n color: color,\n child: Text(\n colorString(color),\n style: TextStyle(color: Colors.white, shadows: [\n Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2)\n ]),\n ),\n );\n }\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":104,"name":"DragTarget基本使用","priority":1,"subtitle":" \n【builder】 : 组件构造器 【DragTargetBuilder】\n【onWillAccept】 : 拖入时 【Function(T)】\n【onAccept】 : 拖拽成功 【Function(T)】\n【onLeave】 : 拖入再脱出 【Function(T)】","code":"import 'package:flutter/material.dart';\nclass CustomDragTarget extends StatefulWidget {\n @override\n _CustomDragTargetState createState() => _CustomDragTargetState();\n}\n\nclass _CustomDragTargetState extends State {\n Color _color = Colors.grey;\n String _info = 'DragTarget';\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Column(\n children: [\n Wrap(\n children: _buildColors(),\n spacing: 10,\n ),\n SizedBox(height: 20,),\n _buildDragTarget()\n ],\n ),\n );\n }\n\n List _buildColors() {\n var colors = [\n Colors.red,\n Colors.yellow,\n Colors.blue,\n Colors.green,\n Colors.orange,\n Colors.purple,\n Colors.cyanAccent\n ];\n return colors\n .map(\n (e) => Draggable(\n child: Container(\n width: 30,\n height: 30,\n alignment: Alignment.center,\n child: Text(\n colors.indexOf(e).toString(),\n style: TextStyle(\n color: Colors.white, fontWeight: FontWeight.bold),\n ),\n decoration: BoxDecoration(color: e, shape: BoxShape.circle),\n ),\n data: e,\n feedback: Container(\n width: 25,\n height: 25,\n decoration: BoxDecoration(color: e, shape: BoxShape.circle),\n )),\n )\n .toList();\n }\n\n Widget _buildDragTarget() {\n return DragTarget(\n onLeave: (data) => setState(() => _info='onLeave'),\n onAccept: (data) => setState(() {\n _info='onAccept';\n _color = data;\n }),\n onWillAccept: (data) {\n setState(() {\n _info='onWillAccept';\n });\n print(\"onWillAccept: data = $data \");\n return data != null;\n },\n builder: (context, candidateData, rejectedData) => Container(\n width: 150.0,\n height: 50.0,\n color: _color,\n child: Center(\n child: Text(\n _info,\n style: TextStyle(color: Colors.white),\n ),\n )));\n }\n}"},{"id":null,"widgetId":173,"name":"StreamBuilder基本使用","priority":1,"subtitle":" \n【stream】 : 子组件 【Stream】\n【initialData】 : 初始数据 【T】\n【builder】 : 点击事件 【AsyncWidgetBuilder】","code":"import 'dart:async';\nimport 'package:flutter/material.dart';\nclass CustomStreamBuilder extends StatefulWidget {\n @override\n _CustomStreamBuilderState createState() => _CustomStreamBuilderState();\n}\n\nclass _CustomStreamBuilderState extends State {\n CountGenerator _generator = CountGenerator()..increment();\n\n @override\n void dispose() {\n _generator.dispose(); //关闭控制器\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Row(\n mainAxisSize: MainAxisSize.min,\n children: [\n FlatButton(\n color: Colors.blue,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n child: Icon(\n Icons.add,\n color: Colors.white,\n ),\n onPressed: () async {\n await _generator.increment();\n },\n ),\n _buildStreamBuilder(),\n FlatButton(\n color: Colors.blue,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n child: Icon(\n Icons.remove,\n color: Colors.white,\n ),\n onPressed: () async {\n await _generator.minus();\n },\n ),\n ],\n ),\n );\n }\n\n Widget _buildStreamBuilder() => StreamBuilder(\n stream: _generator.state,\n builder: (BuildContext context, AsyncSnapshot snap) {\n print(snap);\n if (snap.connectionState == ConnectionState.done) {\n return Text('Done');\n }\n if (snap.connectionState == ConnectionState.active) {\n return Text(\n snap.data.toString(),\n style: Theme.of(context).textTheme.display1,\n );\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\nclass CountGenerator {\n int _count = 0; //计数器数据\n final StreamController _controller = StreamController(); //控制器\n\n Stream get state => _controller.stream; //获取状态流\n int get count => _count; //获取计数器数据\n\n void dispose() {//关闭控制器\n _controller.close();\n }\n\n Future increment() async {//增加记数方法\n _controller.add(++_count);\n }\n\n Future minus() async {//增加记数方法\n _controller.add(--_count);\n }\n}\n"},{"id":null,"widgetId":57,"name":"AppBar基本使用","priority":1,"subtitle":" \n【leading】 : 左侧组件 【Widget】\n【title】 : 中间组件 【Widget】\n【actions】 : 右侧组件 【List】\n【elevation】 : 影深 【double】\n【shape】 : 形状 【ShapeBorder】\n【backgroundColor】 : 影深 【背景色】\n【centerTitle】 : 中间是否居中 【bool】","code":"import 'package:flutter/material.dart';\nimport '../PopupMenuButton/node1_base.dart';\nclass CustomAppBar extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return AppBar(\n title: Text('风雅六社'),\n leading: BackButton(),\n backgroundColor: Colors.amber[500],\n elevation: 2,\n centerTitle: true,\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 actions: [\n IconButton(\n icon: Icon(Icons.star),\n tooltip: 'like',\n onPressed: () {\n // do nothing\n }),\n CustomPopupMenuButton()\n ],\n );\n }\n}\n"},{"id":null,"widgetId":57,"name":"AppBar与TabBar、TabBarView联用","priority":2,"subtitle":" \n【bottom】 : 底部组件 【PreferredSizeWidget】","code":"import 'package:flutter/material.dart';\nimport '../PopupMenuButton/node1_base.dart';\nclass TabAppBar extends StatefulWidget {\n @override\n _TabAppBarState createState() => _TabAppBarState();\n}\n\nclass _TabAppBarState extends State\n with SingleTickerProviderStateMixin {\n final tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼'];\n TabController _tabController;\n\n @override\n void initState() {\n super.initState();\n _tabController = TabController(vsync: this, length: tabs.length);\n }\n\n @override\n void dispose() {\n _tabController.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Container(\n height: 180,\n decoration: BoxDecoration(\n image: DecorationImage(\n image: AssetImage(\n \"assets/images/sabar.jpg\",\n ),\n fit: BoxFit.cover)),\n child: _buildAppBar(),\n ),\n Container(\n height: 150, color: Color(0xff916BF0), child: _buildTableBarView())\n ],\n );\n }\n\n Widget _buildAppBar() => AppBar(\n title: Text('风雅六社'),\n elevation: 1,\n leading: BackButton(),\n backgroundColor: Colors.amber[500].withAlpha(33),\n centerTitle: true,\n shape: RoundedRectangleBorder(\n borderRadius: BorderRadius.only(\n topLeft: Radius.circular(20),\n topRight: Radius.circular(20),\n )),\n actions: [\n IconButton(\n icon: Icon(Icons.star),\n tooltip: 'like',\n onPressed: () {\n // do nothing\n }),\n CustomPopupMenuButton()\n ],\n bottom: TabBar(\n isScrollable: true,\n controller: _tabController,\n indicatorColor: Colors.orangeAccent,\n tabs: tabs.map((e) => Tab(text: e)).toList(),\n ),\n );\n\n Widget _buildTableBarView() => TabBarView(\n controller: _tabController,\n children: tabs\n .map((e) => Center(\n child: Text(\n e,\n style: TextStyle(color: Colors.white, fontSize: 20),\n )))\n .toList());\n}\n"},{"id":null,"widgetId":152,"name":"Ink基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【padding】 : 内边距 【EdgeInsetsGeometry】\n【decoration】 : 装饰 【Decoration】\n【width】 : 宽 【double】\n【height】 : 高 【double】\n【color】 : 颜色 【Color】","code":"import 'package:flutter/material.dart';\nclass CustomInk extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Material(\n color: Colors.orangeAccent,\n child: Padding(\n padding: const EdgeInsets.all(8.0),\n child: Center(\n child: Ink(\n padding: EdgeInsets.all(10),\n decoration: BoxDecoration(\n color: Colors.yellow,\n borderRadius: BorderRadius.all(Radius.circular(20))\n ),\n\n width: 200.0,\n height: 100.0,\n child: InkWell(\n onTap: () {\n },\n child: Center(\n child: Text('Hello'),\n )),\n ),\n ),\n ),\n );\n }\n}\n\n"},{"id":null,"widgetId":152,"name":"Ink.image图片水波纹","priority":2,"subtitle":" \n 其中属性与Image组件一致,详见Image组件","code":"import 'package:flutter/material.dart';\nclass InkImage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Material(\n color: Colors.grey[800],\n child: Center(\n child: Ink.image(\n image: AssetImage('assets/images/sabar.jpg'),\n fit: BoxFit.cover,\n width: 300.0,\n height: 200.0,\n child: InkWell(\n onTap: () {},\n child: Align(\n alignment: Alignment.topLeft,\n child: Padding(\n padding: const EdgeInsets.all(10.0),\n child: Text('Chaos',\n style: TextStyle(\n fontWeight: FontWeight.w900, color: Colors.black)),\n ),\n )),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":198,"name":"Form基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【onChanged】 : 表单变化回调 【VoidCallback】\n【onWillPop】 : 返回回调 【WillPopCallback】","code":"import 'package:flutter/material.dart';\nclass CustomForm extends StatefulWidget {\n @override\n _CustomFormState createState() => _CustomFormState();\n}\n\nclass _CustomFormState extends State {\n GlobalKey _formKey = GlobalKey();\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Form(\n onWillPop: () => _willPop(context),\n key: _formKey,\n onChanged: () {\n print('Form---onChanged');\n },\n child:\n Stack(\n alignment: Alignment.centerRight,\n children: [\n Container(\n width: 350,\n child: UnconstrainedBox(\n child: Container(\n width: 200,\n height: 70,\n child: TextFormField(\n style: TextStyle(textBaseline: TextBaseline.alphabetic),\n decoration: InputDecoration(\n border: OutlineInputBorder(),\n labelText: 'username',\n ),\n validator: _validateUsername,\n ),\n ),\n ),\n ),\n Positioned(\n top: 0, right: 0, child: _buildSubmitButton(context)),\n ],\n ),\n ),\n );\n }\n\n String _validateUsername(value) {\n if (value.isEmpty) {\n return '用户名不能为空';\n }\n return null;\n }\n\n RaisedButton _buildSubmitButton(BuildContext context) {\n return RaisedButton(\n color: Colors.blue,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onPressed: _onSubmit,\n child: Icon(\n Icons.check,\n color: Colors.white,\n ),\n );\n }\n\n _onSubmit(){\n if (_formKey.currentState.validate()) {\n FocusScope.of(context).requestFocus(FocusNode());\n Navigator.of(context).pop();\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"},{"id":null,"widgetId":50,"name":"Tooltip基本使用","priority":1,"subtitle":" \n【preferBelow】 : 是否首选下方 【bool】\n【padding】 : 内边距 【EdgeInsetsGeometry】\n【margin】 : 外边距 【EdgeInsetsGeometry】\n【message】 : 消息内容 【String】\n【showDuration】 : 展示时间 【Duration】\n【waitDuration】 : 悬浮出现时间 【Duration】\n【child】 : 孩子 【Widget】","code":"import 'package:flutter/material.dart';\nclass CustomTooltip extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Tooltip(\n preferBelow: true,\n padding: EdgeInsets.all(5),\n margin: EdgeInsets.all(5),\n message: \"天王盖地虎\",\n showDuration: Duration(seconds: 3),\n waitDuration: Duration(milliseconds: 200),\n child: Icon(Icons.info_outline),\n );\n }\n}\n"},{"id":null,"widgetId":50,"name":"Tooltip的装饰","priority":2,"subtitle":" \n【decoration】 : 装饰对象 【Decoration】\n【textStyle】 : 文字样式 【double】","code":"import 'package:flutter/material.dart';\nclass DecorationTooltip extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Tooltip(\n preferBelow: false,\n padding: EdgeInsets.all(5),\n margin: EdgeInsets.all(2),\n message: \"宝塔镇河妖\",\n textStyle: TextStyle(\n color: Colors.red,\n shadows: [Shadow(color: Colors.white,\n offset: Offset(1, 1))]),\n decoration: BoxDecoration(boxShadow: [\n BoxShadow(\n color: Colors.orangeAccent,\n offset: Offset(1, 1), blurRadius: 8)\n ]),\n child: Icon(Icons.info_outline));\n }\n}\n"},{"id":null,"widgetId":175,"name":"RawMaterialButton高亮和形状","priority":2,"subtitle":" \n【highlightElevation】 : 高亮影深 【double】\n【shape】 : 形状 【ShapeBorder】","code":"import 'package:flutter/material.dart';\nclass ShapeRawMaterialButton extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Wrap(\n spacing: 20,\n children: [\n RawMaterialButton(\n elevation: 2,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n fillColor: Colors.green,\n splashColor: Colors.orange,\n textStyle: TextStyle(color: Colors.white),\n onLongPress: ()=>print('onLongPress'),\n child: Icon(Icons.remove),\n onPressed: ()=>print('onPressed'),\n ),\n RawMaterialButton(\n shape:RoundedRectangleBorder(\n borderRadius: BorderRadius.all(Radius.circular(15))),\n elevation: 0,\n highlightElevation: 0,\n fillColor: Colors.blue,\n splashColor: Colors.orange,\n textStyle: TextStyle(color: Colors.white),\n onLongPress: ()=>print('onLongPress'),\n child: Text('Push'),\n onPressed: ()=>print('onPressed'),\n ),\n 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 onLongPress: ()=>print('onLongPress'),\n child: Icon(Icons.add),\n onPressed: ()=>print('onPressed'),\n ),\n\n ],\n ),\n );\n }\n}"},{"id":null,"widgetId":49,"name":"RefreshIndicator基本使用","priority":1,"subtitle":" \n【child】 : 孩子(可滑动) 【Widget】\n【displacement】 : 指示器悬浮高度 【double】\n【color】 : 指示器颜色 【Color】,","code":"import 'package:flutter/material.dart';\nclass CustomRefreshIndicator extends StatefulWidget {\n @override\n _CustomRefreshIndicatorState createState() => _CustomRefreshIndicatorState();\n}\n\nclass _CustomRefreshIndicatorState extends State {\n int _count = 0;\n\n @override\n Widget build(BuildContext context) {\n\n return Container(\n height: 200,\n width: 200,\n child: RefreshIndicator(\n onRefresh: _increment,\n displacement: 20,\n color: Colors.orange,\n backgroundColor: Colors.white,\n child: SingleChildScrollView(\n child: Container(\n alignment: Alignment.center,\n width: 200,\n height: 300,\n color: Colors.blue,\n child: Text('$_count',style: TextStyle(color: Colors.white,fontSize: 40)),\n ),\n ),\n ),\n );\n }\n\n Future _increment() async {\n await Future.delayed(Duration(seconds: 2));\n setState(() {\n _count++;\n });\n }\n}\n"},{"id":null,"widgetId":175,"name":"RawMaterialButton基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【elevation】 : 影深 【double】\n【fillColor】 : 填充色 【Color】\n【splashColor】 : 水波纹色 【Color】\n【textStyle】 : 文字样式 【TextStyle】\n【onLongPress】 : 长按事件 【Function()】\n【onPressed】 : 点击事件 【Function()】","code":"import 'package:flutter/material.dart';\nclass CustomRawMaterialButton extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Wrap(\n spacing: 20,\n children: [\n RawMaterialButton(\n elevation: 2,\n fillColor: Colors.green,\n splashColor: Colors.orange,\n textStyle: TextStyle(color: Colors.white),\n onLongPress: ()=>print('onLongPress'),\n child: Icon(Icons.remove),\n onPressed: ()=>print('onPressed'),\n ),\n RawMaterialButton(\n elevation: 2,\n fillColor: Colors.blue,\n splashColor: Colors.orange,\n textStyle: TextStyle(color: Colors.white),\n onLongPress: ()=>print('onLongPress'),\n child: Text('Push'),\n onPressed: ()=>print('onPressed'),\n ),\n RawMaterialButton(\n elevation: 2,\n fillColor: Colors.red,\n splashColor: Colors.orange,\n textStyle: TextStyle(color: Colors.white),\n onLongPress: ()=>print('onLongPress'),\n child: Icon(Icons.add),\n onPressed: ()=>print('onPressed'),\n ),\n\n ],\n ),\n );\n }\n}"},{"id":null,"widgetId":112,"name":"SlideTransition基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【textDirection】 : x轴方向 【TextDirection】\n【position】 : 动画 【Animation】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomSlideTransition extends StatefulWidget {\n @override\n _CustomSlideTransitionState createState() => _CustomSlideTransitionState();\n}\n\nclass _CustomSlideTransitionState extends State\n with SingleTickerProviderStateMixin {\n AnimationController _ctrl;\n\n @override\n void initState() {\n _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 1));\n _ctrl.forward();\n super.initState();\n }\n\n @override\n void dispose() {\n _ctrl.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onTap: () {\n setState(() {\n _ctrl.reset();\n _ctrl.forward();\n });\n },\n child: Container(\n width: MediaQuery.of(context).size.width,\n color: Colors.grey.withAlpha(33),\n height: 100,\n child: SlideTransition(\n textDirection: TextDirection.ltr,\n position: Tween(\n begin: Offset.zero,\n end: Offset(0.2, 0.2),\n ).animate(_ctrl),\n child: Container(\n child: Icon(Icons.android, color: Colors.green, size: 60)),\n ),\n ));\n }\n}\n"},{"id":null,"widgetId":45,"name":"Radio基本使用","priority":1,"subtitle":" \n【value】 : 选钮值 【T】\n【groupValue】 : 当前匹配值 【T】\n【activeColor】 : 激活颜色 【Color】\n【onChanged】 : 改变时回调 【Function(T)】","code":"import 'package:flutter/material.dart';\nclass CustomRadio extends StatefulWidget {\n @override\n _CustomRadioState createState() => _CustomRadioState();\n}\n\nclass _CustomRadioState extends State {\n var data = [1, 2, 3, 4, 5];\n double _value = 1;\n\n @override\n Widget build(BuildContext context) {\n return Row(\n mainAxisSize: MainAxisSize.min,\n children: data\n .map((e) => Radio(\n activeColor: Colors.orangeAccent,\n value: e,\n groupValue: _value,\n onChanged: (v) => setState(() => _value = v)))\n .toList(),\n );\n }\n}\n"},{"id":null,"widgetId":165,"name":"PageView基本使用","priority":1,"subtitle":" \n【children】 : 子组件列表 【List】\n【onPageChanged】 : 点击事件 【ValueChanged】","code":"import 'package:flutter/material.dart';\nclass CustomPageView extends StatelessWidget {\n final data = [\n Colors.green[50],\n Colors.green[100],\n Colors.green[200],\n Colors.green[300],\n Colors.green[400],\n Colors.green[500],\n Colors.green[600],\n Colors.green[700],\n Colors.green[800],\n Colors.green[900],\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 150,\n child: PageView(\n onPageChanged: (position){\n print(position);\n },\n children: data\n .map((color) => Container(\n alignment: Alignment.center,\n width: 90,\n color: color,\n child: Text(\n colorString(color),\n style: TextStyle(color: Colors.white,\n fontSize:24,shadows: [\n Shadow(\n color: Colors.black,\n offset: Offset(.5, .5),\n blurRadius: 2)\n ]),\n ),\n ))\n .toList(),\n ),\n );\n }\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":165,"name":"PageView控制器简单实用","priority":3,"subtitle":" \n【controller】 : 页面控制器 【PageController】","code":"import 'package:flutter/material.dart';\nclass CtrlPageView extends StatefulWidget {\n @override\n _CtrlPageViewState createState() => _CtrlPageViewState();\n}\n\nclass _CtrlPageViewState extends State {\n final data = [\n Colors.orange[50],\n Colors.orange[100],\n Colors.orange[200],\n Colors.orange[300],\n Colors.orange[400],\n Colors.orange[500],\n Colors.orange[600],\n Colors.orange[700],\n Colors.orange[800],\n Colors.orange[900],\n ];\n\n PageController _controller;\n\n @override\n void dispose() {\n _controller.dispose();\n super.dispose();\n }\n\n @override\n void initState() {\n super.initState();\n _controller=PageController(\n viewportFraction: 0.8,\n initialPage: (data.length/2).round()\n );\n\n }\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 150,\n child: PageView(\n controller: _controller,\n onPageChanged: (position) {\n print(position);\n },\n children: data\n .map((color) =>\n Container(\n alignment: Alignment.center,\n width: 90,\n color: color,\n child: Text(\n colorString(color),\n style: TextStyle(color: Colors.white,\n fontSize: 24, 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 String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":165,"name":"PageView滑动方向","priority":2,"subtitle":" \n【scrollDirection】 : 滑动方向 【Axis】\n【reverse】 : 是否反向 【bool】","code":"import 'package:flutter/material.dart';\nclass DirectionPageView extends StatelessWidget {\n final data = [\n Colors.orange[50],\n Colors.orange[100],\n Colors.orange[200],\n Colors.orange[300],\n Colors.orange[400],\n Colors.orange[500],\n Colors.orange[600],\n Colors.orange[700],\n Colors.orange[800],\n Colors.orange[900],\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 150,\n child: PageView(\n scrollDirection: Axis.vertical,\n reverse: true,\n onPageChanged: (position) {\n print(position);\n },\n children: data\n .map((color) =>\n Container(\n alignment: Alignment.center,\n width: 90,\n color: color,\n child: Text(\n colorString(color),\n style: TextStyle(color: Colors.white,\n fontSize: 24, 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 String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":60,"name":"BottomNavigationBar基本使用","priority":1,"subtitle":" \n【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)】","code":"import 'package:flutter/material.dart';\nclass CustomBottomNavigationBar extends StatefulWidget {\n @override\n _CustomBottomNavigationBarState createState() =>\n _CustomBottomNavigationBarState();\n}\n\nclass _CustomBottomNavigationBarState extends State {\n var _position = 0;\n BottomNavigationBarType _type = BottomNavigationBarType.shifting;\n final iconsMap = {\n //底栏图标\n \"图鉴\": Icons.home, \"动态\": Icons.toys,\n \"喜欢\": Icons.favorite, \"手册\": Icons.class_,\n \"我的\": Icons.account_circle,\n };\n final _colors = [\n Colors.red,\n Colors.yellow,\n Colors.blue,\n Colors.green,\n Colors.purple,\n ];\n\n @override\n Widget build(BuildContext context) {\n return Column(\n crossAxisAlignment: CrossAxisAlignment.end,\n children: [\n _buildOp(),\n _buildBottomNavigationBar(),\n ],\n );\n }\n\n bool get isShifting => _type == BottomNavigationBarType.shifting;\n\n BottomNavigationBar _buildBottomNavigationBar() {\n return BottomNavigationBar(\n onTap: (position) => setState(() => _position = position),\n currentIndex: _position,\n elevation: 1,\n type: _type,\n fixedColor: isShifting ? Colors.white : _colors[_position],\n backgroundColor: Colors.white,\n iconSize: 25,\n selectedLabelStyle: TextStyle(fontWeight: FontWeight.bold),\n showUnselectedLabels: false,\n showSelectedLabels: true,\n items: iconsMap.keys\n .map((key) => BottomNavigationBarItem(\n title: Text(\n key,\n ),\n icon: Icon(iconsMap[key]),\n backgroundColor: _colors[_position]))\n .toList(),\n );\n }\n\n Widget _buildOp() {\n return Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [\n Text(\n _type.toString(),\n style: TextStyle(fontWeight: FontWeight.bold, color: Colors.blue),\n ),\n Switch(\n value: _type == BottomNavigationBarType.shifting,\n onChanged: (b) {\n setState(() => _type = b\n ? BottomNavigationBarType.shifting\n : BottomNavigationBarType.fixed);\n }),\n ],\n );\n }\n}\n"},{"id":null,"widgetId":60,"name":"可结合PageView进行切页","priority":2,"subtitle":" \n在onTap时进行使用控制器进行切页","code":"import 'package:flutter/material.dart';\nclass BottomNavigationBarWithPageView extends StatefulWidget {\n @override\n _BottomNavigationBarWithPageViewState createState() =>\n _BottomNavigationBarWithPageViewState();\n}\n\nclass _BottomNavigationBarWithPageViewState\n extends State {\n var _position = 0;\n final iconsMap = {\n //底栏图标\n \"图鉴\": Icons.home, \"动态\": Icons.toys,\n \"喜欢\": Icons.favorite, \"手册\": Icons.class_,\n \"我的\": Icons.account_circle,\n };\n final _colors = [\n Colors.red,\n Colors.yellow,\n Colors.blue,\n Colors.green,\n Colors.purple,\n ];\n PageController _controller; //页面控制器,初始0\n\n @override\n void initState() {\n _controller = PageController(\n initialPage: _position,\n );\n super.initState();\n }\n\n @override\n void dispose() {\n _controller.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Column(\n children: [\n Container(\n color: Colors.orange.withAlpha(88),\n width: MediaQuery.of(context).size.width,\n height: 150,\n child: PageView(\n controller: _controller,\n children: iconsMap.keys\n .map((e) => Center(\n child: Text(\n e,\n style: TextStyle(color: Colors.white, fontSize: 20),\n ),\n ))\n .toList(),\n ),\n ),\n _buildBottomNavigationBar()\n ],\n ),\n );\n }\n\n BottomNavigationBar _buildBottomNavigationBar() {\n return BottomNavigationBar(\n onTap: (position) {\n _controller.jumpToPage(position);\n setState(() => _position = position);\n },\n currentIndex: _position,\n elevation: 1,\n type: BottomNavigationBarType.shifting,\n fixedColor: Colors.white,\n iconSize: 25,\n selectedLabelStyle: TextStyle(fontWeight: FontWeight.bold),\n showUnselectedLabels: false,\n showSelectedLabels: true,\n items: iconsMap.keys\n .map((key) => BottomNavigationBarItem(\n title: Text(\n key,\n ),\n icon: Icon(iconsMap[key]),\n backgroundColor: _colors[_position]))\n .toList(),\n );\n }\n}\n"},{"id":null,"widgetId":59,"name":"TabBarView需要与TabBar联用","priority":1,"subtitle":" \n【controller】 : 控制器 【TabController】\n【children】 : 孩子们 【指示器颜色】\n【physics】 : 表现 【ScrollPhysics】","code":"import 'package:flutter/material.dart';\nclass CustomTabBarView extends StatefulWidget {\n @override\n _CustomTabBarViewState createState() => _CustomTabBarViewState();\n}\n\nclass _CustomTabBarViewState extends State with SingleTickerProviderStateMixin {\n final tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼'];\n TabController _tabController;\n\n @override\n void initState() {\n super.initState();\n _tabController = TabController(vsync: this, length: tabs.length);\n }\n\n @override\n void dispose() {\n _tabController.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Column(\n children: [\n _buildTabBar(),\n Container(\n color: Colors.purple,\n width: MediaQuery.of(context).size.width,\n height: 200,\n child: _buildTableBarView())\n ],\n ),\n );\n }\n\n Widget _buildTabBar() => TabBar(\n onTap: (tab) => print(tab),\n labelStyle: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),\n unselectedLabelStyle: TextStyle(fontSize: 16),\n isScrollable: true,\n controller: _tabController,\n labelColor: Colors.blue,\n indicatorWeight: 3,\n indicatorPadding: EdgeInsets.symmetric(horizontal: 10),\n unselectedLabelColor: Colors.grey,\n indicatorColor: Colors.orangeAccent,\n tabs: tabs.map((e) => Tab(text: e)).toList(),\n );\n\n Widget _buildTableBarView() => TabBarView(\n controller: _tabController,\n children: tabs.map((e) => Center(\n child: Text(e, style: TextStyle(color: Colors.white, fontSize: 20),\n ))).toList());\n}\n"},{"id":null,"widgetId":144,"name":"CupertinoContextMenuAction基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【isDefaultAction】 : 是否默认选中 【bool】\n【trailingIcon】 : 尾部 【bool】\n【onPressed】 : 点击事件 【Function()】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoContextMenuAction extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Container(\n width: 200,\n margin: EdgeInsets.all(5),\n child: CupertinoContextMenuAction(\n trailingIcon: CupertinoIcons.settings,\n isDefaultAction: true,\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage'),\n child: Text('张风捷特烈')),\n ),\n Container(\n width: 200,\n margin: EdgeInsets.all(5),\n child: CupertinoContextMenuAction(\n trailingIcon: CupertinoIcons.home,\n isDefaultAction: false,\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage'),\n child: Text('百里·巫缨')),\n ),\n ],\n );\n }\n}\n"},{"id":null,"widgetId":91,"name":"ScaleTransition基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【scale】 : 动画 【Animation】","code":"import 'package:flutter/material.dart';\nclass CustomScaleTransition extends StatefulWidget {\n @override\n _CustomScaleTransitionState createState() => _CustomScaleTransitionState();\n}\n\nclass _CustomScaleTransitionState extends State\n with SingleTickerProviderStateMixin {\n AnimationController _ctrl;\n\n @override\n void initState() {\n _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 2));\n _ctrl.forward();\n super.initState();\n }\n\n @override\n void dispose() {\n _ctrl.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onTap: () {\n setState(() {\n _ctrl.reset();\n _ctrl.forward();\n });\n },\n child: Container(\n color: Colors.grey.withAlpha(22),\n width: 100,\n height: 100,\n child: ScaleTransition(\n scale: CurvedAnimation(parent: _ctrl, curve: Curves.linear),\n child: Icon(Icons.android, color: Colors.green, size: 60),\n ),\n ));\n }\n}\n"},{"id":null,"widgetId":53,"name":"SelectableText对齐属性","priority":2,"subtitle":" \n【textAlign】 : 对齐方式*6 【textAlign】\n【textDirection】 : 文字方向*2 【TextDirection】","code":"import 'package:flutter/material.dart';\nclass AlignSelectableText extends StatefulWidget {\n @override\n _AlignSelectableTextState createState() => _AlignSelectableTextState();\n}\n\nclass _AlignSelectableTextState extends State {\n final text =\n \"The [SelectableText] widget displays a string of text with a single style.\"\n \"The string might break across multiple lines or might all be displayed on\"\n \"the same line depending on the layout constraints.\";\n var _textAlign = TextAlign.left;\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildSelector(),\n SelectableText(\n text,\n style: TextStyle(fontSize: 18, color: Colors.red),\n cursorColor: Colors.green,\n cursorRadius: Radius.circular(3),\n cursorWidth: 5,\n showCursor: true,\n textAlign: _textAlign,\n textDirection: TextDirection.ltr,\n\n autofocus: false,\n ),\n ],\n );\n }\n\n Widget _buildSelector() {\n return Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [\n Text(\n \"textAlign属性选择:\",\n style: TextStyle(\n fontSize: 16, color: Colors.blue, fontWeight: FontWeight.bold),\n ),\n DropdownButton(\n underline: Container(),\n value: _textAlign,\n items: TextAlign.values\n .map((e) => DropdownMenuItem(\n value: e,\n child: Text(e.toString()),\n ))\n .toList(),\n onChanged: (e) {\n setState(() {\n _textAlign = e;\n });\n }),\n ],\n );\n }\n}\n"},{"id":null,"widgetId":53,"name":"SelectableText基本使用","priority":1,"subtitle":" \n【入参】 : 显示文字 【String】\n【style】 : 文字样式 【TextStyle】\n【cursorRadius】 : 光标半径 【Radius】\n【cursorColor】 : 光标颜色 【Color】\n【cursorWidth】 : 光标宽度 【double】\n【showCursor】 : 是否显示光标 【bool】\n【autofocus】 : 自动聚焦 【bool】","code":"import 'package:flutter/material.dart';\nclass CustomSelectableText extends StatelessWidget {\n final text = \" 始臣之解牛之时,所见无非牛者。三年之后,未尝见全牛也。方今之时,\"\n \"臣以神遇而不以目视,官知止而神欲行。依乎天理,批大郤,导大窾,因其固然,\"\n \"技经肯綮之未尝,而况大軱乎!良庖岁更刀,割也;族庖月更刀,折也。\"\n \"今臣之刀十九年矣,所解数千牛矣,而刀刃若新发于硎。彼节者有间,而刀刃者无厚;\"\n \"以无厚入有间,恢恢乎其于游刃必有余地矣,是以十九年而刀刃若新发于硎。\"\n \"虽然,每至于族,吾见其难为,怵然为戒,视为止,行为迟。动刀甚微,謋然已解,如土委地。\"\n \"提刀而立,为之四顾,为之踌躇满志,善刀而藏之.\";\n\n @override\n Widget build(BuildContext context) {\n return SelectableText(\n text,\n style: TextStyle(fontSize: 18, color: Colors.orange),\n cursorColor: Colors.green,\n cursorRadius: Radius.circular(3),\n cursorWidth: 5,\n showCursor: true,\n autofocus: false,\n );\n }\n}\n"},{"id":null,"widgetId":137,"name":"CupertinoDatePicker基本使用","priority":1,"subtitle":" \n【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】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoDatePicker extends StatefulWidget {\n @override\n _CustomCupertinoDatePickerState createState() =>\n _CustomCupertinoDatePickerState();\n}\n\nclass _CustomCupertinoDatePickerState extends State {\n DateTime _date = DateTime.now();\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Text(\n '当前日期:${_date.toIso8601String()}',\n style: TextStyle(color: Colors.grey, fontSize: 16),\n ),\n _buildInfoTitle('CupertinoDatePickerMode.dateAndTime'),\n buildPicker(CupertinoDatePickerMode.dateAndTime),\n _buildInfoTitle('CupertinoDatePickerMode.date'),\n buildPicker(CupertinoDatePickerMode.date),\n _buildInfoTitle('CupertinoDatePickerMode.time'),\n buildPicker(CupertinoDatePickerMode.time),\n ],\n );\n }\n\n Container buildPicker(CupertinoDatePickerMode mode) {\n return Container(\n margin: EdgeInsets.all(10),\n height: 150,\n child: CupertinoDatePicker(\n mode: mode,\n initialDateTime: DateTime.now(),\n// maximumDate: DateTime(2018,8,8),\n// minimumDate: DateTime(2030,8,8),\n minimumYear: 2018,\n maximumYear: 2030,\n use24hFormat: false,\n minuteInterval: 1,\n backgroundColor: CupertinoColors.white,\n onDateTimeChanged: (date) {\n print(date);\n setState(() => _date = date);\n },\n ),\n );\n }\n\n Widget _buildInfoTitle(info){\n return Padding(\n padding: const EdgeInsets.only(left: 20,top: 20,bottom: 5),\n child: Text(\n info,\n style: TextStyle(color: Colors.blue, fontSize: 16,fontWeight: FontWeight.bold),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":150,"name":"InkWell其他属性","priority":2,"subtitle":" \n【child】 : 子组件 【Widget】\n【onHighlightChanged】 : 高亮变化回调 【Function(bool)】\n【highlightColor】 : 高亮色 【Color】\n【splashColor】 : 水波纹色 【Color】\n【radius】 : 水波半径 【double】","code":"import 'package:flutter/material.dart';\nclass ColorInkWell extends StatefulWidget {\n @override\n _ColorInkWellState createState() => _ColorInkWellState();\n}\n\nclass _ColorInkWellState extends State {\n var _info = 'Push';\n\n @override\n Widget build(BuildContext context) {\n return InkWell(\n onTap: () => {},\n splashColor: Colors.blueAccent,\n highlightColor: Colors.orange,\n onHighlightChanged: (v) =>\n setState(() => _info = 'onHighlightChanged:$v'),\n radius: 50,\n child: Container(\n alignment: Alignment.center,\n width: 180,\n height: 50,\n child: Text(_info),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":150,"name":"InkWell基本事件","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【onTap】 : 点击事件 【Function()】\n【onDoubleTap】 : 双击事件 【Function()】\n【onTapCancel】 : 点击取消 【Function()】\n【onLongPress】 : 长按事件 【Function()】","code":"import 'package:flutter/material.dart';\nclass CustomInkWell extends StatefulWidget {\n @override\n _CustomInkWellState createState() => _CustomInkWellState();\n}\n\nclass _CustomInkWellState extends State {\n var _info = 'Push';\n\n @override\n Widget build(BuildContext context) {\n return InkWell(\n onTap: () => setState(() => _info = 'onTap'),\n onDoubleTap: () => setState(() => _info = 'onDoubleTap'),\n onLongPress: () => setState(() => _info = 'onLongPress'),\n onTapCancel: () => setState(() => _info = 'onTapCancel'),\n child: Container(\n alignment: Alignment.center,\n width: 120,\n height: 50,\n child: Text(_info),\n ),\n );\n }\n}"},{"id":null,"widgetId":114,"name":"DefaultTextStyleTransition基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【textAlign】 : 文字对齐方式 【TextAlign】\n【softWrap】 : 是否包裹 【bool】\n【maxLines】 : 最大行数 【int】\n【overflow】 : 溢出模式 【TextOverflow】\n【style】 : 动画 【Animation】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomDefaultTextStyleTransition extends StatefulWidget {\n @override\n _CustomDefaultTextStyleTransitionState createState() =>\n _CustomDefaultTextStyleTransitionState();\n}\n\nclass _CustomDefaultTextStyleTransitionState\n extends State\n with SingleTickerProviderStateMixin {\n AnimationController _ctrl;\n\n @override\n void initState() {\n _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 1));\n _ctrl.forward();\n super.initState();\n }\n\n @override\n void dispose() {\n _ctrl.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onTap: () {\n setState(() {\n _ctrl.reset();\n _ctrl.forward();\n });\n },\n child: Container(\n alignment: Alignment.center,\n width: 300,\n height: 100,\n child: DefaultTextStyleTransition(\n textAlign: TextAlign.start,\n softWrap: true,\n maxLines: 1,\n overflow: TextOverflow.ellipsis,\n style: TextStyleTween(\n begin: TextStyle(color: Colors.blue, fontSize: 50, shadows: [\n Shadow(\n offset: Offset(1, 1), color: Colors.black, blurRadius: 3)\n ]),\n end: TextStyle(color: Colors.white, fontSize: 20, shadows: [\n Shadow(\n offset: Offset(1, 1), color: Colors.purple, blurRadius: 3)\n ])).animate(_ctrl),\n child: Text('张风捷特烈'),\n ),\n ));\n }\n}\n"},{"id":null,"widgetId":176,"name":"Dismissible基本使用","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【background】 : 左底 【Widget】\n【secondaryBackground】 : 右底 【Widget】\n【key】 : 键 【Key】\n【confirmDismiss】 : 确认回调 【DismissDirectionCallback】\n【onDismissed】 : 消失回调 【DismissDirectionCallback】,","code":"import 'package:flutter/material.dart';\nclass CustomDismissible extends StatefulWidget {\n @override\n _CustomDismissibleState createState() => _CustomDismissibleState();\n}\n\nclass _CustomDismissibleState extends State {\n var 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: ListView(\n padding: EdgeInsets.symmetric(horizontal: 5),\n children: data.map((color) => _buildItem(color)).toList(),\n ),\n );\n }\n\n Widget _buildItem(Color color) {\n return Dismissible(\n background: Container(\n color: Colors.green,\n alignment: Alignment(-0.9, 0),\n child: Icon(\n Icons.check,\n color: Colors.white,\n ),\n ),\n secondaryBackground: Container(\n alignment: Alignment(0.9, 0),\n child: Icon(\n Icons.close,\n color: Colors.white,\n ),\n color: Colors.red,\n ),\n key: ValueKey(color),\n onDismissed: (d) {\n data.remove(color);\n },\n confirmDismiss: (e) async {\n if (e == DismissDirection.endToStart) {\n return true;\n } else {\n return false;\n }\n },\n child: Container(\n alignment: Alignment.center,\n height: 50,\n color: color,\n child: Text(\n colorString(color),\n style: TextStyle(color: Colors.white, shadows: [\n Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2)\n ]),\n ),\n ),\n );\n }\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":115,"name":"RelativePositionedTransition基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【size】 : 左和上的偏移量 【Size】\n【rect】 : 动画 【Animation】\n PositionedTransition组件只能在Stack内起作用","code":"import 'package:flutter/material.dart';\nclass CustomRelativePositionedTransition extends StatefulWidget {\n @override\n _CustomRelativePositionedTransitionState createState() =>\n _CustomRelativePositionedTransitionState();\n}\n\nclass _CustomRelativePositionedTransitionState\n extends State\n with SingleTickerProviderStateMixin {\n AnimationController _ctrl;\n\n @override\n void initState() {\n _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 2));\n _ctrl.forward();\n super.initState();\n }\n\n @override\n void dispose() {\n _ctrl.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onTap: () {\n setState(() {\n _ctrl.reset();\n _ctrl.forward();\n });\n },\n child: Container(\n color: Colors.grey.withAlpha(33),\n width: 200,\n height: 100,\n child: Stack(\n children: [\n RelativePositionedTransition(\n size: Size(200, 100),\n rect: RectTween(\n begin: Rect.fromLTRB(0, 0, 50, 50),\n end: Rect.fromLTRB(0, 0, 50, 50).translate(100, 50),\n ).animate(_ctrl),\n child: Icon(\n Icons.android,\n color: Colors.green,\n size: 50,\n ),\n )\n ],\n ),\n ));\n }\n}\n"},{"id":null,"widgetId":176,"name":"Dismissible基本使用","priority":2,"subtitle":" \n【direction】 : 方向 【DismissDirection】\n【crossAxisEndOffset】 : 偏移 【double】,","code":"import 'package:flutter/material.dart';\nclass DirectionDismissible extends StatefulWidget {\n @override\n _CustomDirectionDismissibleState createState() => _CustomDirectionDismissibleState();\n}\n\nclass _CustomDirectionDismissibleState extends State {\n var 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: ListView(\n scrollDirection: Axis.horizontal,\n padding: EdgeInsets.symmetric(horizontal: 5),\n children: data.map((color) => _buildItem(color)).toList(),\n ),\n );\n }\n\n Widget _buildItem(Color color) {\n return Dismissible(\n direction: DismissDirection.vertical,\n background: Container(\n color: Colors.green,\n alignment: Alignment( 0,-0.9,),\n child: Icon(\n Icons.check,\n color: Colors.white,\n ),\n ),\n crossAxisEndOffset: 0.5,\n secondaryBackground: Container(\n alignment: Alignment( 0,0.9,),\n child: Icon(\n Icons.close,\n color: Colors.white,\n ),\n color: Colors.red,\n ),\n key: ValueKey(color),\n onDismissed: (d) {\n data.remove(color);\n },\n confirmDismiss: (e) async {\n print(e);\n if (e == DismissDirection.up) {\n return true;\n } else {\n return false;\n }\n },\n child: Container(\n alignment: Alignment.center,\n width: 80,\n color: color,\n child: Text(\n colorString(color),\n style: TextStyle(color: Colors.white, shadows: [\n Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2)\n ]),\n ),\n ),\n );\n }\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":251,"name":"NestedScrollView基本用法","priority":1,"subtitle":" \n【controller】 : 滑动控制器 【ScrollController】\n【scrollDirection】 : 滑动方向 【Axis】\n【reverse】 : 是否反向 【bool】\n【physics】 : 滑顶样式 【ScrollPhysics】\n【dragStartBehavior】 : 开始拖动行为 【DragStartBehavior】\n【headerSliverBuilder】 : *头部构造器 【NestedScrollViewHeaderSliversBuilder】\n【body】 : *内容 【Widget】","code":"import 'package:flutter/material.dart';\nclass NestedScrollViewDemo extends StatelessWidget {\n final _tabs = ['风神传', '封妖志', \"幻将录\", \"永恒传说\"];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width,\n height: MediaQuery.of(context).size.height - 200,\n child: Scaffold(\n body: DefaultTabController(\n length: _tabs.length,\n child: NestedScrollView(\n headerSliverBuilder:\n (BuildContext context, bool innerBoxIsScrolled) {\n return [\n SliverOverlapAbsorber(\n handle: NestedScrollView.sliverOverlapAbsorberHandleFor(\n context),\n sliver: SliverAppBar(\n title: const Text('旷古奇书'),\n pinned: true,\n elevation: 6, //影深\n expandedHeight: 220.0,\n forceElevated: innerBoxIsScrolled, //为true时展开有阴影\n flexibleSpace: FlexibleSpaceBar(\n background: Image.asset(\n \"assets/images/wy_300x200_filter.jpg\",\n fit: BoxFit.cover,\n ),\n ),\n bottom: TabBar(\n tabs: _tabs\n .map((String name) => Tab(text: name,))\n .toList(),\n ),\n ),\n ),\n ];\n },\n body: _buildTabBarView(),\n ),\n ),\n ));\n }\n\n Widget _buildTabBarView() {\n return TabBarView(\n children: _tabs.map((String name) {\n return SafeArea(\n top: false,\n bottom: false,\n child: Builder(\n builder: (BuildContext context) {\n return CustomScrollView(\n key: PageStorageKey(name),\n slivers: [\n SliverOverlapInjector(\n handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),\n ),\n SliverPadding(\n padding: const EdgeInsets.all(8.0),\n sliver: SliverFixedExtentList(\n itemExtent: 48.0,\n delegate: SliverChildBuilderDelegate(\n (BuildContext context, int index) {\n return ListTile(\n title: Text('《$name》 第 $index章'),\n );\n },\n childCount: 50,\n ),\n ),\n ),\n ],\n );\n },\n ),\n );\n }).toList(),\n );\n }\n}\n"},{"id":null,"widgetId":24,"name":"CupertinoButton点击事件","priority":1,"subtitle":"【color】: 颜色 【Color】\n【pressedOpacity】: 按下时透明度 【double】\n【child】: 子组件 【Widget】\n【padding】: 内边距 【EdgeInsetsGeometry】\n【borderRadius】: 圆角半径 【BorderRadius】\n【onPressed】: 点击事件 【Function】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoButton extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var data = {\n CupertinoColors.activeBlue:4.0,\n Colors.blue:6.0,\n CupertinoColors.activeOrange:8.0,\n };\n return Wrap(\n spacing: 20,\n children:data.keys.map((e)=> CupertinoButton(\n padding: EdgeInsets.zero,\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage'),\n color: e,\n pressedOpacity: 0.4,\n borderRadius: BorderRadius.all(Radius.circular(data[e])),\n child: Text(\"iOS\"),\n )).toList()\n );\n }\n}\n\n"},{"id":null,"widgetId":61,"name":"BottomAppBar基本用法","priority":1,"subtitle":" \n【elevation】 : 影深 【double】\n【shape】 : 形状 【NotchedShape】\n【notchMargin】 : 间隔距离 【double】\n【color】 : 颜色 【Color】\n【child】 : 孩子 【Widget】","code":"import 'package:flutter/material.dart';\nclass CustomBottomAppBar extends StatefulWidget {\n @override\n _CustomBottomAppBarState createState() => _CustomBottomAppBarState();\n}\n\nclass _CustomBottomAppBarState extends State {\n var _position = 0;\n var _location = FloatingActionButtonLocation.centerDocked;\n final iconsMap = {\n \"图鉴\": Icons.home,\n \"动态\": Icons.toys,\n \"喜欢\": Icons.favorite,\n \"手册\": Icons.class_,\n };\n var activeColor = Colors.blue.withAlpha(240);\n\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width,\n height: 180,\n child: Scaffold(\n backgroundColor: Colors.purple.withAlpha(22),\n floatingActionButton: FloatingActionButton(\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage'),\n child: Icon(Icons.add),\n ),\n bottomNavigationBar: _buildBottomAppBar(),\n floatingActionButtonLocation: _location,\n body: _buildContent(),\n ),\n );\n }\n\n Widget _buildBottomAppBar() {\n return BottomAppBar(\n elevation: 1,\n shape: CircularNotchedRectangle(),\n notchMargin: 5,\n color: Colors.red,\n child: Row(\n mainAxisAlignment: MainAxisAlignment.spaceAround,\n children: info.asMap().keys.map((i) => _buildChild(i)).toList()\n ..insertAll(isCenter ? 2 : 4, [SizedBox(width: 30)])),\n );\n }\n\n Container _buildContent() {\n return Container(\n alignment: Alignment.center,\n child: Wrap(\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n Text(\n '当前页索引:$_position',\n style: TextStyle(color: Colors.blue, fontSize: 18),\n ),\n Switch(\n value: isCenter,\n onChanged: (v) {\n setState(() {\n _location = v\n ? FloatingActionButtonLocation.centerDocked\n : FloatingActionButtonLocation.endDocked;\n });\n }),\n ],\n ),\n );\n }\n\n List get info => iconsMap.keys.toList();\n\n bool get isCenter => _location == FloatingActionButtonLocation.centerDocked;\n\n Widget _buildChild(int i) {\n var active = i == _position;\n return Padding(\n padding: const EdgeInsets.all(8.0),\n child: GestureDetector(\n onTap: () => setState(() => _position = i),\n child: Wrap(\n direction: Axis.vertical,\n alignment: WrapAlignment.center,\n children: [\n Icon(\n iconsMap[info[i]],\n color: active ? activeColor : Colors.white,\n size: 30,\n ),\n Text(info[i],\n style: TextStyle(\n color: active ? activeColor : Colors.white, fontSize: 14)),\n ],\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":153,"name":"RawChip点击效果","priority":1,"subtitle":" \n【label】: 中间组件 【Widget】\n【padding】 : 内边距 【EdgeInsetsGeometry】\n【labelPadding】 : label边距 【EdgeInsetsGeometry】\n【shadowColor】: 阴影色 【Color】\n【avatar】: 左侧组件 【Widget】\n【elevation】: 影深 【double】\n【pressElevation】: 点击时影深 【double】\n【onPressed】 : 点击事件 【Function()】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass PressRawChip extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: RawChip(\n padding: EdgeInsets.all(5),\n labelPadding: EdgeInsets.all(3),\n label: Text('张风捷特烈'),\n avatar: Image.asset(\"assets/images/icon_head.png\"),\n elevation: 3,\n pressElevation: 5,\n shadowColor: Colors.orangeAccent,\n onPressed: () => Navigator.of(context).pushNamed('AboutMePage'),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":153,"name":"RawChip选中和删除效果","priority":2,"subtitle":" \n【selected】: 是否选中 【bool】\n【deleteIconColor】: 尾部图标色 【Color】\n【selectedColor】: 选中色 【Color】\n【deleteIcon】: 尾部组件 【Widget】\n【onSelected】: 选中事件 【Function(bool)】\n【onDeleted】 : 尾部事件 【Function()】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass SelectRawChip extends StatefulWidget {\n @override\n _SelectRawChipState createState() => _SelectRawChipState();\n}\n\nclass _SelectRawChipState extends State {\n bool _selected = false;\n @override\n Widget build(BuildContext context) {\n return Container(\n child: RawChip(\n selected: _selected,\n padding: EdgeInsets.all(5),\n labelPadding: EdgeInsets.all(3),\n deleteIconColor: Colors.red,\n selectedColor: Colors.orangeAccent.withAlpha(44),\n label: Text('张风捷特烈'),\n avatar: Image.asset(\"assets/images/icon_head.png\"),\n elevation: 3,\n pressElevation: 5,\n shadowColor: Colors.orangeAccent,\n onSelected: (v)=> setState(() => _selected=v),\n onDeleted: () => Navigator.of(context).pushNamed('AboutMePage'),\n ),\n );\n }\n}"},{"id":null,"widgetId":46,"name":"CircularProgressIndicator基本使用","priority":1,"subtitle":" \n【value】 : 进度 【double】\n【backgroundColor】 : 背景色 【Color】\n【valueColor】 : 进度颜色 【Animation】\n【strokeWidth】 : 线宽 【double】","code":"import 'package:flutter/material.dart';\nclass CustomCircularProgressIndicator extends StatefulWidget {\n @override\n _CustomCircularProgressIndicatorState createState() =>\n _CustomCircularProgressIndicatorState();\n}\n\nclass _CustomCircularProgressIndicatorState\n extends State {\n\n var data = [0.2,0.4,0.6,0.8,null];\n\n @override\n Widget build(BuildContext context) {\n\n return Wrap(\n spacing: 10,\n children:data.map((e)=>Container(\n width: 50,\n height: 50,\n child: CircularProgressIndicator(\n value: e,\n backgroundColor: Colors.grey.withAlpha(33),\n valueColor: AlwaysStoppedAnimation(Colors.orange),\n strokeWidth: 5,\n ),\n )).toList(),\n );\n }\n}\n"},{"id":null,"widgetId":100,"name":"AnimatedCrossFade动画曲线","priority":2,"subtitle":" \n【firstCurve】 : 第一曲线 【Curve】\n【secondCurve】 : 第二曲线 【Curve】\n【sizeCurve】 : 尺寸变化曲线 【CrossFadeState】","code":"import 'package:flutter/material.dart';\nclass CurveAnimatedCrossFade extends StatefulWidget {\n @override\n _CurveAnimatedCrossFadeState createState() => _CurveAnimatedCrossFadeState();\n}\n\nclass _CurveAnimatedCrossFadeState extends State {\n var _crossFadeState = CrossFadeState.showFirst;\n\n bool get isFirst=> _crossFadeState == CrossFadeState.showFirst;\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: [\n Container(\n child: AnimatedCrossFade(\n firstCurve: Curves.easeInCirc,\n secondCurve: Curves.easeInToLinear,\n sizeCurve: Curves.bounceOut,\n firstChild: Container(\n alignment: Alignment.center,\n width: 200,\n height: 80,\n color: Colors.orange ,\n child: FlutterLogo(colors: Colors.blue,size: 50,),\n ),\n secondChild: Container(\n width: 200,\n height: 150,\n alignment: Alignment.center,\n color: Colors.blue,\n child: FlutterLogo(\n textColor: Colors.white,\n colors: Colors.orange,size: 100,style: FlutterLogoStyle.stacked,),\n ),\n duration: Duration(milliseconds: 1000),\n crossFadeState: _crossFadeState,\n ),\n ),\n _buildSwitch(),\n ],\n );\n }\n\n Widget _buildSwitch() => Switch(value: isFirst, onChanged: (v){\n setState(() {\n _crossFadeState= v?CrossFadeState.showFirst:CrossFadeState.showSecond;\n });\n });\n}\n"},{"id":null,"widgetId":47,"name":"LinearProgressIndicator基本使用","priority":1,"subtitle":" \n【value】 : 进度 【double】\n【backgroundColor】 : 背景色 【Color】\n【valueColor】 : 进度颜色 【Animation】\n value为null时会不停循环","code":"import 'package:flutter/material.dart';\nclass CustomLinearProgressIndicator extends StatefulWidget {\n @override\n _CustomLinearProgressIndicatorState createState() =>\n _CustomLinearProgressIndicatorState();\n}\n\nclass _CustomLinearProgressIndicatorState\n extends State {\n var data = [0.2, 0.4, 0.6, 0.8, null];\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n children: data\n .map((e) => Container(\n width: 50,\n height: 3,\n child:LinearProgressIndicator(\n value: e,\n backgroundColor: Colors.grey.withAlpha(33),\n valueColor: AlwaysStoppedAnimation(Colors.orange),\n ),\n ))\n .toList(),\n );\n }\n}\n"},{"id":null,"widgetId":100,"name":"AnimatedCrossFade基本使用","priority":1,"subtitle":" \n【firstChild】 : 第一孩子 【Widget】\n【secondChild】 : 第二孩子 【Widget】\n【crossFadeState】 : 显示第几个 【CrossFadeState】\n【duration】 : 时长 【Duration】","code":"import 'package:flutter/material.dart';\nclass CustomAnimatedCrossFade extends StatefulWidget {\n @override\n _CustomAnimatedCrossFadeState createState() =>\n _CustomAnimatedCrossFadeState();\n}\n\nclass _CustomAnimatedCrossFadeState extends State {\n var _crossFadeState = CrossFadeState.showFirst;\n\n bool get isFirst => _crossFadeState == CrossFadeState.showFirst;\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: [\n Container(\n child: AnimatedCrossFade(\n firstChild: Container(\n alignment: Alignment.center,\n width: 200,\n height: 150,\n color: Colors.orange,\n child: FlutterLogo(colors: Colors.blue, size: 100,),\n ),\n secondChild: Container(\n width: 200,\n height: 150,\n alignment: Alignment.center,\n color: Colors.blue,\n child: FlutterLogo(\n textColor: Colors.white,\n colors: Colors.orange,\n size: 100,\n style: FlutterLogoStyle.stacked,),\n ),\n duration: Duration(milliseconds: 600),\n\n crossFadeState: _crossFadeState,\n ),\n ),\n _buildSwitch(),\n ],\n\n );\n }\n\n Widget _buildSwitch() =>\n Switch(value: isFirst, onChanged: (v) {\n setState(() {\n _crossFadeState =\n v ? CrossFadeState.showFirst : CrossFadeState.showSecond;\n });\n });\n}"},{"id":null,"widgetId":65,"name":"MaterialApp基本用法","priority":1,"subtitle":" \n【theme】 : 主题 【ThemeData】\n【title】 : 任务栏标题 【String】\n【onGenerateRoute】 : 路由生成器 【RouteFactory】\n【home】 : 主页 【Widget】","code":"import 'package:flutter/material.dart';\nimport '../../../../app/router.dart';\nimport '../../StatefulWidget/Scaffold/node1_base.dart';\nclass CustomMaterialApp extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width,\n height: MediaQuery.of(context).size.height - 200,\n child: MaterialApp(\n title: 'Flutter Demo',\n onGenerateRoute: Router.generateRoute,\n theme: ThemeData(\n primarySwatch: Colors.blue,\n ),\n home: CustomScaffold(),\n ),\n );\n }\n}\n"},{"id":null,"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】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CupertinoTextFieldStyle extends StatelessWidget {\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child:\n CupertinoTextField(\n style: TextStyle(color: Colors.blue),\n prefix: Icon(CupertinoIcons.add),\n prefixMode: OverlayVisibilityMode.notEditing,\n suffix: Icon(CupertinoIcons.clear),\n suffixMode: OverlayVisibilityMode.editing,\n cursorColor: Colors.purple,\n cursorWidth: 4,\n cursorRadius: Radius.circular(2),\n readOnly: false,\n placeholder: '输入用户名',\n )\n );\n }\n}\n"},{"id":null,"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】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CupertinoTextFieldDemo extends StatefulWidget {\n @override\n _CupertinoTextFieldDemoState createState() => _CupertinoTextFieldDemoState();\n}\n\nclass _CupertinoTextFieldDemoState extends State {\n var _value = '';\n var _color =Colors.black;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Column(\n children: [\n Text('输入了:$_value',style: TextStyle(color: _color),),\n CupertinoTextField(\n placeholder: 'Input Name',\n showCursor: true,\n minLines: 1,\n maxLines: 4,\n padding: EdgeInsets.all(8),\n onChanged: _onChanged,\n onTap: _onTap,\n onSubmitted: _onSubmitted,\n ),\n ],\n ),\n );\n }\n\n void _onChanged(String value) {\n setState(() {\n _value = value;\n });\n }\n\n void _onTap() {\n print('----_onTap----');\n setState(() {\n _color=Colors.blue;\n });\n }\n\n void _onSubmitted(String value) {\n print('----_onSubmitted:$value}----');\n setState(() {\n _color=Colors.black;\n });\n }\n\n}\n"},{"id":null,"widgetId":124,"name":"AnimatedDefaultTextStyle基本使用","priority":1,"subtitle":" \n【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】","code":"import 'package:flutter/material.dart';\nclass CustomAnimatedDefaultTextStyle extends StatefulWidget {\n @override\n _CustomAnimatedDefaultTextStyleState createState() =>\n _CustomAnimatedDefaultTextStyleState();\n}\n\nclass _CustomAnimatedDefaultTextStyleState\n extends State {\n final TextStyle start = TextStyle(color: Colors.blue, fontSize: 50, shadows: [\n Shadow(offset: Offset(1, 1), color: Colors.black, blurRadius: 3)\n ]);\n final TextStyle end = TextStyle(color: Colors.white, fontSize: 20, shadows: [\n Shadow(offset: Offset(1, 1), color: Colors.purple, blurRadius: 3)\n ]);\n\n TextStyle _style;\n\n @override\n void initState() {\n _style = start;\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildSwitch(),\n Container(\n alignment: Alignment.center,\n color: Colors.grey.withAlpha(22),\n width: 300,\n height: 100,\n child: AnimatedDefaultTextStyle(\n textAlign: TextAlign.start,\n softWrap: true,\n maxLines: 1,\n overflow: TextOverflow.ellipsis,\n duration: Duration(seconds: 1),\n curve: Curves.fastOutSlowIn,\n style: _style,\n onEnd: () => print('End'),\n child: Text(\n '张风捷特烈',\n style: TextStyle(color: Colors.white),\n ),\n ),\n ),\n ],\n );\n }\n\n Widget _buildSwitch() => Switch(\n value: _style == end,\n onChanged: (v) {\n setState(() {\n _style = v ? end : start;\n });\n });\n}\n"},{"id":null,"widgetId":231,"name":"InputDecorator基本使用","priority":1,"subtitle":"【decoration】 : 装饰对象 【InputDecoration】\n【textAlign】 : 文字对齐方式 【TextAlign】\n【child】 : 子组件 【Widget】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass InputDecoratorDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Padding(\n padding: const EdgeInsets.all(8.0),\n child: InputDecorator(\n decoration: InputDecoration(),\n child: EditableText(\n controller: TextEditingController(text:'hello'),\n focusNode: FocusNode(),\n style: TextStyle(fontSize: 12,color: Colors.black),\n cursorColor: Colors.blue,\n backgroundCursorColor: Colors.orange,\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":39,"name":"Checkbox的三态","priority":2,"subtitle":" \n【tristate】 : 是否是三态 【double】\n onChanged时,","code":"import 'package:flutter/material.dart';\nclass TristateCheckBok extends StatefulWidget {\n @override\n _TristateCheckBokState createState() => _TristateCheckBokState();\n}\n\nclass _TristateCheckBokState extends State {\n bool _checked = false;\n final colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green];\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n children: colors\n .map((e) =>\n Checkbox(\n value: _checked,\n tristate: true,\n checkColor: Colors.white,\n activeColor: e,\n onChanged: (v) {\n print(v);\n setState(() => _checked = v);\n }))\n .toList(),\n );\n }\n}\n\n"},{"id":null,"widgetId":39,"name":"Checkbox基础用法","priority":1,"subtitle":" \n【value】 : 是否选中 【double】\n【checkColor】: 选中时✔️gou颜色 【Color】\n【activeColor】: 选中时框内颜色 【Color】\n【onChanged】: 状态改变事件 【Function(bool)】,","code":"import 'package:flutter/material.dart';\nclass CustomCheckbox extends StatefulWidget {\n @override\n _CustomCheckboxState createState() => _CustomCheckboxState();\n}\n\nclass _CustomCheckboxState extends State {\n bool _checked = false;\n final colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green];\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n children: colors\n .map((e) =>\n Checkbox(\n value: _checked,\n checkColor: Colors.white,\n activeColor: e,\n onChanged: (v) =>\n setState(() => _checked = v)))\n .toList(),\n );\n }\n}\n\n\n"},{"id":null,"widgetId":62,"name":"CupertinoNavigationBar基本用法","priority":1,"subtitle":" \n【leading】 : 左侧组件 【Widget】\n【middle】 : 中间组件 【Widget】\n【trailing】 : 尾部组件 【Widget】\n【backgroundColor】 : 背景色 【Color】\n【padding】 : 内边距 【EdgeInsetsDirectional】\n【border】 : 边线 【Border】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoNavigationBar extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return CupertinoNavigationBar(\n leading: Icon(\n CupertinoIcons.back,\n size: 25,\n color: Colors.blue,\n ),\n middle: Text(\"风雪雅舍\"),\n trailing: Image.asset(\n \"assets/images/icon_head.png\",\n width: 25.0,\n height: 25.0,\n ),\n backgroundColor: Color(0xfff1f1f1),\n padding: EdgeInsetsDirectional.only(start: 10,end: 20),\n border: Border.all(color: Colors.transparent),\n );\n }\n}\n"},{"id":null,"widgetId":41,"name":"CupertinoSwitch基本使用","priority":1,"subtitle":" \n【value】 : 是否选中 【double】\n【activeColor】 : 激活态颜色 【Color】\n【onChanged】 : 切换回调 【Function(double)】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoSwitch extends StatefulWidget {\n @override\n _CustomCupertinoSwitchState createState() => _CustomCupertinoSwitchState();\n}\n\nclass _CustomCupertinoSwitchState extends State {\n final colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green];\n\n bool _checked = false;\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n children: colors\n .map((e) => CupertinoSwitch(\n value: _checked,\n activeColor: e,\n onChanged: (v) {\n setState(() => _checked = v);\n }))\n .toList(),\n );\n }\n}\n"},{"id":null,"widgetId":54,"name":"TextField基本用法","priority":1,"subtitle":" \n【controller】 : 控制器 【TextEditingController】\n【style】 : 文字样式 【TextStyle】\n【decoration】 : 装饰线 【InputDecoration】\n【onEditingComplete】 : 输入完成事件 【Function()】\n【onSubmitted】 : 提交事件 【Function(String)】\n【onChanged】 : 输入事件 【Function(String)】","code":"import 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nclass CustomTextField extends StatefulWidget {\n @override\n _CustomTextFieldState createState() => _CustomTextFieldState();\n}\n\nclass _CustomTextFieldState extends State {\n final FocusNode _focusNode = FocusNode();\n TextEditingController _controller;\n\n void initState() {\n super.initState();\n _controller = TextEditingController();\n }\n\n @override\n void dispose() {\n _controller.dispose();\n _focusNode.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 300,\n child: TextField(\n controller: _controller,\n style: TextStyle(color: Colors.blue),\n decoration: InputDecoration(\n border: OutlineInputBorder(),\n labelText: 'username',\n ),\n onEditingComplete: () {\n print('onEditingComplete');\n },\n onChanged: (v) {\n print('onChanged:' + v);\n },\n onSubmitted: (v) {\n FocusScope.of(context).requestFocus(_focusNode);\n print('onSubmitted:' + v);\n _controller.clear();\n },\n ));\n }\n}\n\n"},{"id":null,"widgetId":54,"name":"TextField行数和cursor","priority":2,"subtitle":" \n【minLines】 : 最小行数 【int】\n【maxLines】 : 最大行数 【int】\n【cursorRadius】 : 光标半径 【Radius】\n【cursorColor】 : 光标颜色 【Color】\n【cursorWidth】 : 光标宽度 【double】\n【showCursor】 : 是否显示光标 【bool】\n【autofocus】 : 自动聚焦 【bool】","code":"import 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nclass CursorTextField extends StatefulWidget {\n @override\n _CursorTextFieldState createState() => _CursorTextFieldState();\n}\n\nclass _CursorTextFieldState extends State {\n final FocusNode _focusNode = FocusNode();\n\n @override\n void dispose() {\n _focusNode.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n crossAxisAlignment: CrossAxisAlignment.end,\n children: [\n _buildSubmitBtn(),\n _buildTextField(context),\n ],\n );\n }\n\n Container _buildTextField(BuildContext context) {\n return Container(\n width: 300,\n child: TextField(\n style: TextStyle(color: Colors.blue),\n minLines: 3,\n maxLines: 5,\n cursorColor: Colors.green,\n cursorRadius: Radius.circular(3),\n cursorWidth: 5,\n showCursor: true,\n decoration: InputDecoration(\n contentPadding: EdgeInsets.all(10),\n hintText: \"请输入...\",\n border: OutlineInputBorder(),\n ),\n onChanged: (v) {},\n ),\n );\n }\n\n _buildSubmitBtn() => FlatButton(\n color: Colors.blue,\n child: Text(\n \"提交\",\n style: TextStyle(color: Colors.white, fontSize: 16),\n ),\n onPressed: () => FocusScope.of(context).requestFocus(_focusNode));\n}\n"},{"id":null,"widgetId":54,"name":"decoration的复杂装饰","priority":3,"subtitle":" \nInputDecoration有非常多的装饰点,对应点缀见代码:\nborder: 边线相关\nhelper: 左下角相关提示\ncounter: 右下角相关提示\nprefix: 输入框内部最左侧\nsuffix: 输入框内部最右侧\nlabel: 无焦点时文字\nlabel: 无焦点时文字\nhint: 提示文字相关\nborder: 边线相关","code":"import 'package:flutter/material.dart';\nclass ComplexTextField extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return TextField(\n decoration: InputDecoration(\n border: OutlineInputBorder(),\n focusedBorder: OutlineInputBorder(\n borderSide: BorderSide(color: Colors.blue),\n borderRadius: BorderRadius.only(\n topLeft: Radius.circular(10), bottomLeft: Radius.circular(10))),\n enabledBorder: OutlineInputBorder(\n borderSide: BorderSide(color: Colors.deepPurpleAccent),\n borderRadius: BorderRadius.only(\n topLeft: Radius.circular(10), bottomLeft: Radius.circular(10))),\n labelText: 'username',\n labelStyle: TextStyle(color: Colors.purple),\n helperText: \"help me\",\n helperStyle: TextStyle(color: Colors.blue),\n\n suffixText: \"suffix\",\n suffixIcon: Icon(Icons.done),\n suffixStyle: TextStyle(color: Colors.green),\n\n counterText: \"counter\",\n counterStyle: TextStyle(color: Colors.orange),\n\n prefixText: \"ID \",\n prefixStyle: TextStyle(color: Colors.blue),\n prefixIcon: Icon(Icons.language),\n\n fillColor: Color(0x110099ee),\n filled: true,\n\n // errorText: \"error\",\n // errorMaxLines: 1,\n // errorStyle: TextStyle(color: Colors.red),\n // errorBorder: UnderlineInputBorder(),\n\n hintText: \"请输入用户名\",\n hintMaxLines: 1,\n hintStyle: TextStyle(color: Colors.black38),\n icon: Icon(Icons.assignment_ind),\n ));\n }\n}\n"},{"id":null,"widgetId":262,"name":"CupertinoSegmentedControl的颜色","priority":2,"subtitle":" \n【unselectedColor】 : 未选中色 【Color】\n【selectedColor】 : 选中色 【Color】\n【pressedColor】 : 按下色 【Color】\n【borderColor】 : 边线色 【Color】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CupertinoSegmentedControlColor extends StatefulWidget {\n @override\n _CupertinoSegmentedControlColorState createState() =>\n _CupertinoSegmentedControlColorState();\n}\n\nclass _CupertinoSegmentedControlColorState\n extends State {\n var _value = 1;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: CupertinoSegmentedControl(\n unselectedColor: Colors.yellow,\n selectedColor: Colors.green,\n pressedColor: Colors.blue,\n borderColor: Colors.red,\n groupValue: _value,\n onValueChanged: _onValueChanged,\n padding: EdgeInsets.only(top: 20),\n children: {\n 1: Padding(\n padding: EdgeInsets.only(left: 20, right: 20),\n child: Text(\"混沌战士\"),\n ),\n 2: Text(\"青眼白龙\"),\n 3: Text(\"黑魔术士\"),\n },\n ),\n );\n }\n\n void _onValueChanged(int value) {\n setState(() {\n _value=value;\n });\n }\n}\n"},{"id":null,"widgetId":262,"name":"基本使用","priority":1,"subtitle":" \n【children】 : 组件Map 【Map】\n【onValueChanged】 : 最小值 【ValueChanged】\n【groupValue】 : 选中值 【T】\n【padding】 : 内边距 【EdgeInsetsGeometry】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CupertinoSegmentedControlDemo extends StatefulWidget {\n @override\n _CupertinoSegmentedControlDemoState createState() =>\n _CupertinoSegmentedControlDemoState();\n}\n\nclass _CupertinoSegmentedControlDemoState\n extends State {\n var _value = 1;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: CupertinoSegmentedControl(\n groupValue: _value,\n onValueChanged: _onValueChanged,\n padding: EdgeInsets.only(top: 20),\n children: {\n 1: Padding(\n padding: EdgeInsets.only(left: 20, right: 20),\n child: Text(\"混沌战士\"),\n ),\n 2: Text(\"青眼白龙\"),\n 3: Text(\"黑魔术士\"),\n },\n ),\n );\n }\n\n void _onValueChanged(int value) {\n setState(() {\n _value=value;\n });\n }\n}\n"},{"id":null,"widgetId":42,"name":"Slider基本使用","priority":1,"subtitle":" \n【value】 : 数值 【double】\n【min】 : 最小值 【double】\n【max】 : 最大值 【double】\n【activeColor】 : 激活颜色 【Color】\n【inactiveColor】 : 非激活颜色 【Color】\n【onChanged】 : 改变时回调 【Function(double)】","code":"import 'package:flutter/material.dart';\nclass CustomSlider extends StatefulWidget {\n @override\n _CustomSliderState createState() => _CustomSliderState();\n}\n\nclass _CustomSliderState extends State {\n double _value = 0.0;\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Text('当前值:${_value.toStringAsFixed(1)}'),\n Slider(\n value: _value,\n min: 0.0,\n max: 360.0,\n activeColor: Colors.orangeAccent,\n inactiveColor: Colors.green.withAlpha(99),\n onChanged: (value) {\n setState(() {\n _value = value;\n });\n }),\n ],\n );\n }\n}\n"},{"id":null,"widgetId":42,"name":"Slider的分段与标签","priority":2,"subtitle":" \n【divisions】 : 分段数 【int】\n【label】 : 提示气泡文字 【String】\n【onChangeStart】 : 开始滑动时监听 【Function(double)】\n【onChangeEnd】 : 滑动结束时监听 【Function(double)】","code":"import 'package:flutter/material.dart';\nclass DivisionsSlider extends StatefulWidget {\n @override\n _DivisionsSliderState createState() => _DivisionsSliderState();\n}\n\nclass _DivisionsSliderState extends State {\n double _value = 0.0;\n\n @override\n Widget build(BuildContext context) {\n return Slider(\n value: _value,\n min: 0.0,\n max: 360.0,\n divisions: 10,\n label: '${_value.toStringAsFixed(1)}',\n activeColor: Colors.orangeAccent,\n inactiveColor: Colors.green.withAlpha(99),\n onChangeStart: (value) {\n print('开始滑动:$value');\n },\n onChangeEnd: (value) {\n print('滑动结束:$value');\n },\n onChanged: (value) {\n setState(() {\n _value = value;\n });\n });\n }\n}\n"},{"id":null,"widgetId":157,"name":"CupertinoPageScaffold基本用法","priority":1,"subtitle":" \n【child】 : 内容 【Widget】\n【backgroundColor】 : 背景色 【Color】\n【navigationBar】 : 头部 【ObstructingPreferredSizeWidget】","code":"import 'package:flutter/cupertino.dart';\nclass CustomCupertinoPageScaffold extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width,\n height: MediaQuery.of(context).size.height - 300,\n child: CupertinoPageScaffold(\n navigationBar: CupertinoNavigationBar(\n leading: Icon(CupertinoIcons.reply),\n trailing: Icon(CupertinoIcons.share),\n middle: Text('Flutter Unit'),\n ),\n backgroundColor: CupertinoColors.systemBackground,\n child: Center(\n child: Text('Hello, World!'),\n ),\n ),\n );\n }\n}"},{"id":null,"widgetId":200,"name":"Stepper基本使用","priority":1,"subtitle":" \n【steps】 : 步骤列表 【List】\n【currentStep】 : 当前步骤 【double】\n【onStepTapped】 : 点击回调 【ValueChanged】\n【onStepCancel】 : 上一步回调 【VoidCallback】\n【controlsBuilder】 : 控制器构造 【ControlsWidgetBuilder】","code":"import 'package:flutter/material.dart';\nclass StepperDemo extends StatefulWidget {\n @override\n _StepperDemoState createState() => _StepperDemoState();\n}\n\nclass _StepperDemoState extends State {\n int _position = 0;\n\n final stepsData = {\n \"填写表单\":'请按表单填写个人信息。',\n \"邮箱校验\":'已将邮件发送至您的邮箱,请按照相关指示对您的账号进行邮箱校验。',\n \"注册完成\":'恭喜您,注册完成!',\n };\n\n final steps = [\n Step(\n title: Text(\"填写表单\"),\n content: Container(height: 60, child: Text(\"请按表单填写个人信息\")),\n ),\n Step(title: Text(\"邮箱校验\"), content: Text(\"请对您的账号进行邮箱校验\")),\n Step(title: Text(\"注册完成\"), content: Text(\"恭喜您,注册完成\")),\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: Stepper(\n type:StepperType.horizontal,\n currentStep: _position,\n onStepTapped: (index) {\n setState(() {\n _position = index;\n });\n },\n onStepContinue: () {\n setState(() {\n if (_position < 2) {\n _position++;\n }\n });\n },\n onStepCancel: () {\n if (_position > 0) {\n setState(() {\n _position--;\n });\n }\n },\n controlsBuilder: (_,\n {VoidCallback onStepContinue, VoidCallback onStepCancel}) {\n return Row(\n children: [\n RaisedButton(\n color: Colors.blue,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onPressed: onStepContinue,\n child: Icon(\n Icons.check,\n color: Colors.white,\n ),\n ),\n RaisedButton(\n color: Colors.red,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onPressed: onStepCancel,\n child: Icon(\n Icons.keyboard_backspace,\n color: Colors.white,\n ),\n ),\n ],\n );\n },\n steps: stepsData.keys.map((e){\n bool isActive = stepsData.keys.toList().indexOf(e) ==_position;\n return Step(\n title: Text(e,style: TextStyle(color: isActive?Colors.blue:Colors.black),),\n isActive: isActive,\n state: _getState(stepsData.keys.toList().indexOf(e)),\n content: Container(height: 60, child: Text(stepsData[e])),\n );\n }).toList()),\n );\n }\n _getState(index){\n if(_position==index) return StepState.editing;\n if(_position>index) return StepState.complete;\n return StepState.indexed;\n }\n}\n"},{"id":null,"widgetId":200,"name":"Stepper的方向","priority":2,"subtitle":" \n【type】 : 方向 【StepperType】","code":"import 'package:flutter/material.dart';\nclass VerticalStepper extends StatefulWidget {\n @override\n _VerticalStepperState createState() => _VerticalStepperState();\n}\n\nclass _VerticalStepperState extends State {\n int _position = 0;\n\n final stepsData = {\n \"填写表单\": '请按表单填写个人信息。',\n \"邮箱校验\": '已将邮件发送至您的邮箱,请按照相关指示对您的账号进行邮箱校验。',\n \"注册完成\": '恭喜您,注册完成!',\n };\n\n final steps = [\n Step(\n title: Text(\"填写表单\"),\n content: Container(height: 60, child: Text(\"请按表单填写个人信息\")),\n ),\n Step(title: Text(\"邮箱校验\"), content: Text(\"请对您的账号进行邮箱校验\")),\n Step(title: Text(\"注册完成\"), content: Text(\"恭喜您,注册完成\")),\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Stepper(\n type: StepperType.vertical,\n currentStep: _position,\n onStepTapped: (index) {\n setState(() {\n _position = index;\n });\n },\n onStepContinue: () {\n setState(() {\n if (_position < 2) {\n _position++;\n }\n });\n },\n onStepCancel: () {\n if (_position > 0) {\n setState(() {\n _position--;\n });\n }\n },\n controlsBuilder: (_,\n {VoidCallback onStepContinue, VoidCallback onStepCancel}) {\n return Row(\n children: [\n RaisedButton(\n color: Colors.blue,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onPressed: onStepContinue,\n child: Icon(\n Icons.check,\n color: Colors.white,\n ),\n ),\n RaisedButton(\n color: Colors.red,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onPressed: onStepCancel,\n child: Icon(\n Icons.keyboard_backspace,\n color: Colors.white,\n ),\n ),\n ],\n );\n },\n steps: stepsData.keys.map((e) {\n bool isActive = stepsData.keys.toList().indexOf(e) == _position;\n return Step(\n title: Text(\n e,\n style: TextStyle(color: isActive ? Colors.blue : Colors.black),\n ),\n isActive: isActive,\n state: _getState(stepsData.keys.toList().indexOf(e)),\n content: Container(height: 60, child: Text(stepsData[e])),\n );\n }).toList()),\n );\n }\n\n _getState(index) {\n if (_position == index) return StepState.editing;\n if (_position > index) return StepState.complete;\n return StepState.indexed;\n }\n}\n"},{"id":null,"widgetId":120,"name":"AnimatedAlign基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【duration】 : 动画时长 【Duration】\n【onEnd】 : 动画结束回调 【Function()】\n【alignment】 : 对齐方式 【AlignmentGeometry】\n【curve】 : 动画曲线 【Duration】\n【padding】 : 内边距 【EdgeInsetsGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomAnimatedAlign extends StatefulWidget {\n @override\n _CustomAnimatedAlignState createState() => _CustomAnimatedAlignState();\n}\n\nclass _CustomAnimatedAlignState extends State {\n final Alignment start = Alignment(0, 0);\n final Alignment end = Alignment.bottomRight;\n\n Alignment _alignment;\n\n @override\n void initState() {\n _alignment = start;\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildSwitch(),\n Container(\n color: Colors.grey.withAlpha(22),\n width: 200,\n height: 100,\n child: AnimatedAlign(\n duration: Duration(seconds: 1),\n curve: Curves.fastOutSlowIn,\n alignment: _alignment,\n onEnd: () => print('End'),\n child: Container(\n height: 40,\n width: 80,\n alignment: Alignment.center,\n color: Colors.blue,\n child: Text(\n '张风捷特烈',\n style: TextStyle(color: Colors.white),\n ),\n ),\n ),\n ),\n ],\n );\n }\n\n Widget _buildSwitch() => Switch(\n value: _alignment == end,\n onChanged: (v) {\n setState(() {\n _alignment = v ? end : start;\n });\n });\n}\n"},{"id":null,"widgetId":48,"name":"CupertinoActivityIndicator基本使用","priority":1,"subtitle":" \n【animating】 : 是否loading动画 【bool】\n【radius】 : 半径 【double】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoActivityIndicator extends StatelessWidget {\n\n @override\n Widget build(BuildContext context) {\n\n return Wrap(\n spacing: 20,\n children: [\n CupertinoActivityIndicator(\n animating: true,\n radius: 25,\n ),\n CupertinoActivityIndicator(\n animating: false,\n radius: 25,\n )\n ],\n );\n }\n}\n"},{"id":null,"widgetId":123,"name":"AnimatedContainer基本使用","priority":1,"subtitle":" \n【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】","code":"import 'package:flutter/material.dart';\nclass CustomAnimatedContainer extends StatefulWidget {\n @override\n _CustomAnimatedContainerState createState() =>\n _CustomAnimatedContainerState();\n}\n\nclass _CustomAnimatedContainerState extends State {\n final Decoration startDecoration = BoxDecoration(\n color: Colors.blue,\n image: DecorationImage(\n image: AssetImage('assets/images/wy_200x300.jpg'), fit: BoxFit.cover),\n borderRadius: BorderRadius.all(Radius.circular(20)));\n final Decoration endDecoration = BoxDecoration(\n image: DecorationImage(\n image: AssetImage('assets/images/wy_200x300.jpg'), fit: BoxFit.cover),\n color: Colors.orange,\n borderRadius: BorderRadius.all(Radius.circular(50)));\n\n final Alignment startAlignment = Alignment(0, 0);\n final Alignment endAlignment = Alignment.topLeft + Alignment(0.2, 0.2);\n\n final startHeight = 100.0;\n final endHeight = 50.0;\n\n Decoration _decoration;\n double _height;\n Alignment _alignment;\n\n @override\n void initState() {\n _decoration = startDecoration;\n _height = startHeight;\n _alignment=startAlignment;\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildSwitch(),\n AnimatedContainer(\n duration: Duration(seconds: 1),\n curve: Curves.fastOutSlowIn,\n alignment: _alignment,\n color: Colors.grey.withAlpha(22),\n width: 200,\n height: 120,\n child: UnconstrainedBox(\n child: AnimatedContainer(\n duration: Duration(seconds: 1),\n curve: Curves.fastOutSlowIn,\n decoration: _decoration,\n onEnd: () => print('End'),\n height: _height,\n width: _height,\n ),\n ),\n ),\n ],\n );\n }\n\n Widget _buildSwitch() => Switch(\n value: _height == endHeight,\n onChanged: (v) {\n setState(() {\n _height = v ? endHeight : startHeight;\n _decoration = v ? endDecoration : startDecoration;\n _alignment = v ? endAlignment : startAlignment;\n });\n });\n}\n"},{"id":null,"widgetId":178,"name":"ExpansionPanelList基本使用","priority":1,"subtitle":" \n【children】 : 子组件列表 【List】\n【animationDuration】 : 动画时长 【Duration】\n【expansionCallback】 : 展开回调 【List】\n【onPressed】 : 点击事件 【Function()】","code":"import 'package:flutter/material.dart';\nclass CustomExpansionPanelList extends StatefulWidget {\n @override\n _CustomExpansionPanelListState createState() =>\n _CustomExpansionPanelListState();\n}\n\nclass _CustomExpansionPanelListState extends State {\n var data = [\n Colors.red[50],\n Colors.red[100],\n Colors.red[200],\n Colors.red[300],\n Colors.red[400],\n Colors.red[500],\n Colors.red[600],\n Colors.red[700],\n Colors.red[800],\n Colors.red[900],\n ];\n int _position = 0;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 300,\n child: ExpansionPanelList(\n children: data.map((color) => _buildItem(color)).toList(),\n animationDuration: Duration(milliseconds: 200),\n expansionCallback: (index, open) {\n setState(() => _position=open?-1:index);\n },\n ),\n );\n }\n\n ExpansionPanel _buildItem(Color color) {\n return ExpansionPanel(\n isExpanded: data.indexOf(color) == _position,\n canTapOnHeader: true,\n headerBuilder: (ctx, index) => Center(\n child: Wrap(\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n Container(\n height: 30,\n width: 30,\n decoration:\n BoxDecoration(color: color, shape: BoxShape.circle),\n ),\n Container(\n width: 120,\n alignment: Alignment.center,\n height: 50,\n child: Text(\n colorString(color),\n style: TextStyle(color: Colors.black),\n ),\n ),\n ],\n ),\n ),\n body: Container(\n alignment: Alignment.center,\n height: 50,\n color: color,\n child: Text(\n colorString(color),\n style: TextStyle(color: Colors.white, shadows: [\n Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2)\n ]),\n ),\n ));\n }\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":44,"name":"RangeSlider基本使用","priority":1,"subtitle":" \n【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)】","code":"import 'package:flutter/material.dart';\nclass CustomRangeSlider extends StatefulWidget {\n @override\n _CustomRangeSliderState createState() => _CustomRangeSliderState();\n}\n\nclass _CustomRangeSliderState extends State {\n RangeValues _rangeValues = RangeValues(90, 270);\n\n @override\n Widget build(BuildContext context) {\n return RangeSlider(\n values: _rangeValues,\n divisions: 180,\n min: 0.0,\n max: 360.0,\n labels: RangeLabels(\"${_rangeValues.start.toStringAsFixed(1)}\",\n \"${_rangeValues.end.toStringAsFixed(1)}\"),\n activeColor: Colors.orangeAccent,\n inactiveColor: Colors.green.withAlpha(99),\n onChangeStart: (value) {\n print('开始滑动:$value');\n },\n onChangeEnd: (value) {\n print('滑动结束:$value');\n },\n onChanged: (value) {\n setState(() {\n _rangeValues = value;\n });\n });\n }\n}\n"},{"id":null,"widgetId":136,"name":"YearPicker基本使用","priority":1,"subtitle":" \n【selectedDate】 : 选中日期 【DateTime】\n【firstDate】 : 最前日期限制 【DateTime】\n【lastDate】 : 最后日期限制 【DateTime】\n【onChanged】 : 点击回调 【Function(DateTime)】","code":"import 'package:flutter/material.dart';\nclass CustomYearPicker extends StatefulWidget {\n @override\n _CustomYearPickerState createState() => _CustomYearPickerState();\n}\n\nclass _CustomYearPickerState extends State {\n DateTime _date = DateTime.now();\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height:150,\n child: YearPicker(\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":92,"name":"SizeTransition基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【axis】 : 轴向*2 【Axis】\n【sizeFactor】 : 动画 【Animation】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomSizeTransition extends StatefulWidget {\n @override\n _CustomSizeTransitionState createState() => _CustomSizeTransitionState();\n}\n\nclass _CustomSizeTransitionState extends State\n with SingleTickerProviderStateMixin {\n AnimationController _ctrl;\n\n @override\n void initState() {\n _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 1));\n _ctrl.forward();\n super.initState();\n }\n\n @override\n void dispose() {\n _ctrl.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onTap: () {\n setState(() {\n _ctrl.reset();\n _ctrl.forward();\n });\n },\n child: Wrap(\n runSpacing: 20,\n children: [\n SizeTransition(\n axis: Axis.horizontal,\n sizeFactor: CurvedAnimation(parent: _ctrl, curve: Curves.linear),\n child: Container(\n width: MediaQuery.of(context).size.width,\n color: Colors.orange,\n child: Icon(Icons.android, color: Colors.green, size: 80)),\n ),\n SizeTransition(\n axis: Axis.vertical,\n sizeFactor: CurvedAnimation(parent: _ctrl, curve: Curves.linear),\n child: Container(\n width: MediaQuery.of(context).size.width,\n color: Colors.orange,\n child: Icon(Icons.android, color: Colors.green, size: 80)),\n ),\n ],\n ),\n );\n }\n}\n"},{"id":null,"widgetId":90,"name":"RotationTransition基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【turns】 : 是否消失 【Animation】","code":"import 'package:flutter/material.dart';\nclass CustomRotationTransition extends StatefulWidget {\n @override\n _CustomRotationTransitionState createState() => _CustomRotationTransitionState();\n}\n\nclass _CustomRotationTransitionState extends State with SingleTickerProviderStateMixin{\n\n AnimationController _ctrl;\n\n @override\n void initState() {\n _ctrl= AnimationController(vsync: this,duration: Duration(seconds: 2));\n _ctrl.forward();\n super.initState();\n }\n@override\n void dispose() {\n _ctrl.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n\n return GestureDetector(\n onTap: () {\n setState(() {\n _ctrl.reset();\n _ctrl.forward();\n });\n },\n child: Container(\n color: Colors.grey.withAlpha(22),\n width: 100,\n height: 100,\n child: RotationTransition(\n turns: CurvedAnimation(parent: _ctrl, curve: Curves.linear),\n child: Icon(Icons.android,color: Colors.green,size: 60),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":116,"name":"AnimatedSwitcher基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【duration】 : 动画时长 【Duration】\n【switchOutCurve】 : 切出曲线 【Curves】\n【switchInCurve】 : 切入曲线 【Curves】\n【switchInCurve】 : 切入曲线 【Curves】\n【transitionBuilder】 : 动画构造器 【Widget Function(Widget, Animation)】","code":"import 'package:flutter/material.dart';\nclass CustomAnimatedSwitcher extends StatefulWidget {\n @override\n _CustomAnimatedSwitcherState createState() => _CustomAnimatedSwitcherState();\n}\n\nclass _CustomAnimatedSwitcherState extends State {\n int _count = 0;\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Wrap(\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n _buildMinusBtn(),\n SizedBox(width:80,child: _buildAnimatedSwitcher(context)),\n _buildAddBtn()\n ],\n ),\n );\n }\n\n Widget _buildAnimatedSwitcher(BuildContext context) =>\n AnimatedSwitcher(\n duration: const Duration(milliseconds: 400),\n transitionBuilder: (Widget child, Animation animation) =>\n ScaleTransition(\n child: RotationTransition(turns: animation, child: child),\n scale: animation),\n child: Text(\n '$_count',\n key: ValueKey(_count),\n style: Theme.of(context).textTheme.display3,\n ),\n );\n\n Widget _buildMinusBtn() {\n return MaterialButton(\n padding: EdgeInsets.all(0),\n textColor: Color(0xffFfffff),\n elevation: 3,\n color: Colors.red,\n highlightColor: Color(0xffF88B0A),\n splashColor: Colors.red,\n child: Icon(\n Icons.remove,\n color: Colors.white,\n ),\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onPressed: () => setState(() => _count -= 1));\n }\n\n Widget _buildAddBtn() => MaterialButton(\n padding: EdgeInsets.all(0),\n textColor: Color(0xffFfffff),\n elevation: 3,\n color: Colors.blue,\n highlightColor: Color(0xffF88B0A),\n splashColor: Colors.red,\n child: Icon(\n Icons.add,\n color: Colors.white,\n ),\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onPressed: () => setState(() => _count += 1));\n}\n"},{"id":null,"widgetId":38,"name":"可从资源文件和网络加载图片","priority":1,"subtitle":" \nImage.asset加载资源图片,","code":"import 'package:flutter/material.dart';\nclass LoadImage extends StatelessWidget {\n final assetsImagePath = \"assets/images/icon_head.png\";\n final assetsGif = \"assets/images/pica.gif\";\n final netImageUrl = \"https://user-gold-cdn.xitu.io\"\n \"/2019/7/24/16c225e78234ec26?\"\n \"imageView2/1/w/1304/h/734/q/85/format/webp/interlace/1\";\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n children: [\n _loadFromAssets(),\n _loadFromNet(),\n ],\n );\n }\n\n Widget _loadFromAssets() => Wrap(\n spacing: 10,\n children: [\n Image.asset(assetsImagePath, height: 80, width: 80),\n Image.asset(assetsGif, height: 80, width: 80),\n ],\n );\n\n Widget _loadFromNet() => Image.network(netImageUrl, height: 80);\n}\n"},{"id":null,"widgetId":38,"name":"图片颜色及混合模式","priority":4,"subtitle":" \n【color】 : 颜色 【Color】\n【colorBlendMode】: 混合模式*29 【BlendMode】","code":"import 'package:flutter/material.dart';\nclass BlendModeImage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: BlendMode.values\n .toList()\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 60,\n height: 60,\n color: Colors.red,\n child: Image(\n image: AssetImage(\"assets/images/icon_head.png\"),\n color: Colors.blue.withAlpha(88),\n colorBlendMode: mode)),\n Text(mode.toString().split(\".\")[1])\n ]))\n .toList(),\n );\n }\n}"},{"id":null,"widgetId":38,"name":"图片对齐模式","priority":3,"subtitle":" \n【alignment】 : 颜色 【AlignmentGeometry】\n 常用Alignment类的九个静态常量,但也可定制位置","code":"import 'package:flutter/material.dart';\nclass AlignmentImage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var alignment = [\n Alignment.center,\n Alignment.centerLeft,\n Alignment.centerRight,\n Alignment.topCenter,\n Alignment.topLeft,\n Alignment.topRight,\n Alignment.bottomCenter,\n Alignment.bottomLeft,\n Alignment.bottomRight\n ]; //测试数组\n var imgLi = alignment\n .map((alignment) => //生成子Widget列表\n Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 90,\n height: 60,\n color: Colors.grey.withAlpha(88),\n child: Image(\n image: AssetImage(\"assets/images/wy_30x20.jpg\"),\n alignment: alignment,\n )),\n Text(alignment.toString())\n ]))\n .toList();\n var imageAlignment = Wrap(children: imgLi);\n return imageAlignment;\n }\n}"},{"id":null,"widgetId":38,"name":"图片实现局部放大","priority":6,"subtitle":" \n【centerSlice】 : 保留的区域 【Rect】","code":"import 'package:flutter/material.dart';\nclass CenterSliceImage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: 300,\n height: 80,\n child: Image.asset(\n \"assets/images/right_chat.png\",\n centerSlice: Rect.fromLTRB(9, 27, 60, 27 + 1.0),\n fit: BoxFit.fill,\n ),\n );\n }\n}\n"},{"id":null,"widgetId":38,"name":"图片重复模式","priority":5,"subtitle":" \n【repeat】 : 重复模式*4 【ImageRepeat】","code":"import 'package:flutter/material.dart';\nclass RepeatImage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Wrap(\n children: ImageRepeat.values\n .toList()\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 150,\n height: 60,\n color: Colors.red,\n child: Image(\n image: AssetImage(\"assets/images/wy_30x20.jpg\"),\n repeat: mode)),\n Text(mode.toString().split(\".\")[1])\n ]))\n .toList(),\n );\n }\n}"},{"id":null,"widgetId":38,"name":"图片的适应模式","priority":2,"subtitle":" \n【fit】 : 适应模式*7 【BoxFit】,","code":"import 'package:flutter/material.dart';\nclass FitImage extends StatefulWidget {\n @override\n _FitImageState createState() => _FitImageState();\n}\n\nclass _FitImageState extends State {\n bool _smallImage = false;\n\n @override\n Widget build(BuildContext context) {\n var imageLi = BoxFit.values\n .toList()\n .map((mode) => Column(children: [\n Container(\n margin: EdgeInsets.all(5),\n width: 100,\n height: 80,\n color: Colors.grey.withAlpha(88),\n child: Image(\n image: AssetImage(!_smallImage\n ? \"assets/images/wy_300x200.jpg\"\n : \"assets/images/wy_30x20.jpg\"),\n fit: mode)),\n Text(mode.toString().split(\".\")[1])\n ]))\n .toList();\n\n return Wrap(\n children: [...imageLi, _buildSwitch()],\n );\n }\n\n Widget _buildSwitch() {\n return Container(\n alignment: Alignment.center,\n width: 200,\n height: 100,\n child: Wrap(\n crossAxisAlignment: WrapCrossAlignment.center,\n children: [\n Text(\"使用小图\"),\n Switch(\n value: _smallImage,\n onChanged: (b) => setState(() => _smallImage = b)),\n ],\n ),\n );\n }\n}\n"},{"id":null,"widgetId":351,"name":"constrained属性测试","priority":2,"subtitle":"【constrained】 : 受约束的 【bool】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass InteractiveViewerDemo2 extends StatelessWidget {\n\n Widget build(BuildContext context) {\n const int _rowCount = 20;\n const int _columnCount = 4;\n\n return Container(\n width: 300,\n height: 200,\n child: InteractiveViewer(\n constrained: false,\n scaleEnabled: false,\n child: Table(\n columnWidths: {\n for (int column = 0; column < _columnCount; column += 1)\n column: const FixedColumnWidth(150.0),\n },\n children: buildRows(_rowCount, _columnCount),\n ),\n ),\n );\n }\n\n List buildRows(int rowCount, int columnCount) {\n return [\n for (int row = 0; row < rowCount; row += 1)\n TableRow(\n children: [\n for (int column = 0; column < columnCount; column += 1)\n Container(\n margin: EdgeInsets.all(2),\n height: 50,\n alignment: Alignment.center,\n color: _colorful(row,column),\n child: Text('($row,$column)',style: TextStyle(fontSize: 20,color: Colors.white),),\n ),\n ],\n ),\n ];\n }\n\n final colors = [Colors.red,Colors.yellow,Colors.blue,Colors.green];\n final colors2 = [Colors.yellow,Colors.blue,Colors.green,Colors.red];\n\n _colorful(int row, int column ) => row % 2==0?colors[column]:colors2[column];\n}\n"},{"id":null,"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】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass InteractiveViewerDemo extends StatelessWidget {\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 150,\n color: Colors.grey.withAlpha(33),\n child: InteractiveViewer(\n boundaryMargin: EdgeInsets.all(40.0),\n maxScale: 2.5,\n minScale: 0.3,\n panEnabled: true,\n scaleEnabled: true,\n child: Container(\n child: Image.asset('assets/images/caver.jpeg'),\n ),\n onInteractionStart: _onInteractionStart,\n onInteractionUpdate: _onInteractionUpdate,\n onInteractionEnd: _onInteractionEnd,\n ),\n );\n }\n\n void _onInteractionStart(ScaleStartDetails details) {\n print('onInteractionStart----' + details.toString());\n }\n\n void _onInteractionUpdate(ScaleUpdateDetails details) {\n print('onInteractionUpdate----' + details.toString());\n }\n\n void _onInteractionEnd(ScaleEndDetails details) {\n print('onInteractionEnd----' + details.toString());\n }\n}\n"},{"id":null,"widgetId":351,"name":"变换控制器的使用","priority":3,"subtitle":"【transformationController】 : 变换控制器 【TransformationController】","code":"import 'dart:math';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass InteractiveViewerDemo3 extends StatefulWidget {\n @override\n _InteractiveViewerDemo3State createState() => _InteractiveViewerDemo3State();\n}\n\nclass _InteractiveViewerDemo3State extends State\n with SingleTickerProviderStateMixin {\n final TransformationController _transformationController =\n TransformationController();\n Animation _animationReset;\n AnimationController _controllerReset;\n\n void _onAnimateReset() {\n _transformationController.value = _animationReset.value;\n if (!_controllerReset.isAnimating) {\n _animationReset?.removeListener(_onAnimateReset);\n _animationReset = null;\n _controllerReset.reset();\n }\n }\n\n void _animateResetInitialize() {\n _controllerReset.reset();\n _animationReset = Matrix4Tween(\n begin: _transformationController.value,\n end: Matrix4.identity(),\n ).animate(_controllerReset);\n _animationReset.addListener(_onAnimateReset);\n _controllerReset.forward();\n }\n\n void _animateResetStop() {\n _controllerReset.stop();\n _animationReset?.removeListener(_onAnimateReset);\n _animationReset = null;\n _controllerReset.reset();\n }\n\n void _onInteractionStart(ScaleStartDetails details) {\n if (_controllerReset.status == AnimationStatus.forward) {\n _animateResetStop();\n }\n }\n\n @override\n void initState() {\n super.initState();\n _controllerReset = AnimationController(\n vsync: this,\n duration: const Duration(milliseconds: 400),\n );\n }\n\n @override\n void dispose() {\n _controllerReset.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n direction: Axis.vertical,\n spacing: 10,\n crossAxisAlignment: WrapCrossAlignment.center,\n alignment: WrapAlignment.center,\n children: [\n Container(\n height: 150,\n color: Colors.grey.withAlpha(33),\n child: InteractiveViewer(\n boundaryMargin: EdgeInsets.all(40),\n transformationController: _transformationController,\n minScale: 0.1,\n maxScale: 1.8,\n onInteractionStart: _onInteractionStart,\n child: Container(\n child: Image.asset('assets/images/caver.jpeg'),\n ),\n ),\n ),\n Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [\n _buildButton(),\n _buildButton2(),\n _buildButton3(),\n ],\n )\n ],\n );\n }\n\n Widget _buildButton() {\n return MaterialButton(\n child: Icon(\n Icons.refresh,\n color: Colors.white,\n ),\n color: Colors.green,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onPressed: _animateResetInitialize);\n }\n\n var _x = 0.0;\n\n Widget _buildButton2() {\n return MaterialButton(\n child: Icon(\n Icons.navigate_before,\n color: Colors.white,\n ),\n color: Colors.green,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onPressed: () {\n var temp = _transformationController.value.clone();\n temp.translate(_x - 4);\n _transformationController.value = temp;\n });\n }\n\n Widget _buildButton3() {\n return MaterialButton(\n child: Icon(\n Icons.navigate_next,\n color: Colors.white,\n ),\n color: Colors.green,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onPressed: () {\n var temp = _transformationController.value.clone();\n temp.translate(_x + 4);\n _transformationController.value = temp;\n });\n }\n}\n"},{"id":null,"widgetId":255,"name":"ValueListenableBuilder基本使用","priority":1,"subtitle":"【builder】: 组件构造器 【ValueWidgetBuilder】\n【valueListenable】: 监听值 【ValueListenable】\n【child】: 子组件 【Widget】","code":"import 'package:flutter/material.dart';\nclass ValueListenableBuilderDemo extends StatelessWidget {\n ValueListenableBuilderDemo({Key key}) : super(key: key);\n\n final ValueNotifier _counter = ValueNotifier(0);\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: Scaffold(\n appBar: AppBar(title: Text(\"ValueListenableBuilder\")),\n body: Center(\n child: Column(\n mainAxisAlignment: MainAxisAlignment.center,\n children: [\n Text('You have pushed the button this many times:'),\n ValueListenableBuilder(\n builder: _buildWithValue,\n valueListenable: _counter,\n child: const Text('I am Child!'),\n )\n ],\n ),\n ),\n floatingActionButton: FloatingActionButton(\n child: Icon(Icons.plus_one),\n onPressed: () => _counter.value += 1,\n ),\n ),\n );\n }\n\n Widget _buildWithValue(BuildContext context, int value, Widget child) {\n return Row(\n mainAxisAlignment: MainAxisAlignment.spaceAround,\n children: [\n Text('$value'),\n child,\n ],\n );\n }\n}\n"},{"id":null,"widgetId":177,"name":"ReorderableListView基本使用","priority":1,"subtitle":" \n【children】 : 子组件列表 【List】\n【header】 : 头部组件 【Widget】\n【padding】 : 内边距 【EdgeInsets】\n【onReorder】 : 调换时回调 【ReorderCallback】","code":"import 'package:flutter/material.dart';\nclass CustomReorderableListView extends StatefulWidget {\n @override\n _CustomReorderableListViewState createState() => _CustomReorderableListViewState();\n}\n\nclass _CustomReorderableListViewState extends State {\n var data = [\n Colors.yellow[50],\n Colors.yellow[100],\n Colors.yellow[200],\n Colors.yellow[300],\n Colors.yellow[400],\n Colors.yellow[500],\n Colors.yellow[600],\n Colors.yellow[700],\n Colors.yellow[800],\n Colors.yellow[900],\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 250,\n child: ReorderableListView(\n padding: EdgeInsets.all(10),\n header: Container(\n color: Colors.blue,\n alignment: Alignment.center,\n height: 50,\n child: Text('长按拖拽进行换位',style: TextStyle(color: Colors.white),)),\n onReorder: _handleReorder,\n children: data.map((color) => _buildItem(color)).toList(),\n ),\n );\n }\n\n void _handleReorder(int oldIndex, int newIndex) {\n if (oldIndex < newIndex) {\n newIndex -= 1;\n }\n\n setState(() {\n final element = data.removeAt(oldIndex);\n data.insert(newIndex, element);\n });\n\n }\n\n Widget _buildItem(Color color) {\n return Container(\n key: ValueKey(color) ,\n alignment: Alignment.center,\n height: 50,\n color: color,\n child: Text(\n colorString(color),\n style: TextStyle(color: Colors.white, shadows: [\n Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2)\n ]),\n ),\n );\n }\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":177,"name":"ReorderableListView滑动方向","priority":2,"subtitle":" \n【scrollDirection】 : 滑动方向 【Axis】\n【reverse】 : 是否反向 【bool】","code":"import 'package:flutter/material.dart';\nclass DirectionReorderableListView extends StatefulWidget {\n @override\n _DirectionReorderableListViewState createState() => _DirectionReorderableListViewState();\n}\n\nclass _DirectionReorderableListViewState extends State {\n var data = [\n Colors.yellow[50],\n Colors.yellow[100],\n Colors.yellow[200],\n Colors.yellow[300],\n Colors.yellow[400],\n Colors.yellow[500],\n Colors.yellow[600],\n Colors.yellow[700],\n Colors.yellow[800],\n Colors.yellow[900],\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n child: ReorderableListView(\n scrollDirection: Axis.horizontal,\n reverse: false,\n onReorder: _handleReorder,\n children: data.map((color) => _buildItem(color)).toList(),\n ),\n );\n }\n\n void _handleReorder(int oldIndex, int newIndex) {\n if (oldIndex < newIndex) {\n newIndex -= 1;\n }\n\n setState(() {\n final element = data.removeAt(oldIndex);\n data.insert(newIndex, element);\n });\n\n }\n\n Widget _buildItem(Color color) {\n return Container(\n key: ValueKey(color) ,\n alignment: Alignment.center,\n width: 80,\n color: color,\n child: Text(\n colorString(color),\n style: TextStyle(color: Colors.white, shadows: [\n Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2)\n ]),\n ),\n );\n }\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}"},{"id":null,"widgetId":113,"name":"DecoratedBoxTransition基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【position】 : 前/背景色 【DecorationPosition】\n【decoration】 : 动画 【Animation】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomDecoratedBoxTransition extends StatefulWidget {\n @override\n _CustomDecoratedBoxTransitionState createState() =>\n _CustomDecoratedBoxTransitionState();\n}\n\nclass _CustomDecoratedBoxTransitionState\n extends State\n with SingleTickerProviderStateMixin {\n AnimationController _ctrl;\n\n @override\n void initState() {\n _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 1));\n _ctrl.forward();\n super.initState();\n }\n\n @override\n void dispose() {\n _ctrl.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onTap: () {\n setState(() {\n _ctrl.reset();\n _ctrl.forward();\n });\n },\n child: Container(\n width: 200,\n height: 100,\n child: DecoratedBoxTransition(\n position: DecorationPosition.background,\n decoration: DecorationTween(\n begin: BoxDecoration(\n color: Colors.greenAccent,\n borderRadius: BorderRadius.all(Radius.circular(50)),\n boxShadow: [\n BoxShadow(\n offset: Offset(1, 1),\n color: Colors.purple,\n blurRadius: 3,\n spreadRadius: 1)\n ]),\n end: BoxDecoration(\n color: Colors.orange,\n borderRadius: BorderRadius.all(Radius.circular(10)),\n boxShadow: [\n BoxShadow(\n offset: Offset(1, 1),\n color: Colors.blue,\n blurRadius: 1,\n spreadRadius: 0)\n ])).animate(_ctrl),\n child: Container(\n child: Icon(Icons.android, color: Colors.white, size: 60)),\n ),\n ));\n }\n}\n"},{"id":null,"widgetId":52,"name":"ExpansionTile基本使用","priority":1,"subtitle":" \n【children】 : 展开内容 【List】\n【leading】 : 头左组件 【Widget】\n【title】 : 头中组件 【Widget】\n【trailing】 : 头尾组件 【Widget】\n【backgroundColor】 : 背景色 【Color】\n【onExpansionChanged】 : 折叠事件 【Function(bool)】\n【initiallyExpanded】 : 是否初始时展开 【bool】","code":"import 'package:flutter/material.dart';\nimport '../../StatelessWidget/RadioListTile/node1_base.dart';\nclass CustomExpansionTile extends StatefulWidget {\n @override\n _CustomExpansionTileState createState() => _CustomExpansionTileState();\n}\n\nclass _CustomExpansionTileState extends State {\n @override\n Widget build(BuildContext context) {\n return ExpansionTile(\n leading: Icon(Icons.star),\n title: Text(\"选择语言\"),\n backgroundColor: Colors.grey.withAlpha(6),\n onExpansionChanged: (value) {\n print('$value');\n },\n initiallyExpanded: false,\n children: [CustomRadioListTile()],\n );\n }\n}\n"},{"id":null,"widgetId":158,"name":"CupertinoTabScaffold基本用法","priority":1,"subtitle":" \n【tabBar】 : 页签条 【CupertinoTabBar】\n【backgroundColor】 : 背景色 【Color】\n【controller】 : 控制器 【CupertinoTabController】\n【tabBuilder】 : 页面构造器 【IndexedWidgetBuilder】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoTabScaffold extends StatefulWidget {\n @override\n _CustomCupertinoTabScaffoldState createState() =>\n _CustomCupertinoTabScaffoldState();\n}\n\nclass _CustomCupertinoTabScaffoldState\n extends State {\n var _position = 0;\n final iconsMap = {\n //底栏图标\n \"图鉴\": Icons.home, \"动态\": Icons.toys,\n \"喜欢\": Icons.favorite, \"手册\": Icons.class_,\n \"我的\": Icons.account_circle,\n };\n\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width,\n height: MediaQuery.of(context).size.height - 300,\n child: CupertinoTabScaffold(\n backgroundColor: Colors.grey.withAlpha(11),\n tabBar: _buildTabBar(),\n tabBuilder: (_, index) => _buildContent(index)),\n );\n }\n\n CupertinoTabBar _buildTabBar() => CupertinoTabBar(\n currentIndex: _position,\n onTap: (value) => setState(() => _position = value),\n items: iconsMap.keys\n .map((e) => BottomNavigationBarItem(\n icon: Icon(\n iconsMap[e],\n ),\n title: Text(e),\n ))\n .toList(),\n activeColor: Colors.blue,\n inactiveColor: Color(0xff333333),\n backgroundColor: Color(0xfff1f1f1),\n iconSize: 25.0,\n );\n\n _buildContent(int index) => Container(\n alignment: Alignment.center,\n child: Text(iconsMap.keys.toList()[index]),\n );\n}\n"},{"id":null,"widgetId":105,"name":"LongPressDraggable与DragTarget联用","priority":1,"subtitle":" \n【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)】","code":"import 'package:flutter/material.dart';\nclass CustomLongPressDraggable extends StatefulWidget {\n @override\n _CustomLongPressDraggableState createState() => _CustomLongPressDraggableState();\n}\n\nclass _CustomLongPressDraggableState extends State {\n Color _color = Colors.grey;\n String _info = 'DragTarget';\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Column(\n children: [\n Wrap(\n children: _buildColors(),\n spacing: 10,\n ),\n SizedBox(height: 20,),\n _buildDragTarget()\n ],\n ),\n );\n }\n\n List _buildColors() {\n var colors = [\n Colors.red,\n Colors.yellow,\n Colors.blue,\n Colors.green,\n Colors.orange,\n Colors.purple,\n Colors.cyanAccent\n ];\n return colors\n .map(\n (e) => LongPressDraggable(\n onDragStarted: () => setState(() => _info = '开始拖拽'),\n onDragEnd: (d) => setState(() => _info = '结束拖拽'),\n onDragCompleted: () => _info = '拖拽完成',\n onDraggableCanceled: (v, o) => _info = '拖拽取消',\n child: Container(\n width: 30,\n height: 30,\n alignment: Alignment.center,\n child: Text(\n colors.indexOf(e).toString(),\n style: TextStyle(\n color: Colors.white, fontWeight: FontWeight.bold),\n ),\n decoration: BoxDecoration(color: e, shape: BoxShape.circle),\n ),\n data: e,\n feedback: Container(\n width: 25,\n height: 25,\n decoration: BoxDecoration(color: e, shape: BoxShape.circle),\n )),\n )\n .toList();\n }\n\n Widget _buildDragTarget() {\n return DragTarget(\n onAccept: (data) => setState(() {\n _info='onAccept';\n _color = data;\n }),\n builder: (context, candidateData, rejectedData) => Container(\n width: 150.0,\n height: 50.0,\n color: _color,\n child: Center(\n child: Text(\n _info,\n style: TextStyle(color: Colors.white),\n ),\n )));\n }\n}\n"},{"id":null,"widgetId":138,"name":"CupertinoTimerPicker基本使用","priority":1,"subtitle":" \n【initialTimerDuration】 : 初始时间 【Duration】\n【minuteInterval】 : 分钟间隔数 【double】\n【secondInterval】 : 秒间隔数 【double】\n【alignment】 : 对齐方式 【AlignmentGeometry】\n【backgroundColor】 : 背景色 【Color】\n【mode】 : 模式*3 【CupertinoTimerPickerMode】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoTimerPicker extends StatefulWidget {\n @override\n _CustomCupertinoTimerPickerState createState() =>\n _CustomCupertinoTimerPickerState();\n}\n\nclass _CustomCupertinoTimerPickerState\n extends State {\n Duration _date = Duration(seconds: 30);\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Text(\n '当前时间:${_date.toString()}',\n style: TextStyle(color: Colors.grey, fontSize: 16),\n ),\n _buildInfoTitle('CupertinoTimerPickerMode.hms'),\n buildPicker(CupertinoTimerPickerMode.hms),\n _buildInfoTitle('CupertinoTimerPickerMode.hm'),\n buildPicker(CupertinoTimerPickerMode.hm),\n _buildInfoTitle('CupertinoTimerPickerMode.ms'),\n buildPicker(CupertinoTimerPickerMode.ms),\n ],\n );\n }\n\n Widget _buildInfoTitle(info) {\n return Padding(\n padding: const EdgeInsets.only(left: 20, top: 20, bottom: 5),\n child: Text(\n info,\n style: TextStyle(\n color: Colors.blue, fontSize: 16, fontWeight: FontWeight.bold),\n ),\n );\n }\n\n Widget buildPicker(CupertinoTimerPickerMode mode) {\n return Container(\n margin: EdgeInsets.all(10),\n height: 150,\n child: CupertinoTimerPicker(\n mode: mode,\n initialTimerDuration: Duration(seconds: 30),\n onTimerDurationChanged: (date) {\n print(date);\n setState(() => _date = date);\n },\n ),\n );\n }\n}\n"},{"id":null,"widgetId":93,"name":"PositionedTransition基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【rect】 : 动画 【Animation】\n PositionedTransition组件只能在Stack内起作用","code":"import 'package:flutter/material.dart';\nclass CustomPositionedTransition extends StatefulWidget {\n @override\n _CustomPositionedTransitionState createState() =>\n _CustomPositionedTransitionState();\n}\n\nclass _CustomPositionedTransitionState extends State\n with SingleTickerProviderStateMixin {\n AnimationController _ctrl;\n\n @override\n void initState() {\n _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 2));\n _ctrl.forward();\n super.initState();\n }\n\n @override\n void dispose() {\n _ctrl.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onTap: () {\n setState(() {\n _ctrl.reset();\n _ctrl.forward();\n });\n },\n child: Container(\n color: Colors.grey.withAlpha(33),\n width: 200,\n height: 100,\n child: Stack(\n children: [\n PositionedTransition(\n rect: RelativeRectTween(\n begin: RelativeRect.fromLTRB(0, 50, 150, 100),\n end: RelativeRect.fromLTRB(60, 0, 150, -50),\n ).animate(_ctrl),\n child: Icon(\n Icons.android,\n color: Colors.green,\n size: 50,\n ),\n )\n ],\n ),\n ));\n }\n}\n"},{"id":null,"widgetId":103,"name":"Draggable基本使用","priority":1,"subtitle":" \n【child】 : 孩子 【Widget】\n【feedback】 : 拖拽时的孩子 【Widget】\n【axis】 : 拖动的轴 【Axis】","code":"import 'package:flutter/material.dart';\nclass CustomDraggable extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var axis = [null, Axis.vertical, Axis.horizontal];\n return Wrap(\n spacing: 30,\n children: axis\n .map((e) => Draggable(\n axis: e,\n child: Container(\n width: 30,\n height: 30,\n alignment: Alignment.center,\n decoration: BoxDecoration(\n color: Colors.blue, shape: BoxShape.circle),\n ),\n feedback: Container(\n width: 30,\n height: 30,\n decoration: BoxDecoration(\n color: Colors.red, shape: BoxShape.circle),\n ),\n ))\n .toList());\n }\n}"},{"id":null,"widgetId":103,"name":"Draggable其他使用","priority":3,"subtitle":" \n可以根据拖拽来处理一些事件。如删除、查询、弹框等","code":"import 'package:flutter/material.dart';\nclass DeleteDraggable extends StatefulWidget {\n @override\n _DeleteDraggableState createState() => _DeleteDraggableState();\n}\n\nclass _DeleteDraggableState extends State {\n List colors = [\n Colors.red,\n Colors.yellow,\n Colors.blue,\n Colors.green,\n Colors.orange,\n Colors.purple,\n Colors.cyanAccent\n ];\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Column(\n children: [\n Wrap(\n children: _buildColors(),\n spacing: 10,\n ),\n SizedBox(\n height: 20,\n ),\n _buildDragTarget()\n ],\n ),\n );\n }\n\n Widget _buildDragTarget() {\n return DragTarget(\n onAccept: (data) {\n setState(() {\n colors.removeAt(data);\n });\n },\n onWillAccept: (data) => data != null,\n builder: (context, candidateData, rejectedData) => Container(\n width: 50.0,\n height: 50.0,\n decoration:\n BoxDecoration(color: Colors.red, shape: BoxShape.circle),\n child: Center(\n child: Icon(Icons.delete_sweep, color: Colors.white),\n )));\n }\n\n List _buildColors() => colors\n .map(\n (e) => Draggable(\n child: Container(\n width: 30,\n height: 30,\n alignment: Alignment.center,\n child: Text(\n colors.indexOf(e).toString(),\n style:\n TextStyle(color: Colors.white, fontWeight: FontWeight.bold),\n ),\n decoration: BoxDecoration(color: e, shape: BoxShape.circle),\n ),\n data: colors.indexOf(e),\n feedback: Container(\n width: 25,\n height: 25,\n decoration: BoxDecoration(\n color: e.withAlpha(100), shape: BoxShape.circle),\n )),\n )\n .toList();\n}\n"},{"id":null,"widgetId":103,"name":"Draggable与DragTarget联用","priority":2,"subtitle":" \n【data】 : 数据 【T】\n【onDragStarted】 : 开始拖拽 【Function()】\n【onDragEnd】 : 结束拖拽 【Function(DraggableDetails)】\n【onDragCompleted】 : 拖拽完成 【Function()】\n【onDraggableCanceled】 : 拖拽取消 【Function(Velocity,Offset)】\n【onChanged】 : 改变时回调 【Function(T)】","code":"import 'package:flutter/material.dart';\nclass DraggablePage extends StatefulWidget {\n @override\n _DraggablePageState createState() => _DraggablePageState();\n}\n\nclass _DraggablePageState extends State {\n Color _color = Colors.grey;\n String _info = 'DragTarget';\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Column(\n children: [\n Wrap(\n children: _buildColors(),\n spacing: 10,\n ),\n SizedBox(\n height: 20,\n ),\n _buildDragTarget()\n ],\n ),\n );\n }\n\n List _buildColors() {\n var colors = [\n Colors.red,\n Colors.yellow,\n Colors.blue,\n Colors.green,\n Colors.orange,\n Colors.purple,\n Colors.cyanAccent\n ];\n return colors\n .map(\n (e) => Draggable(\n onDragStarted: () => setState(() => _info = '开始拖拽'),\n onDragEnd: (d) => setState(() => _info = '结束拖拽'),\n onDragCompleted: () => _info = '拖拽完成',\n onDraggableCanceled: (v, o) => _info = '拖拽取消',\n child: Container(\n width: 30,\n height: 30,\n alignment: Alignment.center,\n child: Text(\n colors.indexOf(e).toString(),\n style: TextStyle(\n color: Colors.white, fontWeight: FontWeight.bold),\n ),\n decoration: BoxDecoration(color: e, shape: BoxShape.circle),\n ),\n data: e,\n feedback: Container(\n width: 25,\n height: 25,\n decoration: BoxDecoration(color: e, shape: BoxShape.circle),\n )),\n )\n .toList();\n }\n\n Widget _buildDragTarget() {\n return DragTarget(\n onLeave: (data) => print(\"onLeave: data = $data \"),\n onAccept: (data) {\n print(\"onAccept: data = $data \");\n setState(() {\n _color = data;\n });\n },\n onWillAccept: (data) {\n print(\"onWillAccept: data = $data \");\n return data != null;\n },\n builder: (context, candidateData, rejectedData) => Container(\n width: 150.0,\n height: 50.0,\n color: _color,\n child: Center(\n child: Text(\n _info,\n style: TextStyle(color: Colors.white),\n ),\n )));\n }\n}\n"},{"id":null,"widgetId":121,"name":"AnimatedPositioned基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【duration】 : 动画时长 【Duration】\n【onEnd】 : 动画结束回调 【Function()】\n【curve】 : 动画曲线 【Duration】\n【top】 : 到父顶距离 【double】\n【right】 : 到父右距离 【double】\n【left】 : 到父左距离 【double】\n【bottom】 : 到父底距离 【double】","code":"import 'package:flutter/material.dart';\nclass CustomAnimatedPositioned extends StatefulWidget {\n @override\n _CustomAnimatedPositionedState createState() =>\n _CustomAnimatedPositionedState();\n}\n\nclass _CustomAnimatedPositionedState extends State {\n final startTop = 0.0;\n final endTop = 30.0;\n\n var _top = 0.0;\n\n @override\n void initState() {\n _top = startTop;\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildSwitch(),\n Container(\n color: Colors.grey.withAlpha(33),\n width: 200,\n height: 100,\n child: Stack(\n children: _buildChildren(),\n ),\n ),\n ],\n );\n }\n\n List _buildChildren() => [\n AnimatedPositioned(\n duration: Duration(seconds: 1),\n top: _top,\n left: _top * 4,\n child: Icon(\n Icons.android,\n color: Colors.green,\n size: 50,\n ),\n ),\n AnimatedPositioned(\n duration: Duration(seconds: 1),\n top: 50 - _top,\n left: 150 - _top * 4,\n child: Icon(\n Icons.android,\n color: Colors.red,\n size: 50,\n ),\n )\n ];\n\n Widget _buildSwitch() => Switch(\n value: _top == endTop,\n onChanged: (v) {\n setState(() {\n _top = v ? endTop : startTop;\n });\n });\n}\n"},{"id":null,"widgetId":51,"name":"ExpandIcon基本使用","priority":1,"subtitle":" \n【isExpanded】 : 是否展开 【bool】\n【padding】 : 内边距 【EdgeInsetsGeometry】,\n【size】 : 图标大小 【double】\n【color】 : 不展开时颜色 【Color】\n【expandedColor】 : 展开时颜色 【Color】\n【onPressed】 : 点击事件 【Function(bool)】","code":"import 'package:flutter/material.dart';\nclass CustomExpandIcon extends StatefulWidget {\n @override\n _CustomExpandIconState createState() => _CustomExpandIconState();\n}\n\nclass _CustomExpandIconState extends State {\n var _closed = true;\n\n @override\n Widget build(BuildContext context) {\n return ExpandIcon(\n isExpanded: _closed,\n padding: EdgeInsets.all(5),\n size: 30,\n color: Colors.blue,\n expandedColor: Colors.orangeAccent,\n onPressed: (value) => setState(() => _closed = !_closed),\n );\n }\n}\n"},{"id":null,"widgetId":199,"name":"TextFormField基本使用","priority":1,"subtitle":" \n 基本属性和TextField一致,详见之\n【validator】 : 验证函数 【FormFieldValidator 】\n【onFieldSubmitted】 : 提交回调 【ValueChanged】\n【onSaved】 : 表单save时回调 【FormFieldSetter】","code":"import 'package:flutter/material.dart';\nclass CustomTextFormField extends StatefulWidget {\n @override\n _CustomTextFormFieldState createState() => _CustomTextFormFieldState();\n}\n\nclass _CustomTextFormFieldState extends State {\n GlobalKey _formKey = GlobalKey();\n\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Form(\n key: _formKey,\n child:\n Stack(\n alignment: Alignment.centerRight,\n children: [\n Container(\n width: 350,\n child: UnconstrainedBox(\n child: Container(\n width: 200,\n height: 70,\n child: TextFormField(\n style: TextStyle(textBaseline: TextBaseline.alphabetic),\n decoration: InputDecoration(\n border: OutlineInputBorder(),\n labelText: 'username',\n ),\n validator: _validateUsername,\n onFieldSubmitted: _onFieldSubmitted,\n onSaved: _onSaved,\n ),\n ),\n ),\n ),\n Positioned(\n top: 0, right: 0, child: _buildSubmitButton(context)),\n ],\n ),\n ),\n );\n }\n\n String _validateUsername(value) {\n if (value.isEmpty) {\n return '用户名不能为空';\n }\n return null;\n }\n _onSaved(value){\n print('onSaved:'+value);\n }\n\n void _onFieldSubmitted(value) {\n print('onFieldSubmitted:'+value);\n }\n\n RaisedButton _buildSubmitButton(BuildContext context) {\n return RaisedButton(\n color: Colors.blue,\n shape: CircleBorder(\n side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)),\n ),\n onPressed: _onSubmit,\n child: Icon(\n Icons.check,\n color: Colors.white,\n ),\n );\n }\n\n _onSubmit(){\n _formKey.currentState.save();\n if (_formKey.currentState.validate()) {\n FocusScope.of(context).requestFocus(FocusNode());\n }\n }\n}"},{"id":null,"widgetId":253,"name":"Scrollable的基本使用","priority":1,"subtitle":"【viewportBuilder】 : 视口构造器 【ViewportBuilder】\n【axisDirection】: 滑动方向 【AxisDirection】\n【controller】: 滑动控制器 【ScrollController】\n【dragStartBehavior】: t拖动行为 【DragStartBehavior】\n【physics】: 滚动现象 【ScrollPhysics】\n【color】: 子组件 【Color】","code":"import 'package:flutter/gestures.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/rendering.dart';\nclass ScrollableDemo extends StatelessWidget {\n final data = List.generate(32, (i) => Color(0xFF6600FF - 2 * i));\n\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 250,\n child: Scrollable(\n axisDirection: AxisDirection.down,\n physics: BouncingScrollPhysics(),\n dragStartBehavior: DragStartBehavior.start,\n viewportBuilder: (ctx, position) => Viewport(\n cacheExtent: 200,\n cacheExtentStyle: CacheExtentStyle.pixel,\n offset: position,\n slivers: [_buildSliverList()],\n ),\n ),\n );\n }\n\n Widget _buildSliverList() => SliverList(\n delegate: SliverChildBuilderDelegate(\n (_, int index) => Container(\n margin: EdgeInsets.only(top: 1),\n alignment: Alignment.center,\n width: 100,\n height: 60,\n color: data[index],\n child: Text(\n colorString(data[index]),\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 childCount: data.length),\n );\n\n String colorString(Color color) =>\n \"#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}\";\n}\n"},{"id":null,"widgetId":40,"name":"Switch基础用法","priority":1,"subtitle":" \n【inactiveThumbColor】 : 未选中小圈颜色 【Color】\n【inactiveTrackColor】 : 未选中滑槽颜色 【Color】\n【activeColor】 : 选中时小圈颜色 【Color】\n【activeTrackColor】 : 选中时滑槽颜色 【Color】\n【onChanged】 : 切换回调 【Function(double)】\"\n onChanged时,回调true、null、false三种状态","code":"import 'package:flutter/material.dart';\nclass CustomSwitch extends StatefulWidget {\n @override\n _CustomSwitchState createState() => _CustomSwitchState();\n}\n\nclass _CustomSwitchState extends State {\n final colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green];\n bool _checked = false;\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n children: colors\n .map((e) =>\n Switch(\n value: _checked,\n inactiveThumbColor: e,\n inactiveTrackColor: Colors.grey.withAlpha(88),\n activeColor: Colors.green,\n activeTrackColor: Colors.orange,\n onChanged: (v) {\n setState(() => _checked = v);\n }))\n .toList(),\n );\n }\n}\n\n"},{"id":null,"widgetId":40,"name":"Switch图片","priority":2,"subtitle":" \n【inactiveThumbImage】 : 未选中小圈图片 【ImageProvider】\n【activeThumbImage】 : 选中时滑槽颜色 【ImageProvider】","code":"import 'package:flutter/material.dart';\nclass ImageSwitch extends StatefulWidget {\n @override\n _ImageSwitchState createState() => _ImageSwitchState();\n}\n\nclass _ImageSwitchState extends State {\n final imgs = [\n \"assets/images/head_icon/icon_5.jpg\",\n \"assets/images/head_icon/icon_6.jpg\",\n \"assets/images/head_icon/icon_7.jpg\",\n \"assets/images/head_icon/icon_8.jpg\"];\n bool _checked = false;\n\n @override\n Widget build(BuildContext context) {\n return Wrap(\n spacing: 10,\n children: imgs\n .map((e) =>\n Switch(\n value: _checked,\n inactiveThumbImage: AssetImage(e),\n activeThumbImage: AssetImage('assets/images/pica.gif'),\n onChanged: (v) {\n setState(() => _checked = v);\n }))\n .toList(),\n );\n }\n}\n\n"},{"id":null,"widgetId":232,"name":"Navigator基本用法","priority":1,"subtitle":" \n【initialRoute】 : 最初显示路由 【String】\n【onGenerateRoute】 : 路由生成器 【RouteFactory】\n【observers】 : 路由监听器 【List】\n【onPopPage】 : 出栈回调 【PopPageCallback】","code":"import 'package:flutter/material.dart';\nclass NavigatorDemo extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n height: 200,\n width: 300,\n child: Navigator(\n onPopPage: _onPopPage,\n initialRoute: '/home-content',\n onGenerateRoute: _onGenerateRoute,\n observers: [TolyNavigatorObservers()],\n ),\n );\n }\n\n Route _onGenerateRoute(RouteSettings settings) {\n switch (settings.name) {\n case '/home-content':\n return MaterialPageRoute(\n builder: (_) => HomeContent(), settings: settings);\n case \"/red\":\n return MaterialPageRoute(builder: (_) => RedPage(), settings: settings);\n case \"/yellow\":\n return MaterialPageRoute(\n builder: (_) => YellowPage(), settings: settings);\n case \"/green\":\n return MaterialPageRoute(\n builder: (_) => GreenPage(), settings: settings);\n default:\n return MaterialPageRoute(\n builder: (_) => HomeContent(), settings: settings);\n }\n }\n\n bool _onPopPage(Route route, result) {\n print('----_onPopPage-----');\n return true;\n }\n}\n\n//路由监听器\nclass TolyNavigatorObservers extends NavigatorObserver {\n @override\n void didPush(Route route, Route previousRoute) {\n print(\n '--didPush:--route:--${route.settings}--previousRoute:--${previousRoute?.settings}');\n }\n\n @override\n void didStopUserGesture() {\n print('--didStopUserGesture:--');\n }\n\n @override\n void didStartUserGesture(Route route, Route previousRoute) {\n print(\n '--didStartUserGesture:--route:--${route.settings}--previousRoute:--${previousRoute.settings}');\n }\n\n @override\n void didReplace({Route newRoute, Route oldRoute}) {\n print(\n '--didReplace:--newRoute:--${newRoute.settings}--oldRoute:--${oldRoute.settings}');\n }\n\n @override\n void didRemove(Route route, Route previousRoute) {\n print(\n '--didRemove:--route:--${route.settings}--previousRoute:--${previousRoute.settings}');\n }\n\n @override\n void didPop(Route route, Route previousRoute) {\n print(\n '--didPop:--route:--${route.settings}--previousRoute:--${previousRoute.settings}');\n }\n}\n\nclass HomeContent extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n child: Column(\n children: [\n Row(\n mainAxisAlignment: MainAxisAlignment.spaceEvenly,\n children: [\n RaisedButton(\n color: Colors.red,\n onPressed: () {\n Navigator.pushNamed(context, '/red');\n },\n ),\n RaisedButton(\n color: Colors.yellow,\n onPressed: () {\n Navigator.pushNamed(context, '/yellow');\n },\n ),\n RaisedButton(\n color: Colors.green,\n onPressed: () {\n Navigator.pushNamed(context, '/green');\n },\n )\n ],\n ),\n ],\n ),\n );\n }\n}\n\nclass RedPage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n appBar: AppBar(\n title: Text(\"RedPage\"),\n ),\n body: Container(\n color: Colors.red,\n ),\n );\n }\n}\n\nclass YellowPage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n appBar: AppBar(\n title: Text(\"YellowPage\"),\n ),\n body: Container(\n color: Colors.yellow,\n ),\n );\n }\n}\n\nclass GreenPage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Scaffold(\n appBar: AppBar(\n title: Text(\"GreenPage\"),\n ),\n body: Container(\n color: Colors.green,\n ),\n );\n }\n}\n"},{"id":null,"widgetId":43,"name":"CupertinoSlider基本使用","priority":1,"subtitle":" \n【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)】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoSlider extends StatefulWidget {\n @override\n _CustomCupertinoSliderState createState() => _CustomCupertinoSliderState();\n}\n\nclass _CustomCupertinoSliderState extends State {\n double _value = 0.0;\n\n @override\n Widget build(BuildContext context) {\n return Column(\n mainAxisSize: MainAxisSize.min,\n\n children: [\n Text('当前值:${_value.toStringAsFixed(1)}'),\n CupertinoSlider(\n value: _value,\n divisions: 180,\n min: 0.0,\n max: 360.0,\n activeColor: Colors.green,\n thumbColor: Colors.white,\n onChangeStart: (value) {\n print('开始滑动:$value');\n },\n onChangeEnd: (value) {\n print('滑动结束:$value');\n },\n onChanged: (value) {\n setState(() {\n _value = value;\n });\n }),\n ],\n );\n }\n}\n"},{"id":null,"widgetId":63,"name":"CupertinoTabBar基本用法","priority":1,"subtitle":" \n【currentIndex】 : 当前激活索引 【Widget】\n【items】 : 条目组件 【Widget】\n【backgroundColor】 : 背景色 【Color】\n【inactiveColor】 : 非激活色 【Color】\n【activeColor】 : 激活色 【Color】\n【iconSize】 : 图标大小 【double】\n【border】 : 边线 【Border】\n【onTap】 : 点击事件 【Function(int)】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomCupertinoTabBar extends StatefulWidget {\n @override\n _CustomCupertinoTabBarState createState() => _CustomCupertinoTabBarState();\n}\n\nclass _CustomCupertinoTabBarState extends State {\n var _position = 0;\n final iconsMap = {\n //底栏图标\n \"图鉴\": Icons.home, \"动态\": Icons.toys,\n \"喜欢\": Icons.favorite, \"手册\": Icons.class_,\n \"我的\": Icons.account_circle,\n };\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildContent(context),\n _buildTabBar(),\n ],\n );\n }\n\n Widget _buildTabBar() {\n return CupertinoTabBar(\n currentIndex: _position,\n onTap: (value) => setState(() => _position = value),\n items: iconsMap.keys\n .map((e) => BottomNavigationBarItem(\n icon: Icon(\n iconsMap[e],\n ),\n title: Text(e),\n ))\n .toList(),\n activeColor: Colors.blue,\n inactiveColor: Color(0xff333333),\n backgroundColor: Color(0xfff1f1f1),\n iconSize: 25.0,\n );\n }\n\n Widget _buildContent(BuildContext context) {\n return Container(\n alignment: Alignment.center,\n width: MediaQuery.of(context).size.width,\n height: 150,\n color: Color(0xffE7F3FC),\n child: Text(\n iconsMap.keys.toList()[_position],\n style: TextStyle(color: Colors.blue, fontSize: 24),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":122,"name":"AnimatedPositionedDirectional基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【duration】 : 动画时长 【Duration】\n【onEnd】 : 动画结束回调 【Function()】\n【curve】 : 动画曲线 【Duration】\n【top】 : 到父顶距离 【double】\n【end】 : 到父右距离 【double】\n【start】 : 到父左距离 【double】\n【bottom】 : 到父底距离 【double】","code":"import 'package:flutter/material.dart';\nclass CustomAnimatedPositionedDirectional extends StatefulWidget {\n @override\n _CustomAnimatedPositionedDirectionalState createState() =>\n _CustomAnimatedPositionedDirectionalState();\n}\n\nclass _CustomAnimatedPositionedDirectionalState\n extends State {\n final startTop = 0.0;\n final endTop = 30.0;\n\n var _top = 0.0;\n\n @override\n void initState() {\n _top = startTop;\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n _buildSwitch(),\n Container(\n color: Colors.grey.withAlpha(33),\n width: 200,\n height: 100,\n child: Stack(\n children: _buildChildren(),\n ),\n ),\n ],\n );\n }\n\n List _buildChildren() => [\n AnimatedPositionedDirectional(\n duration: Duration(seconds: 1),\n top: _top,\n start: _top * 4,\n child: Icon(\n Icons.android,\n color: Colors.green,\n size: 50,\n ),\n ),\n AnimatedPositionedDirectional(\n duration: Duration(seconds: 1),\n top: 50 - _top,\n start: 150 - _top * 4,\n child: Icon(\n Icons.android,\n color: Colors.red,\n size: 50,\n ),\n )\n ];\n\n Widget _buildSwitch() => Switch(\n value: _top == endTop,\n onChanged: (v) {\n setState(() {\n _top = v ? endTop : startTop;\n });\n });\n}\n"},{"id":null,"widgetId":293,"name":"MouseRegion基本使用","priority":1,"subtitle":"【onEnter】 : 移入事件 【PointerEnterEventListener】\n【onHover】: 移动事件 【PointerHoverEventListener】\n【onExit】: 移出事件 【PointerExitEventListener】","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass MouseRegionDemo extends StatefulWidget {\n @override\n _MouseRegionDemoState createState() => _MouseRegionDemoState();\n}\n\nclass _MouseRegionDemoState extends State {\n int _enterCounter = 0;\n int _exitCounter = 0;\n double x = 0.0;\n double y = 0.0;\n void _incrementEnter(PointerEvent details) {\n setState(() {\n _enterCounter++;\n });\n }\n void _incrementExit(PointerEvent details) {\n setState(() {\n _exitCounter++;\n });\n }\n void _updateLocation(PointerEvent details) {\n setState(() {\n x = details.position.dx;\n y = details.position.dy;\n });\n }\n @override\n Widget build(BuildContext context) {\n return ConstrainedBox(\n constraints: BoxConstraints.tight(Size(300.0, 200.0)),\n child: MouseRegion(\n onEnter: _incrementEnter,\n onHover: _updateLocation,\n onExit: _incrementExit,\n child: Container(\n color: Colors.lightBlueAccent,\n child: Column(\n mainAxisAlignment: MainAxisAlignment.center,\n children: [\n Text('你的鼠标移入移除信息:'),\n Text(\n '$_enterCounter Entries\\n$_exitCounter Exits',\n style: Theme.of(context).textTheme.headline4,\n ),\n Text(\n 'The cursor is here: (${x.toStringAsFixed(2)}, ${y.toStringAsFixed(2)})',\n ),\n ],\n ),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":244,"name":"EditableText基本使用","priority":1,"subtitle":"【controller】 : 控制器 【TextEditingController】\n【focusNode】 : 焦点 【FocusNode】\n【style】 : 文字样式 【TextStyle】\n【backgroundCursorColor】 : 背景游标颜色 【Color】\n【cursorColor】 : 游标颜色 【Color】\n上面五个是EditableText必须的属性,其他同TextField,此处不再列举。","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass EditableTextDemo extends StatefulWidget {\n @override\n _EditableTextDemoState createState() => _EditableTextDemoState();\n}\n\nclass _EditableTextDemoState extends State {\n final _ctrl = TextEditingController(text:'Hello Flutter Unit!');\n final _node = FocusNode();\n\n @override\n Widget build(BuildContext context) {\n return Padding(\n padding: const EdgeInsets.all(8.0),\n child: EditableText(\n controller: _ctrl,\n focusNode: _node,\n style: TextStyle(fontSize: 16,color: Colors.blue),\n cursorColor: Colors.blue,\n backgroundCursorColor: Colors.orange,\n ),\n );\n }\n}\n"},{"id":null,"widgetId":117,"name":"AnimatedList基本使用","priority":1,"subtitle":" \n【itemBuilder】 : 组件构造器 【AnimatedListItemBuilder】\n【initialItemCount】 : 子组件数量 【int】\n【scrollDirection】 : 滑动方向 【Axis】\n【controller】 : 滑动控制器 【ScrollController】\n【reverse】 : 数据是否反向 【bool】\n【padding】 : 内边距 【EdgeInsetsGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomAnimatedList extends StatefulWidget {\n @override\n _CustomAnimatedListState createState() => _CustomAnimatedListState();\n}\n\nclass _CustomAnimatedListState extends State {\n final GlobalKey _listKey = GlobalKey();\n ListModel _list;\n int _selectedItem;\n int _nextItem;\n\n @override\n void initState() {\n super.initState();\n _list = ListModel(\n listKey: _listKey,\n initialItems: [0, 1, 2, 3],\n removedItemBuilder: _buildRemovedItem,\n );\n _nextItem = 4;\n }\n\n Widget _buildItem(\n BuildContext context, int index, Animation animation) {\n return CardItem(\n animation: animation,\n item: _list[index],\n selected: _selectedItem == _list[index],\n onTap: () {\n setState(() {\n _selectedItem = _selectedItem == _list[index] ? null : _list[index];\n });\n },\n );\n }\n\n Widget _buildRemovedItem(\n int item, BuildContext context, Animation animation) {\n return CardItem(\n animation: animation,\n item: item,\n selected: false,\n );\n }\n\n void _insert() {\n final int index =\n _selectedItem == null ? _list.length : _list.indexOf(_selectedItem);\n _list.insert(index, _nextItem++);\n }\n\n void _remove() {\n if (_selectedItem != null) {\n _list.removeAt(_list.indexOf(_selectedItem));\n setState(() {\n _selectedItem = null;\n });\n }\n }\n\n @override\n Widget build(BuildContext context) {\n return Container(\n color: Colors.grey.withAlpha(33),\n width: MediaQuery.of(context).size.width/2,\n child: Column(\n children: [\n _buildBtn(),\n Container(\n width: MediaQuery.of(context).size.width/2,\n height: 300,\n child: AnimatedList(\n padding: EdgeInsets.all(10.0),\n key: _listKey,\n initialItemCount: _list.length,\n itemBuilder: _buildItem,\n ),\n )\n ],\n ));\n }\n\n Widget _buildBtn() => Row(\n children: [\n IconButton(\n icon: const Icon(\n Icons.add_circle,\n color: Colors.blue,\n ),\n onPressed: _insert,\n ),\n IconButton(\n icon: const Icon(Icons.remove_circle, color: Colors.blue),\n onPressed: _remove,\n ),\n ],\n );\n}\n\nclass ListModel {\n ListModel({\n @required this.listKey,\n @required this.removedItemBuilder,\n Iterable initialItems,\n }) : assert(listKey != null),\n assert(removedItemBuilder != null),\n _items = List.from(initialItems ?? []);\n final GlobalKey listKey;\n final dynamic removedItemBuilder;\n final List _items;\n\n AnimatedListState get _animatedList => listKey.currentState;\n\n void insert(int index, E item) {\n _items.insert(index, item);\n _animatedList.insertItem(index);\n }\n\n E removeAt(int index) {\n final E removedItem = _items.removeAt(index);\n if (removedItem != null) {\n _animatedList.removeItem(index,\n (BuildContext context, Animation animation) =>\n removedItemBuilder(removedItem, context, animation),\n );\n }\n return removedItem;\n }\n\n int get length => _items.length;\n\n E operator [](int index) => _items[index];\n\n int indexOf(E item) => _items.indexOf(item);\n}\n\nclass CardItem extends StatelessWidget {\n const CardItem(\n {Key key,\n @required this.animation,\n this.onTap,\n @required this.item,\n this.selected: false})\n : assert(animation != null),\n assert(item != null && item >= 0),\n assert(selected != null),\n super(key: key);\n final Animation animation;\n final VoidCallback onTap;\n final int item;\n final bool selected;\n\n @override\n Widget build(BuildContext context) {\n return SizeTransition(\n axis: Axis.vertical,\n sizeFactor: animation,\n child: Card(\n child: Container(\n color: Colors.primaries[item % Colors.primaries.length],\n child: CheckboxListTile(\n dense: true,\n title: Text(\n 'Item $item',\n style: TextStyle(color: Colors.white, fontSize: 18),\n ),\n value: selected,\n onChanged: (v) => onTap()),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":149,"name":"InkResponse其他属性","priority":2,"subtitle":" \n【child】 : 子组件 【Widget】\n【onHighlightChanged】 : 高亮变化回调 【Function(bool)】\n【highlightColor】 : 高亮色 【Color】\n【splashColor】 : 水波纹色 【Color】\n【radius】 : 水波半径 【double】","code":"import 'package:flutter/material.dart';\nclass ColorInkResponse extends StatefulWidget {\n @override\n _ColorInkResponseState createState() => _ColorInkResponseState();\n}\n\nclass _ColorInkResponseState extends State {\n var _info = 'Push';\n\n @override\n Widget build(BuildContext context) {\n return InkResponse(\n onTap: () => {},\n splashColor: Colors.blueAccent,\n highlightColor: Colors.orange,\n onHighlightChanged: (v) =>\n setState(() => _info = 'onHighlightChanged:$v'),\n radius: 50,\n child: Container(\n alignment: Alignment.center,\n width: 200,\n height: 100,\n child: Text(_info),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":149,"name":"InkResponse基本事件","priority":1,"subtitle":" \n【child】 : 子组件 【Widget】\n【onTap】 : 点击事件 【Function()】\n【onDoubleTap】 : 双击事件 【Function()】\n【onTapCancel】 : 点击取消 【Function()】\n【onLongPress】 : 长按事件 【Function()】","code":"import 'package:flutter/material.dart';\nclass CustomInkResponse extends StatefulWidget {\n @override\n _CustomInkResponseState createState() => _CustomInkResponseState();\n}\n\nclass _CustomInkResponseState extends State {\n var _info = 'Push';\n\n @override\n Widget build(BuildContext context) {\n return InkResponse(\n onTap: () => setState(() => _info = 'onTap'),\n onDoubleTap: () => setState(() => _info = 'onDoubleTap'),\n onLongPress: () => setState(() => _info = 'onLongPress'),\n onTapCancel: () => setState(() => _info = 'onTapCancel'),\n child: Container(\n alignment: Alignment.center,\n width: 200,\n height: 100,\n child: Text(_info),\n ),\n );\n }\n}"},{"id":null,"widgetId":119,"name":"AnimatedPadding基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【duration】 : 动画时长 【Duration】\n【onEnd】 : 动画结束回调 【Function()】\n【curve】 : 动画曲线 【Duration】\n【padding】 : 内边距 【EdgeInsetsGeometry】","code":"import 'package:flutter/material.dart';\nclass CustomAnimatedPadding extends StatefulWidget {\n @override\n _CustomAnimatedPaddingState createState() => _CustomAnimatedPaddingState();\n}\n\nclass _CustomAnimatedPaddingState extends State {\n final EdgeInsets startPadding = EdgeInsets.all(10);\n final EdgeInsets endPadding = EdgeInsets.all(30);\n\n EdgeInsets _padding;\n\n @override\n void initState() {\n _padding = startPadding;\n super.initState();\n }\n\n @override\n Widget build(BuildContext context) {\n return Column(\n children: [\n Switch(\n value: _padding == endPadding,\n onChanged: (v) {\n setState(() {\n _padding = v ? endPadding : startPadding;\n });\n }),\n Container(\n color: Colors.grey.withAlpha(22),\n width: 200,\n height: 100,\n child: AnimatedPadding(\n duration: Duration(seconds: 1),\n curve: Curves.fastOutSlowIn,\n padding: _padding,\n onEnd: () => print('End'),\n child: Container(\n alignment: Alignment.center,\n color: Colors.blue,\n child: Text(\n '张风捷特烈',\n style: TextStyle(color: Colors.white),\n ),\n ),\n ),\n ),\n ],\n );\n }\n}\n"},{"id":null,"widgetId":111,"name":"AlignTransition基本使用","priority":1,"subtitle":" \n【child】 : 孩子组件 【Widget】\n【alignment】 : 对齐动画 【Animation】","code":"import 'package:flutter/material.dart';\nclass CustomAlignTransition extends StatefulWidget {\n @override\n _CustomAlignTransitionState createState() => _CustomAlignTransitionState();\n}\n\nclass _CustomAlignTransitionState extends State\n with SingleTickerProviderStateMixin {\n AnimationController _ctrl;\n\n @override\n void initState() {\n _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 1));\n _ctrl.forward();\n super.initState();\n }\n\n @override\n void dispose() {\n _ctrl.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return GestureDetector(\n onTap: () {\n setState(() {\n _ctrl.reset();\n _ctrl.forward();\n });\n },\n child: Container(\n width: MediaQuery.of(context).size.width,\n color: Colors.grey.withAlpha(33),\n height: 100,\n child: AlignTransition(\n alignment: AlignmentTween(\n begin: Alignment.topLeft, end: Alignment.bottomRight)\n .animate(_ctrl),\n child: Container(\n child: Icon(Icons.android, color: Colors.green, size: 60)),\n ),\n ));\n }\n}\n"},{"id":null,"widgetId":145,"name":"LicensePage基本使用","priority":1,"subtitle":" \n【applicationIcon】 : 左上图标 【Widget】\n【applicationVersion】 : 版本号 【String】\n【applicationName】 : 应用名 【String】\n【applicationLegalese】 : 应用律术 【String】","code":"import 'package:flutter/material.dart';\nclass CustomLicensePage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width,\n height: 400,\n child: LicensePage(\n applicationIcon: FlutterLogo(),\n applicationVersion: 'v0.0.1',\n applicationName: 'Flutter Unit',\n applicationLegalese: 'Copyright© 2018-2020 张风捷特烈',\n ),\n );\n }\n}\n"},{"id":null,"widgetId":156,"name":"CupertinoApp基本用法","priority":1,"subtitle":" \n【theme】 : 主题 【ThemeData】\n【title】 : 任务栏标题 【String】\n【onGenerateRoute】 : 路由生成器 【RouteFactory】\n【home】 : 主页 【Widget】","code":"import 'package:flutter/cupertino.dart';\nclass CustomCupertinoApp extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width,\n height: MediaQuery.of(context).size.height - 200,\n child: CupertinoApp(\n title: 'Flutter Demo',\n theme: CupertinoThemeData(\n primaryColor: CupertinoColors.white,\n ),\n home: CupertinoPageScaffold(\n navigationBar: CupertinoNavigationBar(\n leading: Icon(\n CupertinoIcons.reply,\n color: CupertinoColors.black,\n ),\n trailing: Icon(\n CupertinoIcons.share,\n color: CupertinoColors.black,\n ),\n middle: Text('Flutter Unit'),\n ),\n backgroundColor: CupertinoColors.systemBackground,\n child: Center(\n child: Text('Hello, World!'),\n ),\n ),\n ),\n );\n }\n}\n"},{"id":null,"widgetId":64,"name":"Scaffold基本用法","priority":1,"subtitle":" \n【appBar】 : 头部组件 【PreferredSizeWidget】\n【bottomNavigationBar】 : 底部组件 【Widget】\n【drawer】 : 左侧滑组件 【Widget】\n【endDrawer】 : 右侧滑组件 【Widget】\n【body】 : 内容组件 【Widget】\n【backgroundColor】 : 背景色 【Color】\n【floatingActionButton】 : 浮动按钮 【Widget】\n【floatingActionButtonLocation】 : 浮动按钮位置 【FloatingActionButtonLocation】","code":"import 'package:flutter/material.dart';\nimport '../PopupMenuButton/node1_base.dart';\nclass CustomScaffold extends StatefulWidget {\n CustomScaffold({Key key}) : super(key: key);\n\n @override\n State createState() => _CustomScaffoldState();\n}\n\n// AppBar 默认的实例,有状态\nclass _CustomScaffoldState extends State with SingleTickerProviderStateMixin {\n final tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼'];\n var _position = 0;\n final iconsMap = {\n \"图鉴\": Icons.home,\n \"动态\": Icons.toys,\n \"喜欢\": Icons.favorite,\n \"手册\": Icons.class_,\n \"我的\": Icons.account_circle,\n };\n final _colors = [\n Colors.blue,\n Colors.red,\n Colors.yellow,\n Colors.green,\n Colors.purple,\n ];\n\n TabController _tabController;\n\n @override\n void initState() {\n super.initState();\n _tabController = TabController(vsync: this, length: tabs.length);\n }\n\n @override\n void dispose() {\n _tabController.dispose();\n super.dispose();\n }\n\n @override\n Widget build(BuildContext context) {\n return Container(\n width: MediaQuery.of(context).size.width,\n height: MediaQuery.of(context).size.height - 300,\n child: Scaffold(\n floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,\n floatingActionButton: FloatingActionButton(\n child: Icon(Icons.add),\n onPressed: () {},\n ),\n drawer: _buildLeftDrawer(),\n endDrawer: _buildLeftDrawer(),\n appBar: AppBar(\n title: Text('风雅六社'),\n backgroundColor: Colors.blue,\n centerTitle: true,\n actions: [Icon(Icons.star), CustomPopupMenuButton()],\n bottom: _buildTabBar(),\n ),\n body: _buildTableBarView(),\n bottomNavigationBar: _buildBottomNavigationBar(),\n ),\n );\n }\n\n Drawer _buildLeftDrawer() => Drawer(\n elevation: 1,\n child: Image.asset(\n 'assets/images/sabar.jpg',\n fit: BoxFit.cover,\n ),\n );\n\n Widget _buildTabBar() => TabBar(\n isScrollable: true,\n controller: _tabController,\n indicatorColor: Colors.orangeAccent,\n tabs: tabs.map((e) => Tab(text: e)).toList(),\n );\n\n Widget _buildBottomNavigationBar() => BottomNavigationBar(\n onTap: (position) => setState(() => _position = position),\n currentIndex: _position,\n elevation: 1,\n backgroundColor: Colors.white,\n iconSize: 25,\n selectedLabelStyle: TextStyle(fontWeight: FontWeight.bold),\n showUnselectedLabels: false,\n showSelectedLabels: true,\n items: iconsMap.keys\n .map((key) => BottomNavigationBarItem(\n title: Text(\n key,\n ),\n icon: Icon(iconsMap[key]),\n backgroundColor: _colors[_position]))\n .toList(),\n );\n\n Widget _buildTableBarView() => TabBarView(\n controller: _tabController,\n children: tabs\n .map((e) => Center(\n child: Text(\n e,\n style: TextStyle(color: Colors.blue, fontSize: 20),\n )))\n .toList());\n}\n"},{"id":null,"widgetId":171,"name":"Hero基本使用","priority":1,"subtitle":" \n【tag】 : 标签 【String】,","code":"import 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\nclass CustomHero extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var hero = Hero(\n //----定义一个Hero,并添加tag标签,此中组件共享\n tag: 'user-head',\n child: ClipRRect(\n borderRadius: BorderRadius.all(Radius.circular(30)),\n child: Image.asset(\n \"assets/images/icon_head.png\",\n width: 60,\n height: 60,\n fit: BoxFit.cover,\n ),\n ),\n );\n\n var container = Container(\n alignment: Alignment(-0.8, -0.8),\n child: hero,\n width: 250,\n height: 250 * 0.618,\n decoration: BoxDecoration(\n gradient: LinearGradient(colors: [\n Colors.red.withAlpha(99),\n Colors.yellow.withAlpha(189),\n Colors.green.withAlpha(88),\n Colors.blue.withAlpha(230)\n ])),\n );\n\n return GestureDetector(\n child: Card(elevation: 5, child: container),\n onTap: () => Navigator.push(\n context,\n Bottom2TopRouter(child: TargetPage(), duration: 1000),\n ),\n );\n }\n\n}\n\nclass TargetPage extends StatelessWidget {\n @override\n Widget build(BuildContext context) {\n var hero = Hero(\n //----定义一个Hero,为其添加标签,两个标签相同,则可以共享\n tag: 'user-head',\n child: Padding(\n padding: EdgeInsets.all(6.0),\n child: CircleAvatar(\n backgroundColor: Colors.transparent,\n backgroundImage: AssetImage(\n \"assets/images/icon_head.png\",\n ),\n ),\n ),\n );\n\n var touch = InkWell(\n onTap: () {\n Navigator.of(context).pop();\n },\n child: hero,\n );\n\n return Scaffold(\n appBar: AppBar(\n actions: [touch],\n ),\n body: Container(\n decoration: BoxDecoration(\n gradient: LinearGradient(colors: [\n Colors.red.withAlpha(99),\n Colors.yellow.withAlpha(189),\n Colors.green.withAlpha(88),\n Colors.blue.withAlpha(230)\n ])),\n ),\n );\n }\n}\n\n//下--->上\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 132af61ea..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/iconfont/icon_builder.dart b/assets/iconfont/icon_builder.dart deleted file mode 100644 index 4bd785edb..000000000 --- a/assets/iconfont/icon_builder.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'dart:io'; - -import 'package:path/path.dart' as path; - -main() async{ - String fontName ='TolyIcon'; - String fileName ='toly_icon'; - String resDir="assets/iconfont";//资源文件夹 - String outFile='lib/app/res/$fileName.dart';//输出文件地址 - - String result = """import 'package:flutter/widgets.dart'; -//Power By 张风捷特烈--- Generated file. Do not edit. - -class $fontName { - - $fontName._(); -"""; - File fileCss = File(path.join(Directory.current.path,"$resDir/iconfont.css")); - if (! await fileCss.exists()) return; - - String read = await fileCss.readAsString(); - List split = read.split(".icon-"); - split.forEach((str) { - if (str.contains("before")) { - List split = str.split(":"); - result += "static const IconData " + - split[0].replaceAll("-", "_") + - " = const IconData(" + - split[2].replaceAll("\"\\", "0x").split("\"")[0] + - ", fontFamily: \"$fontName\");\n"; - } - }); - result+="}"; - fileCss.delete();//删除css文件 - - File fileOut = File(path.join(Directory.current.path,"$outFile")); - if(! await fileOut.exists()){ - await fileOut.create(recursive: true); - } - fileOut.writeAsString(result);//将代码写入dart文件 - - String config=""" - fonts: - - family: $fontName - fonts: - - asset: """+"$resDir/iconfont.ttf"; - - print("build OK:\n $config"); -} \ No newline at end of file diff --git a/assets/iconfont/iconfont.ttf b/assets/iconfont/iconfont.ttf deleted file mode 100644 index 42c75a06b..000000000 Binary files a/assets/iconfont/iconfont.ttf and /dev/null differ diff --git a/assets/iconfont/toly_icon.ttf b/assets/iconfont/toly_icon.ttf new file mode 100644 index 000000000..f112440c2 Binary files /dev/null and b/assets/iconfont/toly_icon.ttf differ 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/flutter.png b/assets/images/flutter.png new file mode 100644 index 000000000..7da1247be Binary files /dev/null and b/assets/images/flutter.png differ diff --git a/assets/images/logo1.webp b/assets/images/logo1.webp new file mode 100644 index 000000000..4e0dfcb61 Binary files /dev/null and b/assets/images/logo1.webp differ diff --git a/assets/images/pica.gif b/assets/images/pica.gif deleted file mode 100644 index 6d2b050f5..000000000 Binary files a/assets/images/pica.gif and /dev/null 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/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/macos-2.webp b/doc/screens/macos-2.webp new file mode 100644 index 000000000..f2de629cf Binary files /dev/null and b/doc/screens/macos-2.webp 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/screens/windows-1.png b/doc/screens/windows-1.png new file mode 100644 index 000000000..dc46f4c6b Binary files /dev/null and b/doc/screens/windows-1.png 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/ios/.gitignore b/ios/.gitignore index e96ef602b..7a7f9873a 100644 --- a/ios/.gitignore +++ b/ios/.gitignore @@ -1,3 +1,4 @@ +**/dgph *.mode1v3 *.mode2v3 *.moved-aside @@ -18,6 +19,7 @@ Flutter/App.framework Flutter/Flutter.framework Flutter/Flutter.podspec Flutter/Generated.xcconfig +Flutter/ephemeral/ Flutter/app.flx Flutter/app.zip Flutter/flutter_assets/ diff --git a/ios/Flutter/.last_build_id b/ios/Flutter/.last_build_id deleted file mode 100644 index 0c650dedc..000000000 --- a/ios/Flutter/.last_build_id +++ /dev/null @@ -1 +0,0 @@ -320b85efcc94a82643501e6e104b8446 \ No newline at end of file diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f78a..9625e105d 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -3,7 +3,7 @@ CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) + en CFBundleExecutable App CFBundleIdentifier @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 11.0 diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig index e8efba114..ec97fc6f3 100644 --- a/ios/Flutter/Debug.xcconfig +++ b/ios/Flutter/Debug.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig index 399e9340e..c4855bfe2 100644 --- a/ios/Flutter/Release.xcconfig +++ b/ios/Flutter/Release.xcconfig @@ -1,2 +1,2 @@ -#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/ios/Podfile b/ios/Podfile index 1e8c3c90a..88359b225 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' +# platform :ios, '11.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e0f7b2a6a..42719a09a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,65 +1,133 @@ PODS: - - connectivity (0.0.1): + - connectivity_plus (0.0.1): + - Flutter + - ReachabilitySwift + - DKImagePickerController/Core (4.3.4): + - DKImagePickerController/ImageDataManager + - DKImagePickerController/Resource + - DKImagePickerController/ImageDataManager (4.3.4) + - DKImagePickerController/PhotoGallery (4.3.4): + - DKImagePickerController/Core + - DKPhotoGallery + - DKImagePickerController/Resource (4.3.4) + - DKPhotoGallery (0.0.17): + - DKPhotoGallery/Core (= 0.0.17) + - DKPhotoGallery/Model (= 0.0.17) + - DKPhotoGallery/Preview (= 0.0.17) + - DKPhotoGallery/Resource (= 0.0.17) + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Core (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Preview + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Model (0.0.17): + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Preview (0.0.17): + - DKPhotoGallery/Model + - DKPhotoGallery/Resource + - SDWebImage + - SwiftyGif + - DKPhotoGallery/Resource (0.0.17): + - SDWebImage + - SwiftyGif + - file_picker (0.0.1): + - DKImagePickerController/PhotoGallery - Flutter - - Reachability - Flutter (1.0.0) - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) - - path_provider (0.0.1): + - package_info_plus (0.4.5): + - Flutter + - path_provider_ios (0.0.1): - Flutter - - Reachability (3.2) - - share (0.0.1): + - r_upgrade (0.0.1): - Flutter - - shared_preferences (0.0.1): + - ReachabilitySwift (5.0.0) + - SDWebImage (5.15.4): + - SDWebImage/Core (= 5.15.4) + - SDWebImage/Core (5.15.4) + - share_plus (0.0.1): + - Flutter + - shared_preferences_ios (0.0.1): - Flutter - sqflite (0.0.2): - Flutter - FMDB (>= 2.7.5) - - url_launcher (0.0.1): + - SwiftyGif (5.4.4) + - url_launcher_ios (0.0.1): + - Flutter + - webview_flutter_wkwebview (0.0.1): - Flutter DEPENDENCIES: - - connectivity (from `.symlinks/plugins/connectivity/ios`) + - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) + - file_picker (from `.symlinks/plugins/file_picker/ios`) - Flutter (from `Flutter`) - - path_provider (from `.symlinks/plugins/path_provider/ios`) - - share (from `.symlinks/plugins/share/ios`) - - shared_preferences (from `.symlinks/plugins/shared_preferences/ios`) + - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) + - path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`) + - r_upgrade (from `.symlinks/plugins/r_upgrade/ios`) + - share_plus (from `.symlinks/plugins/share_plus/ios`) + - shared_preferences_ios (from `.symlinks/plugins/shared_preferences_ios/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - - url_launcher (from `.symlinks/plugins/url_launcher/ios`) + - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) + - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) SPEC REPOS: trunk: + - DKImagePickerController + - DKPhotoGallery - FMDB - - Reachability + - ReachabilitySwift + - SDWebImage + - SwiftyGif EXTERNAL SOURCES: - connectivity: - :path: ".symlinks/plugins/connectivity/ios" + connectivity_plus: + :path: ".symlinks/plugins/connectivity_plus/ios" + file_picker: + :path: ".symlinks/plugins/file_picker/ios" Flutter: :path: Flutter - path_provider: - :path: ".symlinks/plugins/path_provider/ios" - share: - :path: ".symlinks/plugins/share/ios" - shared_preferences: - :path: ".symlinks/plugins/shared_preferences/ios" + package_info_plus: + :path: ".symlinks/plugins/package_info_plus/ios" + path_provider_ios: + :path: ".symlinks/plugins/path_provider_ios/ios" + r_upgrade: + :path: ".symlinks/plugins/r_upgrade/ios" + share_plus: + :path: ".symlinks/plugins/share_plus/ios" + shared_preferences_ios: + :path: ".symlinks/plugins/shared_preferences_ios/ios" sqflite: :path: ".symlinks/plugins/sqflite/ios" - url_launcher: - :path: ".symlinks/plugins/url_launcher/ios" + url_launcher_ios: + :path: ".symlinks/plugins/url_launcher_ios/ios" + webview_flutter_wkwebview: + :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" SPEC CHECKSUMS: - connectivity: c4130b2985d4ef6fd26f9702e886bd5260681467 - Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c + connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a + DKImagePickerController: b512c28220a2b8ac7419f21c491fc8534b7601ac + DKPhotoGallery: fdfad5125a9fdda9cc57df834d49df790dbb4179 + file_picker: ce3938a0df3cc1ef404671531facef740d03f920 + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c - Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 - share: 0b2c3e82132f5888bccca3351c504d0003b3b410 - shared_preferences: af6bfa751691cdc24be3045c43ec037377ada40d + package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7 + path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02 + r_upgrade: 44d715c61914cce3d01ea225abffe894fd51c114 + ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 + SDWebImage: 1c39de67663e5eebb2f41324d5d580eeea12dd4c + share_plus: 599aa54e4ea31d4b4c0e9c911bcc26c55e791028 + shared_preferences_ios: 548a61f8053b9b8a49ac19c1ffbc8b92c50d68ad sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 - url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef + SwiftyGif: 93a1cc87bf3a51916001cf8f3d63835fb64c819f + url_launcher_ios: 839c58cdb4279282219f5e248c3321761ff3c4de + webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a -PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c +PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 -COCOAPODS: 1.10.1 +COCOAPODS: 1.12.0 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 6450c5b7c..4d990781b 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,17 +3,17 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 84DD72D28C46F1BB3BEBC8BF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 110BA964F25219A444600C8C /* Pods_Runner.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + D1D82669BF3BEF35AB05B2B1 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C7383EE4A15C8DD5DC0FFE49 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -30,15 +30,14 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 110BA964F25219A444600C8C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 40803ECD9DE781BA9823C8F9 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 64130BD95C8A67B133F535A6 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 96F0D2C37BCCAC3201451BB2 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 8124494143C478106CBB5535 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -46,7 +45,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - D6D9DCBCB8FFD1DDF50C51BD /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + ADBDEEDA55D5FE126765D6DA /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + C7383EE4A15C8DD5DC0FFE49 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,21 +54,31 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 84DD72D28C46F1BB3BEBC8BF /* Pods_Runner.framework in Frameworks */, + D1D82669BF3BEF35AB05B2B1 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 6D1B05DC8ACF1C1F8FFD8A7D /* Frameworks */ = { + 3357CCFDF19B51BC0DBF2F91 /* Frameworks */ = { isa = PBXGroup; children = ( - 110BA964F25219A444600C8C /* Pods_Runner.framework */, + C7383EE4A15C8DD5DC0FFE49 /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; }; + 47118212ECDE60EDA8D81265 /* Pods */ = { + isa = PBXGroup; + children = ( + ADBDEEDA55D5FE126765D6DA /* Pods-Runner.debug.xcconfig */, + 8124494143C478106CBB5535 /* Pods-Runner.release.xcconfig */, + 64130BD95C8A67B133F535A6 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -86,8 +96,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - AB34C6A5ACB1D149FB8F864B /* Pods */, - 6D1B05DC8ACF1C1F8FFD8A7D /* Frameworks */, + 47118212ECDE60EDA8D81265 /* Pods */, + 3357CCFDF19B51BC0DBF2F91 /* Frameworks */, ); sourceTree = ""; }; @@ -114,17 +124,6 @@ path = Runner; sourceTree = ""; }; - AB34C6A5ACB1D149FB8F864B /* Pods */ = { - isa = PBXGroup; - children = ( - D6D9DCBCB8FFD1DDF50C51BD /* Pods-Runner.debug.xcconfig */, - 96F0D2C37BCCAC3201451BB2 /* Pods-Runner.release.xcconfig */, - 40803ECD9DE781BA9823C8F9 /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -132,14 +131,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 36F73146086477EBD377777E /* [CP] Check Pods Manifest.lock */, + BF5658AA55AEB255A85F6911 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - C780EF36233459FEE95BD85A /* [CP] Embed Pods Frameworks */, + 690C1029D98F763064F681B2 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -156,7 +155,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1300; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -198,44 +197,42 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 36F73146086477EBD377777E /* [CP] Check Pods Manifest.lock */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - ); inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); + name = "Thin Binary"; outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + 690C1029D98F763064F681B2 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); - inputPaths = ( + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "Thin Binary"; - outputPaths = ( + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -248,21 +245,26 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - C780EF36233459FEE95BD85A /* [CP] Embed Pods Frameworks */ = { + BF5658AA55AEB255A85F6911 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - name = "[CP] Embed Pods Frameworks"; + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -340,7 +342,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -355,20 +357,25 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = XQPP7CHG9D; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = XQPP7CHG9D; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( + GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "DISABLE_PUSH_NOTIFICATIONS=1", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + INFOPLIST_KEY_CFBundleDisplayName = FlutterUnit; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.toly1994.flutterUnit; + MARKETING_VERSION = 2.9.2; + PRODUCT_BUNDLE_IDENTIFIER = "com.toly1994.flutter-unit"; PRODUCT_NAME = "$(TARGET_NAME)"; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = flutter_unit_profile; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; @@ -422,7 +429,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -471,11 +478,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; }; @@ -487,20 +495,29 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = XQPP7CHG9D; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( + GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "DISABLE_PUSH_NOTIFICATIONS=1", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + INFOPLIST_KEY_CFBundleDisplayName = FlutterUnit; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.toly1994.flutterUnit; + MARKETING_VERSION = 2.9.2; + PRODUCT_BUNDLE_IDENTIFIER = "com.toly1994.flutter-unit"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = flutter_unit_profile; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -514,20 +531,29 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = XQPP7CHG9D; ENABLE_BITCODE = NO; - FRAMEWORK_SEARCH_PATHS = ( + GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "DISABLE_PUSH_NOTIFICATIONS=1", ); INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - LIBRARY_SEARCH_PATHS = ( + INFOPLIST_KEY_CFBundleDisplayName = FlutterUnit; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools"; + LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", - "$(PROJECT_DIR)/Flutter", + "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.toly1994.flutterUnit; + MARKETING_VERSION = 2.9.2; + PRODUCT_BUNDLE_IDENTIFIER = "com.toly1994.flutter-unit"; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = flutter_unit_profile; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140cfd..c87d15a33 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ - - - - + + - - - -NSAppTransportSecurity - - NSAllowsArbitraryLoads - - + CADisableMinimumFrameDurationOnPhone + CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + Flutter Unit CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -28,6 +26,8 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + UIApplicationSupportsIndirectInputEvents + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -47,5 +47,9 @@ UIViewControllerBasedStatusBarAppearance + NSPhotoLibraryUsageDescription + FlutterUnit 申请访问相册,以提升您的图片选择功能的体验 + NSPhotoLibraryAddUsageDescription + FlutterUnit 申请访问相册,以支持您保存图片到相册 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/.gitignore b/lib/.gitignore deleted file mode 100644 index 84ea85a96..000000000 --- a/lib/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/tools/ \ No newline at end of file diff --git a/lib/app/api/app_info.dart b/lib/app/api/app_info.dart deleted file mode 100644 index bb9bffbac..000000000 --- a/lib/app/api/app_info.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter_unit/app/res/path_unit.dart'; -import 'package:flutter_unit/app/utils/http_utils/http_util.dart'; -import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; - -class AppInfoApi { - - static Future> getAppVersion() async { - String errorMsg = ""; - var result = await HttpUtil.getInstance() - .client - .get(PathUnit.appInfo) - .catchError((err) { - errorMsg = err.toString(); - }); - - // 获取的数据非空且 status = true - if (result.data != null && result.data['status']) { - // 说明有数据 - if (result.data['data'] != null) { - return ResultBean.ok( - AppInfo( - appName: result.data['data']['appName'], - appVersion: result.data['data']['appVersion'], - appUrl: result.data['data']['appUrl'], - )); - } else { - return ResultBean.ok(null); - } - } - return ResultBean.error('请求错误: $errorMsg'); - } -} - -class AppInfo{ - final String appName; - final String appVersion; - final String appUrl; - - AppInfo({this.appName, this.appVersion, this.appUrl}); -} \ No newline at end of file diff --git a/lib/app/api/category_api.dart b/lib/app/api/category_api.dart deleted file mode 100644 index 93868d69a..000000000 --- a/lib/app/api/category_api.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter_unit/app/res/path_unit.dart'; -import 'package:flutter_unit/app/utils/http_utils/http_util.dart'; -import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; - -/// create by 张风捷特烈 on 2021/2/24 -/// contact me by email 1981462002@qq.com -/// 说明: - -class CategoryApi { - static Future> uploadCategoryData( - {String data, String likeData}) async { - String errorMsg = ""; - - var result = await HttpUtil.getInstance().client.post( - PathUnit.categoryDataSync, - data: {"data": data, "likeData": likeData}).catchError((err) { - errorMsg =err.toString(); - }); - - if (result.data != null) { - return ResultBean.fromData(result.data); - } - - return ResultBean.error('请求错误: $errorMsg'); - } - - static Future> getCategoryData() async { - String errorMsg = ""; - var result = await HttpUtil.getInstance() - .client - .get(PathUnit.categoryData) - .catchError((err) { - errorMsg =err.toString(); - - }); - - // 获取的数据非空且 status = true - if (result.data != null && result.data['status']) { - // 说明有数据 - if (result.data['data'] != null) { - return ResultBean.ok(CategoryData.fromJson(result.data['data'])); - } else { - return ResultBean.ok(null); - } - } - - return ResultBean.error('请求错误: $errorMsg'); - } -} - -class CategoryData{ - final int categoryDataId; - final int userId; - final String data; - final String likeData; - - CategoryData({this.categoryDataId, this.userId, this.data, 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/app/api/issues_api.dart b/lib/app/api/issues_api.dart deleted file mode 100644 index 4127ab828..000000000 --- a/lib/app/api/issues_api.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'dart:convert'; - -import 'package:dio/dio.dart'; -import 'package:flutter_unit/model/github/issue_comment.dart'; -import 'package:flutter_unit/model/github/issue.dart'; -import 'package:flutter_unit/model/github/repository.dart'; - -/// create by 张风捷特烈 on 2020/6/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -const kBaseUrl = 'http://119.45.173.197: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/app/res/color_unit.dart b/lib/app/res/color_unit.dart deleted file mode 100644 index 1c6866cec..000000000 --- a/lib/app/res/color_unit.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/11/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -class ColorUnit{ - - // 文字相关 - static const Color input_border_color = Color(0xffD0D7DD); - static const Color text_color = Color(0xff323C47); - static const Color input_hit_text_color = Color(0xff939EA7); - static const Color head_text_color = Color(0xff666666); - - // 缺省相关 - - static const Color error_color = Colors.red; - static const Color warning_color = Colors.orangeAccent; - -} \ No newline at end of file diff --git a/lib/app/res/cons.dart b/lib/app/res/cons.dart deleted file mode 100644 index b8040c191..000000000 --- a/lib/app/res/cons.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/permanent/code/highlighter_style.dart'; - -class Cons { - - static const MENU_INFO = ["关于", "帮助", "问题反馈"]; //菜单栏 - - - static const tabColors = [ - 0xff44D1FD, - 0xffFD4F43, - 0xffB375FF, - 0xFF4CAF50, - 0xFFFF9800, - 0xFF00F1F1, - 0xFFDBD83F - ]; - - static const tabs = [ - 'Stles', - 'Stful', - 'Scrow', - 'Mcrow', - 'Sliver', - 'Proxy', - 'Other' - ]; //标题列表 - - static const fontFamilySupport = [ - 'local', - 'ComicNeue', - 'IndieFlower', - 'BalooBhai2', - 'Inconsolata', - 'Neucha' - ]; - - 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 themeColorSupport = { - Colors.red: "毁灭之红", - Colors.orange: "愤怒之橙", - Colors.yellow: "警告之黄", - Colors.green: "伪装之绿", - Colors.blue: "冷漠之蓝", - Colors.indigo: "无限之靛", - Colors.purple: "神秘之紫", - - 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/lib/app/res/path_unit.dart b/lib/app/res/path_unit.dart deleted file mode 100644 index 75cdce27b..000000000 --- a/lib/app/res/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://119.45.173.197: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/FlutterUnit'; - - static const login = '/login'; - -} \ No newline at end of file diff --git a/lib/app/res/sp.dart b/lib/app/res/sp.dart deleted file mode 100644 index b5aeb7df6..000000000 --- a/lib/app/res/sp.dart +++ /dev/null @@ -1,13 +0,0 @@ -/// create by 张风捷特烈 on 2020-04-10 -/// contact me by email 1981462002@qq.com -/// 说明: - -class SP{ - - static const themeColorIndex = 'theme_color_index'; - static const showBackground = 'show_background'; - static const fontFamily = 'font_family'; - static const codeStyleIndex = 'code_style'; - static const itemStyleIndex = 'item_style_index'; - -} \ No newline at end of file diff --git a/lib/app/res/str_unit.dart b/lib/app/res/str_unit.dart deleted file mode 100644 index e15c25d7a..000000000 --- a/lib/app/res/str_unit.dart +++ /dev/null @@ -1,46 +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/lib/app/res/style/behavior/no_scroll_behavior.dart b/lib/app/res/style/behavior/no_scroll_behavior.dart deleted file mode 100644 index 84c41938d..000000000 --- a/lib/app/res/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/lib/app/res/style/gap_unit.dart b/lib/app/res/style/gap_unit.dart deleted file mode 100644 index c8848c38b..000000000 --- a/lib/app/res/style/gap_unit.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; - -class GapUnit{ - static const Widget H5 = SizedBox(width: 5); - static const Widget H10 = SizedBox(height: 10); - - static const Widget W5 = SizedBox(width: 5); - static const Widget W10 = SizedBox(width: 10); - -} \ No newline at end of file diff --git a/lib/app/res/style/unit_color.dart b/lib/app/res/style/unit_color.dart deleted file mode 100644 index bdb35a77c..000000000 --- a/lib/app/res/style/unit_color.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 -/// 说明: - -class UnitColor { - static const collectColorSupport = [ - Color(0xFFF2F2F2), - Colors.black, - Colors.red, - Colors.orange, - Colors.yellow, - Colors.green, - Colors.blue, - Colors.indigo, - Colors.purple, - - Colors.cyanAccent, - Color(0xffd1d08f), - Colors.pink, - Colors.amber, - Colors.lime, - Colors.teal, - Colors.cyan, - Color(0xff586CF2), - Colors.purpleAccent, - ]; - - static const int primaryIntValue = 0xFF24292E; - - static const MaterialColor primarySwatch = const MaterialColor( - primaryIntValue, - const { - 50: const Color(primaryIntValue), - 100: const Color(primaryIntValue), - 200: const Color(primaryIntValue), - 300: const Color(primaryIntValue), - 400: const Color(primaryIntValue), - 500: const Color(primaryIntValue), - 600: const Color(primaryIntValue), - 700: const Color(primaryIntValue), - 800: const Color(primaryIntValue), - 900: const Color(primaryIntValue), - }, - ); - - static const String primaryValueString = "#24292E"; - static const String primaryLightValueString = "#42464b"; - static const String primaryDarkValueString = "#121917"; - static const String miWhiteString = "#ececec"; - static const String actionBlueString = "#267aff"; - static const String webDraculaBackgroundColorString = "#282a36"; - - static const Color primaryValue = Color(0xFF24292E); - static const Color primaryLightValue = Color(0xFF42464b); - static const Color primaryDarkValue = Color(0xFF121917); - - static const Color cardWhite = Color(0xFFFFFFFF); - static const Color textWhite = Color(0xFFFFFFFF); - static const Color miWhite = Color(0xffececec); - static const Color white = Color(0xFFFFFFFF); - static const Color actionBlue = Color(0xff267aff); - static const Color subTextColor = Color(0xff959595); - static const Color subLightTextColor = Color(0xffc4c4c4); - - static const Color mainBackgroundColor = miWhite; - - static const Color mainTextColor = primaryDarkValue; - static const Color textColorWhite = white; -} diff --git a/lib/app/res/style/unit_text_style.dart b/lib/app/res/style/unit_text_style.dart deleted file mode 100644 index 76af312f1..000000000 --- a/lib/app/res/style/unit_text_style.dart +++ /dev/null @@ -1,178 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'unit_color.dart'; - -///文本样式 -class TStyleUnit { - static const lagerTextSize = 30.0; - static const bigTextSize = 23.0; - static const normalTextSize = 18.0; - static const middleTextWhiteSize = 16.0; - static const smallTextSize = 14.0; - static const minTextSize = 12.0; - - static const minText = TextStyle( - color: UnitColor.subLightTextColor, - fontSize: minTextSize, - ); - - static const splashShadows = TextStyle( - color: Colors.grey, - shadows: [ - Shadow( - color: Colors.black, - blurRadius: 1, - offset: Offset(0.1, 0.1)) - ], - fontSize: 12); - - static const smallTextWhite = TextStyle( - color: UnitColor.textColorWhite, - fontSize: smallTextSize, - ); - - static const shadowTextStyle = TextStyle(color: Colors.grey, shadows: [ - Shadow(color: Colors.white, offset: Offset(.5, .5), blurRadius: .5) - ]); - - static const smallText = TextStyle( - color: UnitColor.mainTextColor, - fontSize: smallTextSize, - ); - - static const smallTextBold = TextStyle( - color: UnitColor.mainTextColor, - fontSize: smallTextSize, - fontWeight: FontWeight.bold, - ); - - static const smallSubLightText = TextStyle( - color: UnitColor.subLightTextColor, - fontSize: smallTextSize, - ); - - static const smallActionLightText = TextStyle( - color: UnitColor.actionBlue, - fontSize: smallTextSize, - ); - - static const smallMiLightText = TextStyle( - color: UnitColor.miWhite, - fontSize: smallTextSize, - ); - - static const smallSubText = TextStyle( - color: UnitColor.subTextColor, - fontSize: smallTextSize, - ); - - static const middleText = TextStyle( - color: UnitColor.mainTextColor, - fontSize: middleTextWhiteSize, - ); - - static const middleTextWhite = TextStyle( - color: UnitColor.textColorWhite, - fontSize: middleTextWhiteSize, - ); - - static const middleSubText = TextStyle( - color: UnitColor.subTextColor, - fontSize: middleTextWhiteSize, - ); - - static const middleSubLightText = TextStyle( - color: UnitColor.subLightTextColor, - fontSize: middleTextWhiteSize, - ); - - static const middleTextBold = TextStyle( - color: UnitColor.mainTextColor, - fontSize: middleTextWhiteSize, - fontWeight: FontWeight.bold, - ); - - static const middleTextWhiteBold = TextStyle( - color: UnitColor.textColorWhite, - fontSize: middleTextWhiteSize, - fontWeight: FontWeight.bold, - ); - - static const middleSubTextBold = TextStyle( - color: UnitColor.subTextColor, - fontSize: middleTextWhiteSize, - fontWeight: FontWeight.bold, - ); - - static const normalText = TextStyle( - color: UnitColor.mainTextColor, - fontSize: normalTextSize, - ); - - static const normalTextBold = TextStyle( - color: UnitColor.mainTextColor, - fontSize: normalTextSize, - fontWeight: FontWeight.bold, - ); - - static const normalSubText = TextStyle( - color: UnitColor.subTextColor, - fontSize: normalTextSize, - ); - - static const normalTextWhite = TextStyle( - color: UnitColor.textColorWhite, - fontSize: normalTextSize, - ); - - static const normalTextMitWhiteBold = TextStyle( - color: UnitColor.miWhite, - fontSize: normalTextSize, - fontWeight: FontWeight.bold, - ); - - static const normalTextActionWhiteBold = TextStyle( - color: UnitColor.actionBlue, - fontSize: normalTextSize, - fontWeight: FontWeight.bold, - ); - - static const normalTextLight = TextStyle( - color: UnitColor.primaryLightValue, - fontSize: normalTextSize, - ); - - static const largeText = TextStyle( - color: UnitColor.mainTextColor, - fontSize: bigTextSize, - ); - - static const largeTextBold = TextStyle( - color: UnitColor.mainTextColor, - fontSize: bigTextSize, - fontWeight: FontWeight.bold, - ); - - static const largeTextWhite = TextStyle( - color: UnitColor.textColorWhite, - fontSize: bigTextSize, - ); - - static const largeTextWhiteBold = TextStyle( - color: UnitColor.textColorWhite, - fontSize: bigTextSize, - fontWeight: FontWeight.bold, - ); - - static const largeLargeTextWhite = TextStyle( - color: UnitColor.textColorWhite, - fontSize: lagerTextSize, - fontWeight: FontWeight.bold, - ); - - static const largeLargeText = TextStyle( - color: UnitColor.primaryValue, - fontSize: lagerTextSize, - fontWeight: FontWeight.bold, - ); -} diff --git a/lib/app/res/style_unit.dart b/lib/app/res/style_unit.dart deleted file mode 100644 index 8382da53f..000000000 --- a/lib/app/res/style_unit.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'color_unit.dart'; -import 'size_unit.dart'; - -/// create by 张风捷特烈 on 2020/11/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -class TextStyleUnit { - static const TextStyle hintStyle = const TextStyle( - color: ColorUnit.input_hit_text_color, - fontSize: SizeUnit.input_hit_text_size); - - static const TextStyle primary = const TextStyle( - color: ColorUnit.text_color, fontSize: SizeUnit.input_text_size); - - static const TextStyle headTextStyle = const TextStyle( - color: ColorUnit.head_text_color, fontSize: SizeUnit.head_text_size); - - static const TextStyle smallSubTextStyle = const TextStyle( - color: ColorUnit.input_hit_text_color, - fontSize: SizeUnit.small_text_size); - - static const TextStyle bigTextStyle = const TextStyle( - color: ColorUnit.text_color, fontSize: SizeUnit.big_text_size); -} diff --git a/lib/app/res/toly_icon.dart b/lib/app/res/toly_icon.dart deleted file mode 100644 index 3b4c19719..000000000 --- a/lib/app/res/toly_icon.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/widgets.dart'; -//Power By 张风捷特烈--- Generated file. Do not edit. - -class TolyIcon { - - TolyIcon._(); -static const IconData upload_success = const IconData( 0xe60b, fontFamily: "TolyIcon"); -static const IconData download = const IconData( 0xea51, fontFamily: "TolyIcon"); -static const IconData upload = const IconData( 0xea52, fontFamily: "TolyIcon"); -static const IconData error = const IconData( 0xe614, fontFamily: "TolyIcon"); -static const IconData dingzhi1 = const IconData( 0xe60e, fontFamily: "TolyIcon"); -static const IconData icon_collect = const IconData( 0xe672, fontFamily: "TolyIcon"); -static const IconData yonghu = const IconData( 0xe619, fontFamily: "TolyIcon"); -static const IconData icon_common = const IconData( 0xe634, fontFamily: "TolyIcon"); -static const IconData icon_see = const IconData( 0xe608, fontFamily: "TolyIcon"); -static const IconData icon_issues = const IconData( 0xe7a7, fontFamily: "TolyIcon"); -static const IconData icon_fork = const IconData( 0xe623, fontFamily: "TolyIcon"); -static const IconData icon_github_star = const IconData( 0xe7df, fontFamily: "TolyIcon"); -static const IconData icon_show = const IconData( 0xe648, fontFamily: "TolyIcon"); -static const IconData icon_hide = const IconData( 0xe649, fontFamily: "TolyIcon"); -static const IconData icon_email = const IconData( 0xe694, fontFamily: "TolyIcon"); -static const IconData icon_github = const IconData( 0xe689, fontFamily: "TolyIcon"); -static const IconData icon_juejin = const IconData( 0xe601, fontFamily: "TolyIcon"); -static const IconData icon_share = const IconData( 0xe613, fontFamily: "TolyIcon"); -static const IconData icon_background = const IconData( 0xe60a, fontFamily: "TolyIcon"); -static const IconData icon_code = const IconData( 0xe70b, fontFamily: "TolyIcon"); -static const IconData icon_item = const IconData( 0xe66f, fontFamily: "TolyIcon"); -static const IconData icon_kafei = const IconData( 0xe6aa, fontFamily: "TolyIcon"); -static const IconData icon_tag = const IconData( 0xe6e7, fontFamily: "TolyIcon"); -static const IconData icon_them = const IconData( 0xe6c2, fontFamily: "TolyIcon"); -static const IconData icon_bug = const IconData( 0xe7af, fontFamily: "TolyIcon"); -static const IconData icon_layout = const IconData( 0xe631, fontFamily: "TolyIcon"); -static const IconData icon_sound = const IconData( 0xe606, fontFamily: "TolyIcon"); -static const IconData icon_search = const IconData( 0xe604, fontFamily: "TolyIcon"); -static const IconData icon_star_ok = const IconData( 0xe6ae, fontFamily: "TolyIcon"); -static const IconData icon_star = const IconData( 0xe609, fontFamily: "TolyIcon"); -static const IconData icon_star_add = const IconData( 0xe68e, fontFamily: "TolyIcon"); -} \ No newline at end of file diff --git a/lib/app/router/router_utils.dart b/lib/app/router/router_utils.dart deleted file mode 100644 index 9bce89cb6..000000000 --- a/lib/app/router/router_utils.dart +++ /dev/null @@ -1,163 +0,0 @@ -import 'package:flutter/material.dart'; - -//缩放路由动画 -class ScaleRouter extends PageRouteBuilder { - final Widget child; - final int durationMs; - final Curve curve; - ScaleRouter({this.child, this.durationMs = 500,this.curve=Curves.fastOutSlowIn}) - : super( - pageBuilder: (context, animation, secondaryAnimation) => child, - transitionDuration: Duration(milliseconds: durationMs), - transitionsBuilder: (context, a1, a2, child) => - ScaleTransition( - scale: Tween(begin: 0.0, end: 1.0).animate( - CurvedAnimation(parent: a1, curve: curve)), - child: child, - ), - ); -} -//渐变透明路由动画 -class FadeRouter extends PageRouteBuilder { - final Widget child; - final int durationMs; - final Curve curve; - FadeRouter({this.child, this.durationMs = 500,this.curve=Curves.fastOutSlowIn}) - : super( - pageBuilder: (context, animation, secondaryAnimation) => child, - transitionDuration: Duration(milliseconds: durationMs), - transitionsBuilder: (context, a1, a2, child) => - FadeTransition( - opacity: Tween(begin: 0.1, end: 1.0).animate( - CurvedAnimation(parent: a1, curve:curve,)), - child: child, - )); -} - -//旋转路由动画 -class RotateRouter extends PageRouteBuilder { - final Widget child; - final int durationMs; - final Curve curve; - RotateRouter({this.child, this.durationMs = 500,this.curve=Curves.fastOutSlowIn}) - : super( - pageBuilder: (context, animation, secondaryAnimation) => child, - transitionDuration: Duration(milliseconds: durationMs), - transitionsBuilder: (context, a1, a2, child) => - RotationTransition( - turns: Tween(begin: 0.1, end: 1.0).animate( - CurvedAnimation(parent: a1, curve:curve,)), - child: child, - )); -} - -//右--->左 -class Right2LeftRouter extends PageRouteBuilder { - final Widget child; - final int durationMs; - final Curve curve; - Right2LeftRouter({this.child,this.durationMs=500,this.curve=Curves.fastOutSlowIn}) - :super( - transitionDuration:Duration(milliseconds: durationMs), - pageBuilder:(ctx,a1,a2)=>child, - transitionsBuilder:(ctx,a1,a2, child,) => - SlideTransition( - child: child, - position: Tween( - begin: Offset(1.0, 0.0), end: Offset(0.0, 0.0),).animate( - CurvedAnimation(parent: a1, curve: curve)), - )); -} - -//左--->右 -class Left2RightRouter extends PageRouteBuilder { - final Widget child; - final int durationMs; - final Curve curve; - List mapper; - Left2RightRouter({this.child,this.durationMs=500,this.curve=Curves.fastOutSlowIn}) - :assert(true),super( - transitionDuration:Duration(milliseconds: durationMs), - pageBuilder:(ctx,a1,a2){return child;}, - transitionsBuilder:(ctx,a1,a2,child,) { - return SlideTransition( - position: Tween( - begin: Offset(-1.0, 0.0), end: Offset(0.0, 0.0),).animate( - CurvedAnimation(parent: a1, curve: curve)), - child: child - ); - }); -} - -//上--->下 -class Top2BottomRouter extends PageRouteBuilder { - final Widget child; - final int durationMs; - final Curve curve; - Top2BottomRouter({this.child,this.durationMs=500,this.curve=Curves.fastOutSlowIn}) - :super( - transitionDuration:Duration(milliseconds: durationMs), - pageBuilder:(ctx,a1,a2){return child;}, - transitionsBuilder:(ctx,a1,a2, child,) { - return SlideTransition( - position: Tween( - begin: Offset(0.0,-1.0), end: Offset(0.0, 0.0),).animate( - CurvedAnimation(parent: a1, curve: curve)), - child: child - ); - }); -} - -//下--->上 -class Bottom2TopRouter extends PageRouteBuilder { - final Widget child; - final int durationMs; - final Curve curve; - Bottom2TopRouter({this.child,this.durationMs=500,this.curve=Curves.fastOutSlowIn}) - :super( - transitionDuration:Duration(milliseconds: durationMs), - pageBuilder:(ctx,a1,a2)=> child, - transitionsBuilder:(ctx,a1,a2, child,) { - return SlideTransition( - position: Tween( - begin: Offset(0.0, 1.0), end: Offset(0.0, 0.0),).animate( - CurvedAnimation(parent: a1, curve: curve)), - child: child - ); - }); -} - -//缩放+透明+旋转路由动画 -class ScaleFadeRotateRouter extends PageRouteBuilder { - final Widget child; - final int durationMs; - final Curve curve; - ScaleFadeRotateRouter({this.child, this.durationMs = 1000,this.curve=Curves.fastOutSlowIn}) : super( - transitionDuration: Duration(milliseconds: durationMs), - pageBuilder: (ctx, a1, a2)=>child,//页面 - transitionsBuilder: (ctx, a1, a2, Widget child,) => - RotationTransition(//旋转动画 - turns: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( - parent: a1, - curve: curve, - )), - child: ScaleTransition(//缩放动画 - scale: Tween(begin: 0.0, end: 1.0).animate( - CurvedAnimation(parent: a1, curve: curve)), - child: FadeTransition(opacity://透明度动画 - Tween(begin: 0.5, end: 1.0).animate(CurvedAnimation(parent: a1, curve: curve)), - child: child,), - ), - )); -} -//无动画 -class NoAnimRouter extends PageRouteBuilder { - final Widget child; - NoAnimRouter({this.child}) - : super( - opaque: false, - pageBuilder: (context, animation, secondaryAnimation) => child, - transitionDuration: Duration(milliseconds: 0), - transitionsBuilder: - (context, animation, secondaryAnimation, child) => child); -} diff --git a/lib/app/router/unit_router.dart b/lib/app/router/unit_router.dart deleted file mode 100644 index 0836a5c30..000000000 --- a/lib/app/router/unit_router.dart +++ /dev/null @@ -1,134 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/pages/app/unit_navigation.dart'; -import 'package:flutter_unit/views/pages/about/about_me_page.dart'; -import 'package:flutter_unit/views/pages/about/about_app_page.dart'; -import 'package:flutter_unit/views/pages/about/version_info.dart'; -import 'package:flutter_unit/views/pages/category/category_detail.dart'; -import 'package:flutter_unit/views/pages/category/collect_page.dart'; -import 'package:flutter_unit/views/pages/data_manage/data_manage_page.dart'; -import 'package:flutter_unit/views/pages/gallery/gallery_page.dart'; -import 'package:flutter_unit/views/pages/issues_point/issues_detail.dart'; -import 'package:flutter_unit/views/pages/issues_point/issues_point_page.dart'; -import 'package:flutter_unit/user_system/pages/login/login_page.dart'; -import 'package:flutter_unit/user_system/pages/register/register_page.dart'; -import 'package:flutter_unit/views/pages/search/serach_page.dart'; -import 'package:flutter_unit/views/pages/setting/code_style_setting.dart'; -import 'package:flutter_unit/views/pages/setting/font_setting.dart'; -import 'package:flutter_unit/views/pages/setting/item_style_setting.dart'; -import 'package:flutter_unit/views/pages/setting/theme_color_setting.dart'; -import 'package:flutter_unit/views/pages/unit_todo/attr_unit_page.dart'; -import 'package:flutter_unit/views/pages/unit_todo/point_unit_page.dart'; - -import 'package:flutter_unit/views/pages/widget_detail/widget_detail_page.dart'; -import 'package:flutter_unit/views/pages/unit_todo/layout_unit_page.dart'; -import 'package:flutter_unit/views/pages/setting/setting_page.dart'; - -import 'router_utils.dart'; - -class UnitRouter { - static const String widget_detail = '/widget_detail'; - - static const String detail = 'detail'; - static const String search = 'search'; - 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 galley = 'GalleryPage'; - 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 nav: - return Left2RightRouter(child: UnitNavigation()); - - // 组件详情页 - case widget_detail: - return Right2LeftRouter( - child: WidgetDetailPage(model: settings.arguments)); - - case search: - return Right2LeftRouter(child: SearchPage()); - case collect: - return Right2LeftRouter(child: CollectPage()); - - case setting: - return Right2LeftRouter(child: SettingPage()); - case data_manage: - return Right2LeftRouter(child: DataManagePage()); - case font_setting: - return Right2LeftRouter(child: FontSettingPage()); - case theme_color_setting: - return Right2LeftRouter(child: ThemeColorSettingPage()); - case code_style_setting: - return Right2LeftRouter(child: CodeStyleSettingPage()); - case item_style_setting: - return Right2LeftRouter(child: ItemStyleSettingPage()); - - case version_info: - return Right2LeftRouter(child: VersionInfo()); - - case version_info: - return Right2LeftRouter(child: VersionInfo()); - - case issues_point: - return Right2LeftRouter(child: IssuesPointPage()); - case login: - return Right2LeftRouter(child: LoginPage()); - - case register: - return Right2LeftRouter(child: RegisterPage()); - - case attr: - return Right2LeftRouter(child: AttrUnitPage()); - case bug: - return Right2LeftRouter(child: BugUnitPage()); - case galley: - return Right2LeftRouter(child: GalleryPage()); - case layout: - return Right2LeftRouter(child: LayoutUnitPage()); - case about_app: - return Right2LeftRouter(child: AboutAppPage()); - case about_me: - return Right2LeftRouter(child: AboutMePage()); - - case point_detail: - return Right2LeftRouter(child: IssuesDetailPage()); - - case category_show: - return Right2LeftRouter( - child: CategoryShow( - model: settings.arguments, - )); - - 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 c353721a8..000000000 --- a/lib/app/utils/Toast.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -class Toast { - static toast(BuildContext context, String msg, - {duration = const Duration(milliseconds: 600), - Color color, - SnackBarAction action}) { - - Scaffold.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/convert.dart b/lib/app/utils/convert.dart deleted file mode 100644 index 2bfff6991..000000000 --- a/lib/app/utils/convert.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'package:flutter_unit/model/enums.dart'; -import 'package:flutter_unit/painter_system/gallery_factory.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){ - 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"; - } - } - - -} diff --git a/lib/app/utils/http_utils/http_util.dart b/lib/app/utils/http_utils/http_util.dart deleted file mode 100644 index 41bbec787..000000000 --- a/lib/app/utils/http_utils/http_util.dart +++ /dev/null @@ -1,69 +0,0 @@ - -import 'package:dio/dio.dart'; -import 'package:flutter_unit/app/res/path_unit.dart'; - -import 'logs_interceptor.dart'; -import 'token_interceptor.dart'; - -const int _kReceiveTimeout = 5000; -const int _kSendTimeout = 5000; -const int _kConnectTimeout = 5000; - -class HttpUtil { - static HttpUtil _instance = HttpUtil._internal(); - Dio _dio; - - - - - static const CODE_SUCCESS = 200; - static const CODE_TIME_OUT = -1; - - factory HttpUtil() => _instance; - TokenInterceptors tokenInterceptors; - - ///通用全局单例,第一次使用时初始化 - HttpUtil._internal() { - if (null == _dio) { - _dio = Dio(BaseOptions( - baseUrl: PathUnit.baseUrl, - connectTimeout: _kReceiveTimeout, - receiveTimeout: _kConnectTimeout, - sendTimeout: _kSendTimeout, - )); - _dio.interceptors.add(LogsInterceptors()); - // _dio.interceptors.add(ResponseInterceptors()); - } - } - - static HttpUtil getInstance() { - return _instance._default(); - - } - - Dio get client => _dio; - - void setToken(String token){ - print('---token---$token-------'); - tokenInterceptors = TokenInterceptors(token: token); - _dio.interceptors.add(tokenInterceptors); - } - void deleteToken(){ - _dio.interceptors.remove(tokenInterceptors); - } - - void rebase(String baseIp) { - _dio.options.baseUrl = baseIp; - } - - //一般请求,默认域名 - HttpUtil _default() { - if (_dio != null) { - _dio.options.baseUrl=PathUnit.baseUrl; - _dio.options.headers = {}; - } - return this; - } -} - - diff --git a/lib/app/utils/http_utils/logs_interceptor.dart b/lib/app/utils/http_utils/logs_interceptor.dart deleted file mode 100644 index 56d414e25..000000000 --- a/lib/app/utils/http_utils/logs_interceptor.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:dio/dio.dart'; - -class LogsInterceptors extends InterceptorsWrapper { - @override - onRequest(RequestOptions options) async{ -// print("==========请求baseUrl:${options.baseUrl} =========="); -// print("==========请求url:${options.path} =========="); -// print('==========请求头: ' + options.headers.toString()+"=========="); -// if (options.data != null) { -// print('==========请求参数: ' + options.data.toString()+"=========="); -// } - return options; - } - - @override - onResponse(Response response)async { - if (response != null) { - // var responseStr = response.toString(); - } - - return response; // continue - } - - @override - onError(DioError err) async{ - print('==========请求异常: ' + err.toString()+"=========="); - print('==========请求异常信息: ' + err.response?.toString()+"==========" ?? "=========="); - return err; - } -} \ No newline at end of file diff --git a/lib/app/utils/http_utils/result_bean.dart b/lib/app/utils/http_utils/result_bean.dart deleted file mode 100644 index 545bfa948..000000000 --- a/lib/app/utils/http_utils/result_bean.dart +++ /dev/null @@ -1,36 +0,0 @@ -class ResultBean { - T data; - bool status; - String msg; - - ResultBean({this.data, this.status, this.msg}); - - @override - String toString() { - return 'RepResult{data: $data, status: $status, msg:$msg}'; - } - - static ResultBean fromData(dynamic data) { - return ResultBean( - msg: data['msg'], - data: data['data'], - status: data['status'], - ); - } - - static ResultBean error(String msg) { - return ResultBean( - msg: msg, - data: null, - status: false, - ); - } - - static ResultBean ok(T data) { - return ResultBean( - msg: '请求成功', - data: data, - status: true, - ); - } -} diff --git a/lib/app/utils/http_utils/token_interceptor.dart b/lib/app/utils/http_utils/token_interceptor.dart deleted file mode 100644 index 398ae7f88..000000000 --- a/lib/app/utils/http_utils/token_interceptor.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:dio/dio.dart'; - -const String _kTokenKey = 'Authorization'; -const String _kTokenPrefix = 'Bearer '; - -class TokenInterceptors extends InterceptorsWrapper { - - String token; - - - TokenInterceptors({this.token=''}); - - @override - Future onRequest(RequestOptions options) async{ - // print('----RequestOptions---${options.path}------------'); - if(token.isNotEmpty){ - options.headers[_kTokenKey] = '$_kTokenPrefix$token'; - } - return options; - } -} diff --git a/lib/app/utils/pather.dart b/lib/app/utils/pather.dart deleted file mode 100644 index f4ed2dd1f..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._(); - - 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/stream_ext/backpressure.dart b/lib/app/utils/stream_ext/backpressure.dart deleted file mode 100755 index 29bebe917..000000000 --- a/lib/app/utils/stream_ext/backpressure.dart +++ /dev/null @@ -1,356 +0,0 @@ -import 'dart:async'; -import 'dart:collection'; - -import 'forwarding_stream.dart'; - - -/// A [Sink] that supports event hooks. -/// -/// This makes it suitable for certain rx transformers that need to -/// take action after onListen, onPause, onResume or onCancel. -/// -/// The [ForwardingSink] has been designed to handle asynchronous events from -/// [Stream]s. See, for example, [Stream.eventTransformed] which uses -/// `EventSink`s to transform events. -abstract class ForwardingSink { - /// Handle data event - void add(EventSink sink, T data); - - /// Handle error event - void addError(EventSink sink, dynamic error, [StackTrace st]); - - /// Handle close event - void close(EventSink sink); - - /// Fires when a listener subscribes on the underlying [Stream]. - void onListen(EventSink sink); - - /// Fires when a subscriber pauses. - void onPause(EventSink sink, [Future resumeSignal]); - - /// Fires when a subscriber resumes after a pause. - void onResume(EventSink sink); - - /// Fires when a subscriber cancels. - FutureOr onCancel(EventSink sink); -} - - -// import 'package:rxdart/src/utils/forwarding_sink.dart'; -// import 'package:rxdart/src/utils/forwarding_stream.dart'; - -/// The strategy that is used to determine how and when a new window is created. -enum WindowStrategy { - /// cancels the open window (if any) and immediately opens a fresh one. - everyEvent, - - /// waits until the current open window completes, then when the - /// source [Stream] emits a next event, it opens a new window. - eventAfterLastWindow, - - /// opens a recurring window right after the very first event on - /// the source [Stream] is emitted. - firstEventOnly, - - /// does not open any windows, rather all events are buffered and emitted - /// whenever the handler triggers, after this trigger, the buffer is cleared. - onHandler -} - -class _BackpressureStreamSink implements ForwardingSink { - final WindowStrategy _strategy; - final Stream Function(S event) _windowStreamFactory; - final T Function(S event) _onWindowStart; - final T Function(List queue) _onWindowEnd; - final int _startBufferEvery; - final bool Function(List queue) _closeWindowWhen; - final bool _ignoreEmptyWindows; - final bool _dispatchOnClose; - final queue = []; - var skip = 0; - var _hasData = false; - StreamSubscription _windowSubscription; - - _BackpressureStreamSink( - this._strategy, - this._windowStreamFactory, - this._onWindowStart, - this._onWindowEnd, - this._startBufferEvery, - this._closeWindowWhen, - this._ignoreEmptyWindows, - this._dispatchOnClose); - - @override - void add(EventSink sink, S data) { - _hasData = true; - maybeCreateWindow(data, sink); - - if (skip == 0) { - queue.add(data); - } - - if (skip > 0) { - skip--; - } - - maybeCloseWindow(sink); - } - - @override - void addError(EventSink sink, dynamic e, [st]) => sink.addError(e, st); - - @override - void close(EventSink sink) { - // treat the final event as a Window that opens - // and immediately closes again - if (_dispatchOnClose && queue.isNotEmpty) { - resolveWindowStart(queue.last, sink); - } - - resolveWindowEnd(sink, true); - - queue.clear(); - - _windowSubscription?.cancel(); - sink.close(); - } - - @override - FutureOr onCancel(EventSink sink) => _windowSubscription?.cancel(); - - @override - void onListen(EventSink sink) {} - - @override - void onPause(EventSink sink, [Future resumeSignal]) => - _windowSubscription?.pause(resumeSignal); - - @override - void onResume(EventSink sink) => _windowSubscription?.resume(); - - void maybeCreateWindow(S event, EventSink sink) { - switch (_strategy) { - // for example throttle - case WindowStrategy.eventAfterLastWindow: - if (_windowSubscription != null) return; - - _windowSubscription = singleWindow(event, sink); - - resolveWindowStart(event, sink); - - break; - // for example scan - case WindowStrategy.firstEventOnly: - if (_windowSubscription != null) return; - - _windowSubscription = multiWindow(event, sink); - - resolveWindowStart(event, sink); - - break; - // for example debounce - case WindowStrategy.everyEvent: - _windowSubscription?.cancel(); - - _windowSubscription = singleWindow(event, sink); - - resolveWindowStart(event, sink); - - break; - case WindowStrategy.onHandler: - break; - } - } - - void maybeCloseWindow(EventSink sink) { - if (_closeWindowWhen != null && - _closeWindowWhen(UnmodifiableListView(queue))) { - resolveWindowEnd(sink); - } - } - - StreamSubscription singleWindow(S event, EventSink sink) => - buildStream(event, sink).take(1).listen( - null, - onError: sink.addError, - onDone: () => resolveWindowEnd(sink), - ); - - // opens a new Window which is kept open until the main Stream - // closes. - StreamSubscription multiWindow(S event, EventSink sink) => - buildStream(event, sink).listen( - (dynamic _) => resolveWindowEnd(sink), - onError: sink.addError, - onDone: () => resolveWindowEnd(sink), - ); - - Stream buildStream(S event, EventSink sink) { - Stream stream; - - _windowSubscription?.cancel(); - - stream = _windowStreamFactory(event); - - if (stream == null) { - sink.addError(ArgumentError.notNull('windowStreamFactory')); - } - - return stream; - } - - void resolveWindowStart(S event, EventSink sink) { - if (_onWindowStart != null) { - sink.add(_onWindowStart(event)); - } - } - - void resolveWindowEnd(EventSink sink, [bool isControllerClosing = false]) { - if (isControllerClosing || - _strategy == WindowStrategy.eventAfterLastWindow || - _strategy == WindowStrategy.everyEvent) { - _windowSubscription?.cancel(); - _windowSubscription = null; - } - - if (isControllerClosing && !_dispatchOnClose) { - return; - } - - if (_hasData && (queue.isNotEmpty || !_ignoreEmptyWindows)) { - if (_onWindowEnd != null) { - sink.add(_onWindowEnd(List.unmodifiable(queue))); - } - - // prepare the buffer for the next window. - // by default, this is just a cleared buffer - if (!isControllerClosing && _startBufferEvery > 0) { - // ...unless startBufferEvery is provided. - // here we backtrack to the first event of the last buffer - // and count forward using startBufferEvery until we reach - // the next event. - // - // if the next event is found inside the current buffer, - // then this event and any later events in the buffer - // become the starting values of the next buffer. - // if the next event is not yet available, then a skip - // count is calculated. - // this count will skip the next Future n-events. - // when skip is reset to 0, then we start adding events - // again into the new buffer. - // - // example: - // startBufferEvery = 2 - // last buffer: [0, 1, 2, 3, 4] - // 0 is the first event, - // 2 is the n-th event - // new buffer starts with [2, 3, 4] - // - // example: - // startBufferEvery = 3 - // last buffer: [0, 1] - // 0 is the first event, - // the n-the event is not yet dispatched at this point - // skip becomes 1 - // event 2 is skipped, skip becomes 0 - // event 3 is now added to the buffer - final startWith = (_startBufferEvery < queue.length) - ? queue.sublist(_startBufferEvery) - : []; - - skip = _startBufferEvery > queue.length - ? _startBufferEvery - queue.length - : 0; - - queue - ..clear() - ..addAll(startWith); - } else { - queue.clear(); - } - } - } -} - -/// A highly customizable [StreamTransformer] which can be configured -/// to serve any of the common rx backpressure operators. -/// -/// The [StreamTransformer] works by creating windows, during which it -/// buffers events to a [Queue]. -/// -/// The [StreamTransformer] works by creating windows, during which it -/// buffers events to a [Queue]. It uses a [WindowStrategy] to determine -/// how and when a new window is created. -/// -/// onWindowStart and onWindowEnd are handlers that fire when a window -/// opens and closes, right before emitting the transformed event. -/// -/// startBufferEvery allows to skip events coming from the source [Stream]. -/// -/// ignoreEmptyWindows can be set to true, to allow events to be emitted -/// at the end of a window, even if the current buffer is empty. -/// If the buffer is empty, then an empty [List] will be emitted. -/// If false, then nothing is emitted on an empty buffer. -/// -/// dispatchOnClose will cause the remaining values in the buffer to be -/// emitted when the source [Stream] closes. -/// When false, the remaining buffer is discarded on close. -class BackpressureStreamTransformer extends StreamTransformerBase { - /// Determines how the window is created - final WindowStrategy strategy; - - /// Factory method used to create the [Stream] which will be buffered - final Stream Function(S event) windowStreamFactory; - - /// Handler which fires when the window opens - final T Function(S event) onWindowStart; - - /// Handler which fires when the window closes - final T Function(List queue) onWindowEnd; - - /// Used to skip an amount of events - final int startBufferEvery; - - /// Predicate which determines when the current window should close - final bool Function(List queue) closeWindowWhen; - - /// Toggle to prevent, or allow windows that contain - /// no events to be dispatched - final bool ignoreEmptyWindows; - - /// Toggle to prevent, or allow the final set of events to be dispatched - /// when the source [Stream] closes - final bool dispatchOnClose; - - /// Constructs a [StreamTransformer] which buffers events emitted by the - /// [Stream] that is created by [windowStreamFactory]. - /// - /// Use the various optional parameters to precisely determine how and when - /// this buffer should be created. - /// - /// For more info on the parameters, see [BackpressureStreamTransformer], - /// or see the various back pressure [StreamTransformer]s for examples. - BackpressureStreamTransformer(this.strategy, this.windowStreamFactory, - {this.onWindowStart, - this.onWindowEnd, - this.startBufferEvery = 0, - this.closeWindowWhen, - this.ignoreEmptyWindows = true, - this.dispatchOnClose = true}); - - @override - Stream bind(Stream stream) { - var sink = _BackpressureStreamSink( - strategy, - windowStreamFactory, - onWindowStart, - onWindowEnd, - startBufferEvery, - closeWindowWhen, - ignoreEmptyWindows, - dispatchOnClose, - ); - return forwardStream(stream, sink); - } -} diff --git a/lib/app/utils/stream_ext/ext.dart b/lib/app/utils/stream_ext/ext.dart deleted file mode 100644 index deb1cf9f8..000000000 --- a/lib/app/utils/stream_ext/ext.dart +++ /dev/null @@ -1,84 +0,0 @@ -import 'backpressure.dart'; -import 'timer_stream.dart'; - -/// create by 张风捷特烈 on 2020/11/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -/// Transforms a [Stream] so that will only emit items from the source sequence -/// if a window has completed, without the source sequence emitting -/// another item. -/// -/// This window is created after the last debounced event was emitted. -/// You can use the value of the last debounced event to determine -/// the length of the next window. -/// -/// A window is open until the first window event emits. -/// -/// The debounce [StreamTransformer] filters out items emitted by the source -/// Stream that are rapidly followed by another emitted item. -/// -/// [Interactive marble diagram](http://rxmarbles.com/#debounce) -/// -/// ### Example -/// -/// Stream.fromIterable([1, 2, 3, 4]) -/// .debounceTime(Duration(seconds: 1)) -/// .listen(print); // prints 4 -class DebounceStreamTransformer extends BackpressureStreamTransformer { - /// Constructs a [StreamTransformer] which buffers events into a [List] and - /// emits this [List] whenever the current [window] fires. - /// - /// The [window] is reset whenever the [Stream] that is being transformed - /// emits an event. - DebounceStreamTransformer(Stream Function(T event) window) - : super(WindowStrategy.everyEvent, window, - onWindowEnd: (Iterable queue) => queue.last) { - assert(window != null, 'window stream factory cannot be null'); - } -} - -/// Extends the Stream class with the ability to debounce events in various ways -extension DebounceExtensions on Stream { - /// Transforms a [Stream] so that will only emit items from the source sequence - /// if a [window] has completed, without the source sequence emitting - /// another item. - /// - /// This [window] is created after the last debounced event was emitted. - /// You can use the value of the last debounced event to determine - /// the length of the next [window]. - /// - /// A [window] is open until the first [window] event emits. - /// - /// debounce filters out items emitted by the source [Stream] - /// that are rapidly followed by another emitted item. - /// - /// [Interactive marble diagram](http://rxmarbles.com/#debounce) - /// - /// ### Example - /// - /// Stream.fromIterable([1, 2, 3, 4]) - /// .debounce((_) => TimerStream(true, Duration(seconds: 1))) - /// .listen(print); // prints 4 - Stream debounce(Stream Function(T event) window) => - transform(DebounceStreamTransformer(window)); - - /// Transforms a [Stream] so that will only emit items from the source - /// sequence whenever the time span defined by [duration] passes, without the - /// source sequence emitting another item. - /// - /// This time span start after the last debounced event was emitted. - /// - /// debounceTime filters out items emitted by the source [Stream] that are - /// rapidly followed by another emitted item. - /// - /// [Interactive marble diagram](http://rxmarbles.com/#debounceTime) - /// - /// ### Example - /// - /// Stream.fromIterable([1, 2, 3, 4]) - /// .debounceTime(Duration(seconds: 1)) - /// .listen(print); // prints 4 - Stream debounceTime(Duration duration) => transform( - DebounceStreamTransformer((_) => TimerStream(true, duration))); -} diff --git a/lib/app/utils/stream_ext/forwarding_stream.dart b/lib/app/utils/stream_ext/forwarding_stream.dart deleted file mode 100755 index 6c4681242..000000000 --- a/lib/app/utils/stream_ext/forwarding_stream.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'dart:async'; - -import 'backpressure.dart'; - -// import 'package:rxdart/src/utils/forwarding_sink.dart'; - -// import 'forwarding_sink.dart'; - -/// @private -/// Helper method which forwards the events from an incoming [Stream] -/// to a new [StreamController]. -/// It captures events such as onListen, onPause, onResume and onCancel, -/// which can be used in pair with a [ForwardingSink] -Stream forwardStream( - Stream stream, - ForwardingSink connectedSink, -) { - ArgumentError.checkNotNull(stream, 'stream'); - ArgumentError.checkNotNull(connectedSink, 'connectedSink'); - - StreamController controller; - StreamSubscription subscription; - - void runCatching(void Function() block) { - try { - block(); - } catch (e, s) { - connectedSink.addError(controller, e, s); - } - } - - final onListen = () { - runCatching(() => connectedSink.onListen(controller)); - - subscription = stream.listen( - (data) => runCatching(() => connectedSink.add(controller, data)), - onError: (dynamic e, StackTrace st) => - runCatching(() => connectedSink.addError(controller, e, st)), - onDone: () => runCatching(() => connectedSink.close(controller)), - ); - }; - - final onCancel = () { - final onCancelSelfFuture = subscription.cancel(); - final onCancelConnectedFuture = connectedSink.onCancel(controller); - final futures = [ - if (onCancelSelfFuture is Future) onCancelSelfFuture, - if (onCancelConnectedFuture is Future) onCancelConnectedFuture, - ]; - return Future.wait(futures); - }; - - final onPause = ([Future resumeSignal]) { - subscription.pause(resumeSignal); - runCatching(() => connectedSink.onPause(controller, resumeSignal)); - }; - - final onResume = () { - subscription.resume(); - runCatching(() => connectedSink.onResume(controller)); - }; - - // Create a new Controller, which will serve as a trampoline for - // forwarded events. - if (stream.isBroadcast) { - controller = StreamController.broadcast( - onListen: onListen, - onCancel: onCancel, - sync: true, - ); - } else { - controller = StreamController( - onListen: onListen, - onPause: onPause, - onResume: onResume, - onCancel: onCancel, - sync: true, - ); - } - - return controller.stream; -} diff --git a/lib/app/utils/stream_ext/timer_stream.dart b/lib/app/utils/stream_ext/timer_stream.dart deleted file mode 100644 index be2ea0b3f..000000000 --- a/lib/app/utils/stream_ext/timer_stream.dart +++ /dev/null @@ -1,56 +0,0 @@ -import 'dart:async'; - -/// Emits the given value after a specified amount of time. -/// -/// ### Example -/// -/// TimerStream('hi', Duration(minutes: 1)) -/// .listen((i) => print(i)); // print 'hi' after 1 minute -class TimerStream extends Stream { - final StreamController _controller; - - /// Constructs a [Stream] which emits [value] after the specified [Duration]. - TimerStream(T value, Duration duration) - : _controller = _buildController(value, duration); - - @override - StreamSubscription listen(void Function(T event) onData, - {Function onError, void Function() onDone, bool cancelOnError}) { - return _controller.stream.listen( - onData, - onError: onError, - onDone: onDone, - cancelOnError: cancelOnError, - ); - } - - static StreamController _buildController(T value, Duration duration) { - if (duration == null) { - throw ArgumentError('duration cannot be null'); - } - - StreamSubscription subscription; - StreamController controller; - - controller = StreamController( - sync: true, - onListen: () { - subscription = - Stream.fromFuture(Future.delayed(duration, () => value)).listen( - controller.add, - onError: controller.addError, - onDone: () { - if (!controller.isClosed) { - controller.close(); - } - }, - ); - }, - onPause: ([Future resumeSignal]) => - subscription.pause(resumeSignal), - onResume: () => subscription.resume(), - onCancel: () => subscription.cancel(), - ); - return controller; - } -} 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/blocs/bloc_exp.dart b/lib/blocs/bloc_exp.dart deleted file mode 100644 index b79d2bee8..000000000 --- a/lib/blocs/bloc_exp.dart +++ /dev/null @@ -1,44 +0,0 @@ -/// create by 张风捷特烈 on 2020/4/28 -/// contact me by email 1981462002@qq.com -/// 说明: - -library bloc_unit; - -export 'category/category_bloc.dart'; -export 'category/category_state.dart'; -export 'category/category_event.dart'; - -export 'category_widget/category_widget_bloc.dart'; -export 'category_widget/category_widget_state.dart'; -export 'category_widget/category_widget_event.dart'; - -export 'like/like_bloc.dart'; -export 'like/like_state.dart'; -export 'like/like_event.dart'; - -export 'detail/detail_bloc.dart'; -export 'detail/detail_state.dart'; -export 'detail/detail_event.dart'; - -export 'global/global_bloc.dart'; -export 'global/global_state.dart'; -export 'global/global_event.dart'; - -export 'widgets/widgets_bloc.dart'; -export 'widgets/widgets_state.dart'; -export 'widgets/widgets_event.dart'; - - - -export 'point/point_bloc.dart'; -export 'point/point_state.dart'; -export 'point/point_event.dart'; - - -export 'point_comment/point_comment_bloc.dart'; -export 'point_comment/point_comment_state.dart'; -export 'point_comment/point_comment_event.dart'; - -export 'search/search_bloc.dart'; -export 'search/search_state.dart'; -export 'search/search_event.dart'; \ No newline at end of file diff --git a/lib/blocs/category/category_bloc.dart b/lib/blocs/category/category_bloc.dart deleted file mode 100644 index f8ef1b876..000000000 --- a/lib/blocs/category/category_bloc.dart +++ /dev/null @@ -1,92 +0,0 @@ - -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/res/style/unit_color.dart'; -import 'package:flutter_unit/app/utils/color_utils.dart'; -import 'package:flutter_unit/model/category_model.dart'; -import 'package:flutter_unit/repositories/bean/category_po.dart'; -import 'package:flutter_unit/repositories/rep/category_repository.dart'; - -import 'category_event.dart'; -import 'category_state.dart'; - -/// create by 张风捷特烈 on 2020-04-21 -/// contact me by email 1981462002@qq.com -/// 说明: - -class CategoryBloc extends Bloc { - final CategoryRepository repository; - - CategoryBloc({@required this.repository}) : super(const CategoryLoadingState()); - - @override - Stream mapEventToState(CategoryEvent event) async* { - if (event is EventLoadCategory) { - yield const CategoryLoadingState(); - // 使用 repository 加载 收藏集数据 - final category = await repository.loadCategories(); - yield category.isEmpty - ? const CategoryEmptyState() - : CategoryLoadedState(category); - } - - if (event is EventDeleteCategory) { - await repository.deleteCategory(event.id); - add(const EventLoadCategory()); - } - - if (event is EventToggleWidget) { - await repository.toggleCategory(event.categoryId, event.widgetId); - add(const EventLoadCategory()); - } - - if (event is EventAddCategory) { - CategoryPo categoryPo = CategoryPo( - name: event.name, - color: event.color ?? - ColorUtils.colorString(UnitColor.collectColorSupport[0]), - info: event.info ?? '这里什么都没有...', - created: DateTime.now(), - updated: DateTime.now()); - - final success = await repository.addCategory(categoryPo); - - if (success) { - yield const AddCategorySuccess(); - add(const EventLoadCategory()); - } else { - yield const AddCategoryFailed(); - } - } - - if (event is EventUpdateCategory) { - CategoryPo categoryPo = CategoryPo( - id: event.id, - name: event.name, - priority: event.priority ?? 0, - image: event.image ?? '', - color: event.color ?? - ColorUtils.colorString(UnitColor.collectColorSupport[0]), - info: event.info ?? '这里什么都没有...', - updated: DateTime.now()); - - final success = await repository.updateCategory(categoryPo); - - if (success) { -// yield AddCategorySuccess(); - add(const EventLoadCategory()); - } else { -// yield AddCategoryFailed(); - } - } - - } - - List get categories { - if(state is CategoryLoadedState){ - return (state as CategoryLoadedState).categories; - }else{ - return null; - } - } -} diff --git a/lib/blocs/category/category_event.dart b/lib/blocs/category/category_event.dart deleted file mode 100644 index 8d95d8a01..000000000 --- a/lib/blocs/category/category_event.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-21 -/// contact me by email 1981462002@qq.com -/// 说明: 收藏集相关事件 - -abstract class CategoryEvent extends Equatable{ - const CategoryEvent(); - @override - List get props => []; -} -// 加载 收藏集 事件 -class EventLoadCategory extends CategoryEvent{ - const EventLoadCategory(); - @override - List get props => []; -} - -// 将一个 widget 添加/移除 收藏集 -class EventToggleWidget extends CategoryEvent{ - final int widgetId; - final int categoryId; - const EventToggleWidget({this.widgetId, this.categoryId}); - - @override - List get props => [widgetId,categoryId]; -} - -// 删除 收藏集 -class EventDeleteCategory extends CategoryEvent{ - final int id; - - const EventDeleteCategory({@required this.id}); - - @override - List get props => [id]; -} - -// 添加 收藏集 -class EventAddCategory extends CategoryEvent{ - final String name; - final String info; - final String color; - - const EventAddCategory({@required this.name, this.info, this.color}); - - @override - List get props => [name,info,color]; -} - -// 更新 收藏集 -class EventUpdateCategory extends CategoryEvent { - final int id; - final String name; - final String info; - final String color; - final int priority; - final String image; - - const EventUpdateCategory({@required this.name, this.info, this.color,this.priority,this.image,this.id}); - - @override - List get props => [name, info, color,priority,image,id]; -} \ No newline at end of file diff --git a/lib/blocs/category_widget/category_widget_bloc.dart b/lib/blocs/category_widget/category_widget_bloc.dart deleted file mode 100644 index de5ee92e5..000000000 --- a/lib/blocs/category_widget/category_widget_bloc.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/blocs/category/category_bloc.dart'; -import 'package:flutter_unit/blocs/category/category_event.dart'; -import 'package:flutter_unit/repositories/rep/category_repository.dart'; - -import 'category_widget_event.dart'; -import '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()); - - CategoryRepository get repository => categoryBloc.repository; - - @override - Stream mapEventToState( - CategoryWidgetEvent event) async* { - if (event is EventLoadCategoryWidget) { - final widgets = - await repository.loadCategoryWidgets(categoryId: event.categoryId); - yield widgets.isNotEmpty - ? CategoryWidgetLoadedState(widgets) - : CategoryWidgetEmptyState(); - categoryBloc.add(EventLoadCategory()); - } - - if (event is EventToggleCategoryWidget) { - await repository.toggleCategory(event.categoryId, event.widgetId); - add(EventLoadCategoryWidget(event.categoryId)); - } - } -} diff --git a/lib/blocs/category_widget/category_widget_event.dart b/lib/blocs/category_widget/category_widget_event.dart deleted file mode 100644 index 44e98252d..000000000 --- a/lib/blocs/category_widget/category_widget_event.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:equatable/equatable.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/lib/blocs/detail/detail_bloc.dart b/lib/blocs/detail/detail_bloc.dart deleted file mode 100644 index c477b7be5..000000000 --- a/lib/blocs/detail/detail_bloc.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/model/widget_model.dart'; -import 'package:flutter_unit/repositories/rep/widget_repository.dart'; - -import 'detail_event.dart'; -import 'detail_state.dart'; - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明: - -class DetailBloc extends Bloc { - final WidgetRepository repository; - - DetailBloc({@required this.repository}):super(DetailLoading()); - - - @override - Stream mapEventToState(DetailEvent event) async* { - if (event is FetchWidgetDetail) { - yield* _mapLoadWidgetToState(event.widgetModel); - } - if(event is ResetDetailState){ - yield DetailLoading(); - } - } - - Stream _mapLoadWidgetToState( - WidgetModel widgetModel) async* { - yield DetailLoading(); - try { - final nodes = await this.repository.loadNode(widgetModel); - final links = await this.repository.loadWidget(widgetModel.links); - if(nodes.isEmpty){ - yield DetailEmpty(); - }else{ - yield DetailWithData(widgetModel: widgetModel, nodes: nodes,links: links); - } - - } catch (_) { - yield DetailFailed(); - } - } -} diff --git a/lib/blocs/detail/detail_event.dart b/lib/blocs/detail/detail_event.dart deleted file mode 100644 index 914643787..000000000 --- a/lib/blocs/detail/detail_event.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/model/widget_model.dart'; - - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明: 详情事件 - -abstract class DetailEvent extends Equatable { - const DetailEvent(); - @override - List get props => []; -} - - -class FetchWidgetDetail extends DetailEvent { - final WidgetModel widgetModel; - - const FetchWidgetDetail(this.widgetModel); - - @override - List get props => [widgetModel]; - - @override - String toString() { - return 'FetchWidgetDetail{widgetModel: $widgetModel}'; - } -} - - -class ResetDetailState extends DetailEvent { - -} \ No newline at end of file diff --git a/lib/blocs/detail/detail_state.dart b/lib/blocs/detail/detail_state.dart deleted file mode 100644 index 65135574a..000000000 --- a/lib/blocs/detail/detail_state.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/model/node_model.dart'; -import 'package:flutter_unit/model/widget_model.dart'; - - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明: 详情状态类 - -abstract class DetailState extends Equatable { - const DetailState(); - - @override - List get props => []; -} - -class DetailWithData extends DetailState { - final WidgetModel widgetModel; - final List links; - final List nodes; - - const DetailWithData({this.widgetModel, this.nodes,this.links}); - - @override - List get props => [widgetModel,nodes]; - - @override - String toString() { - return 'DetailWithData{widget: $widgetModel, nodes: $nodes}'; - } - -} - -class DetailLoading extends DetailState {} - -class DetailEmpty extends DetailState {} - -class DetailFailed extends DetailState {} \ No newline at end of file diff --git a/lib/blocs/global/global_bloc.dart b/lib/blocs/global/global_bloc.dart deleted file mode 100644 index b7b09dbfa..000000000 --- a/lib/blocs/global/global_bloc.dart +++ /dev/null @@ -1,72 +0,0 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/res/cons.dart'; -import 'package:flutter_unit/app/res/sp.dart'; -import 'package:flutter_unit/repositories/app_start.dart'; -import 'package:flutter_unit/repositories/local_storage.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -import 'global_event.dart'; -import 'global_state.dart'; - -/// create by 张风捷特烈 on 2020-03-22 -/// contact me by email 1981462002@qq.com -/// 说明: 全局信息的bloc - -class GlobalBloc extends Bloc { - - final AppStart storage; - - GlobalBloc(this.storage):super(GlobalState()); - - Future get sp => LocalStorage.sp; - - @override - Stream mapEventToState(GlobalEvent event) async* { - // 程序初始化事件处理: 使用 AppStorage 进行初始化 - if (event is EventInitApp) { - yield await storage.initApp(); - } - - // 切换字体事件处理 : 固化索引 + 产出新状态 - if (event is EventSwitchFontFamily) { - int familyIndex = Cons.fontFamilySupport.indexOf(event.family); - await sp..setInt(SP.fontFamily, familyIndex); - yield state.copyWith(fontFamily: event.family); - } - - // 切换主题色事件处理 : 固化索引 + 产出新状态 - if (event is EventSwitchThemeColor) { - int themeIndex = - Cons.themeColorSupport.keys.toList().indexOf(event.color); - await sp - ..setInt(SP.themeColorIndex, themeIndex); - yield state.copyWith(themeColor: event.color); - } - - // 切换背景显示事件处理 : 固化数据 + 产出新状态 - if (event is EventSwitchShowBg) { - await sp - ..setBool(SP.showBackground, event.show); - yield state.copyWith(showBackGround: event.show); - } - - // 切换背景显示事件处理 : 产出新状态 - if (event is EventSwitchShowOver) { - yield state.copyWith(showPerformanceOverlay: event.show); - } - - // 切换code样式事件处理 : 固化索引 + 产出新状态 - if (event is EventSwitchCoderTheme) { - await sp - ..setInt(SP.codeStyleIndex, event.codeStyleIndex); - yield state.copyWith(codeStyleIndex: event.codeStyleIndex); - } - - // 切换item样式事件处理 : 固化索引 + 产出新状态 - if (event is EventChangeItemStyle) { - await sp - ..setInt(SP.itemStyleIndex, event.index); - yield state.copyWith(itemStyleIndex: event.index); - } - } -} diff --git a/lib/blocs/global/global_event.dart b/lib/blocs/global/global_event.dart deleted file mode 100644 index 5117ac04e..000000000 --- a/lib/blocs/global/global_event.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; - -abstract class GlobalEvent extends Equatable { - const GlobalEvent(); - - @override - List get props => []; -} - -class EventInitApp extends GlobalEvent { - const EventInitApp(); - - @override - List get props => []; -} - -class EventSwitchFontFamily extends GlobalEvent { - final String family; - - const EventSwitchFontFamily(this.family); - - @override - List get props => [family]; -} - -class EventSwitchThemeColor extends GlobalEvent { - final MaterialColor color; - - const EventSwitchThemeColor(this.color); - - @override - List get props => [color]; -} - -class EventSwitchCoderTheme extends GlobalEvent { - final int codeStyleIndex; - - const EventSwitchCoderTheme(this.codeStyleIndex); - - @override - List get props => [codeStyleIndex]; -} - -class EventSwitchShowBg extends GlobalEvent { - final bool show; - - const EventSwitchShowBg(this.show); - - @override - List get props => [show]; -} - -class EventSwitchShowOver extends GlobalEvent { - final bool show; - - const EventSwitchShowOver(this.show); - - @override - List get props => [show]; -} - -class EventChangeItemStyle extends GlobalEvent { - final int index; - - const EventChangeItemStyle(this.index); - - @override - List get props => [index]; -} diff --git a/lib/blocs/global/global_state.dart b/lib/blocs/global/global_state.dart deleted file mode 100644 index 8d39f0a8a..000000000 --- a/lib/blocs/global/global_state.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; -/// create by 张风捷特烈 on 2020-04-11 -/// contact me by email 1981462002@qq.com -/// 说明: 全局状态类 -/// -class GlobalState 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; - - const GlobalState({ - this.fontFamily = 'ComicNeue', - this.themeColor = Colors.blue, - this.showBackGround = true, - this.codeStyleIndex = 0, - this.itemStyleIndex = 0, - this.showPerformanceOverlay = false, - }); - - @override - List get props => [ - fontFamily, - themeColor, - showBackGround, - codeStyleIndex, - itemStyleIndex, - showPerformanceOverlay - ]; - - GlobalState copyWith({ - double height, - String fontFamily, - MaterialColor themeColor, - bool showBackGround, - int codeStyleIndex, - int itemStyleIndex, - bool showPerformanceOverlay, - }) => - GlobalState( - fontFamily: fontFamily ?? this.fontFamily, - themeColor: themeColor ?? this.themeColor, - showBackGround: showBackGround ?? this.showBackGround, - codeStyleIndex: codeStyleIndex ?? this.codeStyleIndex, - itemStyleIndex: itemStyleIndex ?? this.itemStyleIndex, - showPerformanceOverlay: showPerformanceOverlay ?? this.showPerformanceOverlay, - ); - - @override - String toString() { - return 'GlobalState{fontFamily: $fontFamily, themeColor: $themeColor, showBackGround: $showBackGround, codeStyleIndex: $codeStyleIndex, itemStyleIndex: $itemStyleIndex, showPerformanceOverlay: $showPerformanceOverlay}'; - } -} diff --git a/lib/blocs/like/like_bloc.dart b/lib/blocs/like/like_bloc.dart deleted file mode 100644 index 2072b76fd..000000000 --- a/lib/blocs/like/like_bloc.dart +++ /dev/null @@ -1,33 +0,0 @@ - -import 'package:flutter/cupertino.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/repositories/rep/widget_repository.dart'; - -import 'like_event.dart'; -import 'like_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(LikeWidgetState(widgets: [])); - - - @override - Stream mapEventToState( - LikeWidgetEvent event, - ) async* { - if (event is ToggleLikeWidgetEvent) { - await repository.toggleLike(event.id); - final widgets = await repository.loadLikeWidgets(); - yield LikeWidgetState(widgets: widgets); - } - if( event is EventLoadLikeData){ - final widgets = await repository.loadLikeWidgets(); - yield LikeWidgetState(widgets: widgets); - } - } -} diff --git a/lib/blocs/like/like_event.dart b/lib/blocs/like/like_event.dart deleted file mode 100644 index 6a0c8e693..000000000 --- a/lib/blocs/like/like_event.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:equatable/equatable.dart'; - -/// create by 张风捷特烈 on 2020-04-09 -/// contact me by email 1981462002@qq.com -/// 说明: - -abstract class LikeWidgetEvent extends Equatable {} - -class EventLoadLikeData extends LikeWidgetEvent { - List get props => []; -} - -class ToggleLikeWidgetEvent extends LikeWidgetEvent { - final int id; - - ToggleLikeWidgetEvent({this.id}); - - @override - // TODO: implement props - List get props => [id]; -} - -class LoadCollectEvent extends LikeWidgetEvent { - @override - List get props => []; -} diff --git a/lib/blocs/like/like_state.dart b/lib/blocs/like/like_state.dart deleted file mode 100644 index e4344ee83..000000000 --- a/lib/blocs/like/like_state.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/model/widget_model.dart'; - -/// create by 张风捷特烈 on 2020-04-09 -/// contact me by email 1981462002@qq.com -/// 说明: - -class LikeWidgetState extends Equatable { - final List widgets; - - LikeWidgetState({this.widgets}); - - @override - // TODO: implement props - List get props => [widgets]; -} diff --git a/lib/blocs/point/point_bloc.dart b/lib/blocs/point/point_bloc.dart deleted file mode 100644 index 501496371..000000000 --- a/lib/blocs/point/point_bloc.dart +++ /dev/null @@ -1,35 +0,0 @@ - -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/api/issues_api.dart'; -import 'package:flutter_unit/blocs/point/point_state.dart'; - - -import 'point_event.dart'; - - -/// create by 张风捷特烈 on 2020-09-03 -/// contact me by email 1981462002@qq.com -/// 说明: - -class PointBloc extends Bloc { - PointBloc() : super(PointLoading()); - - - @override - Stream mapEventToState(PointEvent event) async* { - if (event is EventLoadPoint) { - yield* _mapLoadWidgetToState(); - } - } - - Stream _mapLoadWidgetToState() async* { - yield PointLoading(); - try { - final issues = await IssuesApi.getIssues(); - yield PointLoaded(issues); - } catch (err) { - print(err); - yield PointLoadFailure(err); - } - } -} diff --git a/lib/blocs/point/point_event.dart b/lib/blocs/point/point_event.dart deleted file mode 100644 index 5c686d59c..000000000 --- a/lib/blocs/point/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/blocs/point/point_state.dart b/lib/blocs/point/point_state.dart deleted file mode 100644 index c3da3d2af..000000000 --- a/lib/blocs/point/point_state.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/model/github/issue.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/blocs/point_comment/point_comment_bloc.dart b/lib/blocs/point_comment/point_comment_bloc.dart deleted file mode 100644 index b07e16053..000000000 --- a/lib/blocs/point_comment/point_comment_bloc.dart +++ /dev/null @@ -1,38 +0,0 @@ - -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/api/issues_api.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()); - - - - @override - Stream mapEventToState(PointCommentEvent event) async* { - if (event is EventLoadPointComment) { - yield* _mapLoadWidgetToState(event); - } - } - - Stream _mapLoadWidgetToState(EventLoadPointComment event) async* { - yield PointCommentLoading(event.point); - try { - final comments = await IssuesApi.getIssuesComment(event.point.number); - comments.sort((a,b)=>a.createdAt.compareTo(b.createdAt)); - yield PointCommentLoaded(event.point,comments); - } catch (err) { - print(err); - yield PointCommentLoadFailure(err); - } - } -} diff --git a/lib/blocs/point_comment/point_comment_event.dart b/lib/blocs/point_comment/point_comment_event.dart deleted file mode 100644 index 3dfc044c5..000000000 --- a/lib/blocs/point_comment/point_comment_event.dart +++ /dev/null @@ -1,19 +0,0 @@ - - -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/model/github/issue.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/blocs/point_comment/point_comment_state.dart b/lib/blocs/point_comment/point_comment_state.dart deleted file mode 100644 index 0edcedb68..000000000 --- a/lib/blocs/point_comment/point_comment_state.dart +++ /dev/null @@ -1,54 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/model/github/issue_comment.dart'; -import 'package:flutter_unit/model/github/issue.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/blocs/search/search_bloc.dart b/lib/blocs/search/search_bloc.dart deleted file mode 100644 index 27309f75e..000000000 --- a/lib/blocs/search/search_bloc.dart +++ /dev/null @@ -1,42 +0,0 @@ - - -import 'package:flutter/material.dart'; -import 'package:flutter_unit/repositories/rep/widget_repository.dart'; - -import 'search_event.dart'; -import 'search_state.dart'; -import 'package:bloc/bloc.dart'; -import 'package:flutter_unit/app/utils/stream_ext/ext.dart'; -class SearchBloc extends Bloc { - final WidgetRepository repository; - - SearchBloc({@required this.repository}):super(SearchStateNoSearch()); - - - @override - Stream> transformEvents( - Stream events, TransitionFunction transitionFn) { - return super.transformEvents(events - .debounceTime(Duration(milliseconds: 500),), - transitionFn, - ); - } - - @override - Stream mapEventToState(SearchEvent event,) async* { - if (event is SearchWidgetEvent) { - if (event.args.name.isEmpty&&event.args.stars.every((e)=>e==-1)) { - yield SearchStateNoSearch(); - } else { - yield SearchStateLoading(); - try { - final results = await repository.searchWidgets(event.args); - yield results.length==0?SearchStateEmpty():SearchStateSuccess(results); - } catch (error) { - print(error); - yield SearchStateError(); - } - } - } - } -} \ No newline at end of file diff --git a/lib/blocs/search/search_event.dart b/lib/blocs/search/search_event.dart deleted file mode 100644 index c14bff160..000000000 --- a/lib/blocs/search/search_event.dart +++ /dev/null @@ -1,11 +0,0 @@ - -import 'package:flutter_unit/repositories/dao/widget_dao.dart'; - -abstract class SearchEvent{//事件基 - const SearchEvent(); -} - -class SearchWidgetEvent extends SearchEvent { - final SearchArgs args;//参数 - const SearchWidgetEvent({this.args}); -} diff --git a/lib/blocs/search/search_state.dart b/lib/blocs/search/search_state.dart deleted file mode 100644 index 60254a370..000000000 --- a/lib/blocs/search/search_state.dart +++ /dev/null @@ -1,19 +0,0 @@ - - -import 'package:flutter_unit/model/widget_model.dart'; - -abstract class SearchState {//基态 - const SearchState(); -} - -class SearchStateNoSearch extends SearchState {}//无搜索状态 - -class SearchStateEmpty extends SearchState {}//结果为空 - -class SearchStateLoading extends SearchState {}//加载中 -class SearchStateError extends SearchState {}//异常 - -class SearchStateSuccess extends SearchState {//有结果 - final List result;//搜索结果 - const SearchStateSuccess(this.result); -} diff --git a/lib/blocs/widgets/widgets_bloc.dart b/lib/blocs/widgets/widgets_bloc.dart deleted file mode 100644 index d624c23e9..000000000 --- a/lib/blocs/widgets/widgets_bloc.dart +++ /dev/null @@ -1,39 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/model/enums.dart'; -import 'package:flutter_unit/repositories/rep/widget_repository.dart'; - - -import 'widgets_event.dart'; -import '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(WidgetsLoading(WidgetFamily.statelessWidget)); - - - - @override - Stream mapEventToState(WidgetsEvent event) async* { - if (event is EventTabTap) { - yield* _mapLoadWidgetToState(event.family); - } - } - - Stream _mapLoadWidgetToState(WidgetFamily family) async* { - yield WidgetsLoading(family); - try { - final widgets = await this.repository.loadWidgets(family); - yield WidgetsLoaded(family, widgets: widgets); - } catch (err) { - print(err); - yield WidgetsLoadFailed(family,err.toString()); - } - } -} diff --git a/lib/blocs/widgets/widgets_event.dart b/lib/blocs/widgets/widgets_event.dart deleted file mode 100644 index a5280cf71..000000000 --- a/lib/blocs/widgets/widgets_event.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/model/enums.dart'; - - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明: - -abstract class WidgetsEvent extends Equatable { - const WidgetsEvent(); - @override - List get props => []; -} - -class EventTabTap extends WidgetsEvent { - final WidgetFamily family; - - EventTabTap(this.family); - -} - - diff --git a/lib/blocs/widgets/widgets_state.dart b/lib/blocs/widgets/widgets_state.dart deleted file mode 100644 index 02d6000a9..000000000 --- a/lib/blocs/widgets/widgets_state.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_unit/model/enums.dart'; -import 'package:flutter_unit/app/res/cons.dart'; -import 'package:flutter_unit/model/widget_model.dart'; - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明: 主页 Widget 列表 状态类 - -abstract class WidgetsState extends Equatable { - final WidgetFamily activeFamily; - - const WidgetsState(this.activeFamily); - - Color get color => Color(Cons.tabColors[activeFamily.index]); - Color get nextColor => Color(Cons.tabColors[(activeFamily.index+1)%Cons.tabColors.length]); - - @override - List get props => [activeFamily]; -} - -class WidgetsLoading extends WidgetsState { - WidgetsLoading(WidgetFamily activeFamily) : super(activeFamily); - - @override - List get props => [activeFamily]; -} - -class WidgetsLoaded extends WidgetsState { - final List widgets; - - WidgetsLoaded(WidgetFamily activeFamily, {this.widgets = const []}) - : super(activeFamily); - - @override - List get props => [activeFamily,widgets]; - - @override - String toString() { - return 'WidgetsLoaded{widgets: $widgets}'; - } -} - -class WidgetsLoadFailed extends WidgetsState { - final String error; - - const WidgetsLoadFailed(WidgetFamily activeFamily, this.error) - : super(activeFamily); - - @override - List get props => [activeFamily,error]; -} diff --git a/lib/main.dart b/lib/main.dart index 95bab34c8..442228576 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,13 +1,3 @@ -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/pages/app/bloc_wrapper.dart'; -import 'views/pages/app/flutter_unit.dart'; - -void main() { - WidgetsFlutterBinding.ensureInitialized(); - //滚动性能优化 1.22.0 - GestureBinding.instance.resamplingEnabled = true; - runApp(BlocWrapper(child: FlutterUnit())); -} - +import 'src/starter/fx_application.dart'; +void main(List args) => const FxApplication().run(args); diff --git a/lib/model/category_model.dart b/lib/model/category_model.dart deleted file mode 100644 index da1f3ef64..000000000 --- a/lib/model/category_model.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/utils/color_utils.dart'; -import 'package:flutter_unit/repositories/bean/category_po.dart'; -import 'package:intl/intl.dart'; - -/// create by 张风捷特烈 on 2020-04-21 -/// contact me by email 1981462002@qq.com -/// 说明: 收藏夹展示数据模型 - -class CategoryModel extends Equatable { - final int id; - final String name; - final String info; - final String createDate; - final String imageCover; - final int count; - final Color color; - - CategoryModel( - {this.name, - this.id, - this.info, - this.createDate, - this.imageCover, - this.count, - this.color}); - - bool get canDelete => id > 1; - - static CategoryModel fromPo(CategoryPo po) { - return CategoryModel( - id: po.id, - name: po.name, - info: po.info, - createDate: DateFormat('yyyy-MM-dd HH:mm').format(po.created), - imageCover: po.image, - count: po.count, - color: ColorUtils.parse(po.color), - ); - } - - @override - List get props => [ - id, - name, - info, - createDate, - imageCover, - count, - color, - ]; - - @override - String toString() { - return 'CategoryModel{id: $id, name: $name, info: $info, createDate: $createDate, imageCover: $imageCover, count: $count, color: $color}'; - } -} - - -// 收藏集的 To 对象,用与上传/同步 收藏集 -class CategoryTo{ - final CategoryPo model; - final List widgetIds; - final List likesData; - - CategoryTo({this.model, this.widgetIds,this.likesData}); - - Map toJson() => { - "model": this.model, - "widgetIds": this.widgetIds, - "likesData":this.likesData - }; -} \ No newline at end of file diff --git a/lib/model/enums.dart b/lib/model/enums.dart deleted file mode 100644 index 37a33388c..000000000 --- a/lib/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/lib/model/github/g/github_user.g.dart b/lib/model/github/g/github_user.g.dart deleted file mode 100644 index 96866ebb5..000000000 --- a/lib/model/github/g/github_user.g.dart +++ /dev/null @@ -1,93 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of '../github_user.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -GithubUser _$UserFromJson(Map json) { - return GithubUser( - json['login'] as String, - json['id'] as int, - json['node_id'] as String, - json['avatar_url'] as String, - json['gravatar_id'] as String, - json['url'] as String, - json['html_url'] as String, - json['followers_url'] as String, - json['following_url'] as String, - json['gists_url'] as String, - json['starred_url'] as String, - json['subscriptions_url'] as String, - json['organizations_url'] as String, - json['repos_url'] as String, - json['events_url'] as String, - json['received_events_url'] as String, - json['type'] as String, - json['site_admin'] as bool, - json['name'] as String, - json['company'] as String, - json['blog'] as String, - json['location'] as String, - json['email'] as String, - json['starred'] as String, - json['bio'] as String, - json['public_repos'] as int, - json['public_gists'] as int, - json['followers'] as int, - json['following'] as int, - json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - json['private_gists'] as int, - json['total_private_repos'] as int, - json['owned_private_repos'] as int, - json['disk_usage'] as int, - json['collaborators'] as int, - json['two_factor_authentication'] as bool, - ); -} - -Map _$UserToJson(GithubUser instance) => { - 'login': instance.login, - 'id': instance.id, - 'node_id': instance.nodeId, - 'avatar_url': instance.avatarUrl, - 'gravatar_id': instance.gravatarId, - 'url': instance.url, - 'html_url': instance.htmlUrl, - 'followers_url': instance.followersUrl, - 'following_url': instance.followingUrl, - 'gists_url': instance.gistsUrl, - 'starred_url': instance.starredUrl, - 'subscriptions_url': instance.subscriptionsUrl, - 'organizations_url': instance.organizationsUrl, - 'repos_url': instance.reposUrl, - 'events_url': instance.eventsUrl, - 'received_events_url': instance.receivedEventsUrl, - 'type': instance.type, - 'site_admin': instance.siteAdmin, - 'name': instance.name, - 'company': instance.company, - 'blog': instance.blog, - 'location': instance.location, - 'email': instance.email, - 'starred': instance.starred, - 'bio': instance.bio, - 'public_repos': instance.publicRepos, - 'public_gists': instance.publicGists, - 'followers': instance.followers, - 'following': instance.following, - 'created_at': instance.createdAt?.toIso8601String(), - 'updated_at': instance.updatedAt?.toIso8601String(), - 'private_gists': instance.privateGists, - 'total_private_repos': instance.totalPrivateRepos, - 'owned_private_repos': instance.ownedPrivateRepos, - 'disk_usage': instance.diskUsage, - 'collaborators': instance.collaborators, - 'two_factor_authentication': instance.twoFactorAuthentication, -}; diff --git a/lib/model/github/g/issue.g.dart b/lib/model/github/g/issue.g.dart deleted file mode 100644 index 193380c8d..000000000 --- a/lib/model/github/g/issue.g.dart +++ /dev/null @@ -1,55 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of '../issue.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -Issue _$IssueFromJson(dynamic json) { - return Issue( - json['id'] as int, - json['number'] as int, - json['title'] as String, - json['state'] as String, - json['locked'] as bool, - json['comments'] as int, - json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - json['closed_at'] == null - ? null - : DateTime.parse(json['closed_at'] as String), - json['body'] as String, - json['body_html'] as String, - json['user'] == null - ? null - : GithubUser.fromJson(json['user'] as Map), - json['repository_url'] as String, - json['html_url'] as String, - json['closed_by'] == null - ? null - : GithubUser.fromJson(json['closed_by'] as Map), - ); -} - -Map _$IssueToJson(Issue instance) => { - 'id': instance.id, - 'number': instance.number, - 'title': instance.title, - 'state': instance.state, - 'locked': instance.locked, - 'comments': instance.commentNum, - 'created_at': instance.createdAt?.toIso8601String(), - 'updated_at': instance.updatedAt?.toIso8601String(), - 'closed_at': instance.closedAt?.toIso8601String(), - 'body': instance.body, - 'body_html': instance.bodyHtml, - 'user': instance.user, - 'repository_url': instance.repoUrl, - 'html_url': instance.htmlUrl, - 'closed_by': instance.closeBy, - }; diff --git a/lib/model/github/g/issue_comment.g.dart b/lib/model/github/g/issue_comment.g.dart deleted file mode 100644 index 65c20fde0..000000000 --- a/lib/model/github/g/issue_comment.g.dart +++ /dev/null @@ -1,40 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of '../issue_comment.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -IssueComment _$IssueEventFromJson(Map json) { - return IssueComment( - json['id'] as int, - json['user'] == null - ? null - : GithubUser.fromJson(json['user'] as Map), - json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - json['author_association'] as String, - json['body'] as String, - json['body_html'] as String, - json['event'] as String, - json['html_url'] as String, - ); -} - -Map _$IssueEventToJson(IssueComment instance) => - { - 'id': instance.id, - 'user': instance.user, - 'created_at': instance.createdAt?.toIso8601String(), - 'updated_at': instance.updatedAt?.toIso8601String(), - 'author_association': instance.authorAssociation, - 'body': instance.body, - 'body_html': instance.bodyHtml, - 'event': instance.type, - 'html_url': instance.htmlUrl, - }; diff --git a/lib/model/github/g/license.g.dart b/lib/model/github/g/license.g.dart deleted file mode 100644 index a38d6f0b1..000000000 --- a/lib/model/github/g/license.g.dart +++ /dev/null @@ -1,19 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of '../license.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -License _$LicenseFromJson(Map json) { - return License( - json['name'] as String, - json['spdx_id'] as String, - ); -} - -Map _$LicenseToJson(License instance) => { - 'name': instance.name, - 'spdx_id': instance.spdxId, - }; diff --git a/lib/model/github/g/repository.g.dart b/lib/model/github/g/repository.g.dart deleted file mode 100644 index 089d10fc6..000000000 --- a/lib/model/github/g/repository.g.dart +++ /dev/null @@ -1,96 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of '../repository.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -Repository _$RepositoryFromJson(Map json) { - return Repository( - json['id'] as int, - json['size'] as int, - json['name'] as String, - json['full_name'] as String, - json['html_url'] as String, - json['description'] as String, - json['language'] as String, - json['license'] == null - ? null - : License.fromJson(json['license'] as Map), - json['default_branch'] as String, - json['created_at'] == null - ? null - : DateTime.parse(json['created_at'] as String), - json['updated_at'] == null - ? null - : DateTime.parse(json['updated_at'] as String), - json['pushed_at'] == null - ? null - : DateTime.parse(json['pushed_at'] as String), - json['git_url'] as String, - json['ssh_url'] as String, - json['clone_url'] as String, - json['svn_url'] as String, - json['stargazers_count'] as int, - json['watchers_count'] as int, - json['forks_count'] as int, - json['open_issues_count'] as int, - json['subscribers_count'] as int, - json['private'] as bool, - json['fork'] as bool, - json['has_issues'] as bool, - json['has_projects'] as bool, - json['has_downloads'] as bool, - json['has_wiki'] as bool, - json['has_pages'] as bool, - json['owner'] == null - ? null - : GithubUser.fromJson(json['owner'] as Map), - json['parent'] == null - ? null - : Repository.fromJson(json['parent'] as Map), - json['permissions'] == null - ? null - : RepositoryPermissions.fromJson( - json['permissions'] as Map), - (json['topics'] as List)?.map((e) => e as String)?.toList(), - )..allIssueCount = json['allIssueCount'] as int; -} - -Map _$RepositoryToJson(Repository instance) => - { - 'id': instance.id, - 'size': instance.size, - 'name': instance.name, - 'full_name': instance.fullName, - 'html_url': instance.htmlUrl, - 'description': instance.description, - 'language': instance.language, - 'default_branch': instance.defaultBranch, - 'created_at': instance.createdAt?.toIso8601String(), - 'updated_at': instance.updatedAt?.toIso8601String(), - 'pushed_at': instance.pushedAt?.toIso8601String(), - 'git_url': instance.gitUrl, - 'ssh_url': instance.sshUrl, - 'clone_url': instance.cloneUrl, - 'svn_url': instance.svnUrl, - 'stargazers_count': instance.stargazersCount, - 'watchers_count': instance.watchersCount, - 'forks_count': instance.forksCount, - 'open_issues_count': instance.openIssuesCount, - 'subscribers_count': instance.subscribersCount, - 'private': instance.private, - 'fork': instance.fork, - 'has_issues': instance.hasIssues, - 'has_projects': instance.hasProjects, - 'has_downloads': instance.hasDownloads, - 'has_wiki': instance.hasWiki, - 'has_pages': instance.hasPages, - 'owner': instance.owner, - 'license': instance.license, - 'parent': instance.parent, - 'permissions': instance.permissions, - 'topics': instance.topics, - 'allIssueCount': instance.allIssueCount, - }; diff --git a/lib/model/github/g/repository_permissions.g.dart b/lib/model/github/g/repository_permissions.g.dart deleted file mode 100644 index 699ed7c4d..000000000 --- a/lib/model/github/g/repository_permissions.g.dart +++ /dev/null @@ -1,24 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of '../repository_permissions.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -RepositoryPermissions _$RepositoryPermissionsFromJson( - Map json) { - return RepositoryPermissions( - json['admin'] as bool, - json['push'] as bool, - json['pull'] as bool, - ); -} - -Map _$RepositoryPermissionsToJson( - RepositoryPermissions instance) => - { - 'admin': instance.admin, - 'push': instance.push, - 'pull': instance.pull, - }; diff --git a/lib/model/github/github_user.dart b/lib/model/github/github_user.dart deleted file mode 100644 index 1558c03fb..000000000 --- a/lib/model/github/github_user.dart +++ /dev/null @@ -1,94 +0,0 @@ -/// create by 张风捷特烈 on 2020/6/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -part 'g/github_user.g.dart'; - -class GithubUser { - GithubUser( - this.login, - this.id, - this.nodeId, - this.avatarUrl, - this.gravatarId, - this.url, - this.htmlUrl, - this.followersUrl, - this.followingUrl, - this.gistsUrl, - this.starredUrl, - this.subscriptionsUrl, - this.organizationsUrl, - this.reposUrl, - this.eventsUrl, - this.receivedEventsUrl, - this.type, - this.siteAdmin, - this.name, - this.company, - this.blog, - this.location, - this.email, - this.starred, - this.bio, - this.publicRepos, - this.publicGists, - this.followers, - this.following, - this.createdAt, - this.updatedAt, - this.privateGists, - this.totalPrivateRepos, - this.ownedPrivateRepos, - this.diskUsage, - this.collaborators, - this.twoFactorAuthentication); - - String login; - int id; - String nodeId; - String avatarUrl; - String gravatarId; - String url; - String htmlUrl; - String followersUrl; - String followingUrl; - String gistsUrl; - String starredUrl; - String subscriptionsUrl; - String organizationsUrl; - String reposUrl; - String eventsUrl; - String receivedEventsUrl; - String type; - bool siteAdmin; - String name; - String company; - String blog; - String location; - String email; - String starred; - String bio; - int publicRepos; - int publicGists; - int followers; - int following; - DateTime createdAt; - DateTime updatedAt; - int privateGists; - int totalPrivateRepos; - int ownedPrivateRepos; - int diskUsage; - int collaborators; - bool twoFactorAuthentication; - - - factory GithubUser.fromJson(Map json) => _$UserFromJson(json); - - - Map toJson() => _$UserToJson(this); - - // 命名构造函数 - GithubUser.empty(); - -} diff --git a/lib/model/github/issue.dart b/lib/model/github/issue.dart deleted file mode 100644 index 62634e81e..000000000 --- a/lib/model/github/issue.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/model/github/github_user.dart'; - -part 'g/issue.g.dart'; - -class Issue extends Equatable { - final int id; - final int number; - final String title; - final String state; - final bool locked; - - final int commentNum; - - final DateTime createdAt; - - final DateTime updatedAt; - - final DateTime closedAt; - final String body; - - final String bodyHtml; - - final GithubUser user; - - final String repoUrl; - - final String htmlUrl; - - final GithubUser closeBy; - - Issue( - this.id, - this.number, - this.title, - this.state, - this.locked, - this.commentNum, - this.createdAt, - this.updatedAt, - this.closedAt, - this.body, - this.bodyHtml, - this.user, - this.repoUrl, - this.htmlUrl, - this.closeBy, - ); - - static Issue fromJson(dynamic json) => _$IssueFromJson(json); - - Map toJson() => _$IssueToJson(this); - - @override - String toString() { - return 'Issue{id: $id, number: $number, title: $title, state: $state, locked: $locked, commentNum: $commentNum, createdAt: $createdAt, updatedAt: $updatedAt, closedAt: $closedAt, body: $body, bodyHtml: $bodyHtml, user: $user, repoUrl: $repoUrl, htmlUrl: $htmlUrl, closeBy: $closeBy}'; - } - - @override - List get props => [ - id, - number, - title, - state, - locked, - commentNum, - createdAt, - updatedAt, - closedAt, - body, - bodyHtml, - user, - repoUrl, - htmlUrl, - closeBy, - ]; -} diff --git a/lib/model/github/issue_comment.dart b/lib/model/github/issue_comment.dart deleted file mode 100644 index c426f2dba..000000000 --- a/lib/model/github/issue_comment.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:flutter_unit/model/github/github_user.dart'; - -part 'g/issue_comment.g.dart'; - - -class IssueComment{ - int id; - GithubUser user; - - DateTime createdAt; - - DateTime updatedAt; - - String authorAssociation; - String body; - - String bodyHtml; - String type; - String htmlUrl; - - IssueComment( - this.id, - this.user, - this.createdAt, - this.updatedAt, - this.authorAssociation, - this.body, - this.bodyHtml, - this.type, - this.htmlUrl, - ); - - factory IssueComment.fromJson(Map json) => _$IssueEventFromJson(json); - - Map toJson() => _$IssueEventToJson(this); -} diff --git a/lib/model/github/license.dart b/lib/model/github/license.dart deleted file mode 100644 index d9f70d1c7..000000000 --- a/lib/model/github/license.dart +++ /dev/null @@ -1,14 +0,0 @@ - -part 'g/license.g.dart'; - -class License { - - String name; - String spdxId; - - License(this.name,this.spdxId); - - factory License.fromJson(Map json) => _$LicenseFromJson(json); - - Map toJson() => _$LicenseToJson(this); -} diff --git a/lib/model/github/repository.dart b/lib/model/github/repository.dart deleted file mode 100644 index 1ab793142..000000000 --- a/lib/model/github/repository.dart +++ /dev/null @@ -1,137 +0,0 @@ - - -import 'package:flutter_unit/model/github/github_user.dart'; - -import 'license.dart'; -import 'repository_permissions.dart'; - -part 'g/repository.g.dart'; - -class Repository { - int id; - - int size; - - String name; - - String fullName; - - - String htmlUrl; - - String description; - - String language; - - - String defaultBranch; - - - DateTime createdAt; - - - DateTime updatedAt; - - DateTime pushedAt; - - - String gitUrl; - - - String sshUrl; - - - String cloneUrl; - - - String svnUrl; - - - int stargazersCount; - - - int watchersCount; - - - int forksCount; - - - int openIssuesCount; - - - int subscribersCount; - - - bool private; - - bool fork; - - bool hasIssues; - - bool hasProjects; - - bool hasDownloads; - - bool hasWiki; - - bool hasPages; - - GithubUser owner; - - License license; - - Repository parent; - - RepositoryPermissions permissions; - - List topics; - - int allIssueCount; - - Repository( - this.id, - this.size, - this.name, - this.fullName, - this.htmlUrl, - this.description, - this.language, - this.license, - this.defaultBranch, - this.createdAt, - this.updatedAt, - this.pushedAt, - this.gitUrl, - this.sshUrl, - this.cloneUrl, - this.svnUrl, - this.stargazersCount, - this.watchersCount, - this.forksCount, - this.openIssuesCount, - this.subscribersCount, - this.private, - this.fork, - this.hasIssues, - this.hasProjects, - this.hasDownloads, - this.hasWiki, - this.hasPages, - this.owner, - this.parent, - this.permissions, - this.topics, - ); - - factory Repository.fromJson(Map json) => _$RepositoryFromJson(json); - - - @override - String toString() { - return 'Repository{id: $id, size: $size, name: $name, fullName: $fullName, htmlUrl: $htmlUrl, description: $description, language: $language, defaultBranch: $defaultBranch, createdAt: $createdAt, updatedAt: $updatedAt, pushedAt: $pushedAt, gitUrl: $gitUrl, sshUrl: $sshUrl, cloneUrl: $cloneUrl, svnUrl: $svnUrl, stargazersCount: $stargazersCount, watchersCount: $watchersCount, forksCount: $forksCount, openIssuesCount: $openIssuesCount, subscribersCount: $subscribersCount, private: $private, fork: $fork, hasIssues: $hasIssues, hasProjects: $hasProjects, hasDownloads: $hasDownloads, hasWiki: $hasWiki, hasPages: $hasPages, owner: $owner, license: $license, parent: $parent, permissions: $permissions, topics: $topics, allIssueCount: $allIssueCount}'; - } - - Map toJson() => _$RepositoryToJson(this); - - Repository.empty(); -} diff --git a/lib/model/github/repository_permissions.dart b/lib/model/github/repository_permissions.dart deleted file mode 100644 index 53723069d..000000000 --- a/lib/model/github/repository_permissions.dart +++ /dev/null @@ -1,18 +0,0 @@ - - -part 'g/repository_permissions.g.dart'; - -class RepositoryPermissions { - bool admin; - bool push; - bool pull; - - RepositoryPermissions( - this.admin, - this.push, - this.pull, - ); - - factory RepositoryPermissions.fromJson(Map json) => _$RepositoryPermissionsFromJson(json); - Map toJson() => _$RepositoryPermissionsToJson(this); -} diff --git a/lib/model/node_model.dart b/lib/model/node_model.dart deleted file mode 100644 index 591937ca1..000000000 --- a/lib/model/node_model.dart +++ /dev/null @@ -1,29 +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({this.name, this.subtitle, 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}'; - } -} \ No newline at end of file diff --git a/lib/model/widget_model.dart b/lib/model/widget_model.dart deleted file mode 100644 index 406d33435..000000000 --- a/lib/model/widget_model.dart +++ /dev/null @@ -1,86 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter_unit/app/utils/convert.dart'; -import 'package:flutter_unit/model/enums.dart'; -import 'package:flutter_unit/app/res/cons.dart'; -import 'package:flutter_unit/repositories/bean/widget_po.dart'; - -/// create by 张风捷特烈 on 2020-03-04 -/// contact me by email 1981462002@qq.com -/// 说明: 组件信息-展示-数据模型 -/// - -enum WidgetType { - singe_show, //单一展示型 0 - structure, //结构型 1 - shape, //可形变 2 - event, //事件型 3 - ability, //功能型 4 - layout, //布局型 5 - scroll, //滚动型 -} - -class WidgetModel extends Equatable { - final int id; - final String name; - final String nameCN; - final WidgetFamily family; - final bool deprecated; - final WidgetType type; - final List links; - final double lever; - final ImageProvider image; - final String info; - - Color get color => Color(Cons.tabColors[family.index]); - - const WidgetModel( - {this.id, - this.name, - this.nameCN, - this.family, - this.deprecated, - this.links, - this.type, - this.lever, - this.image, - this.info}); - - @override - List get props => [id]; - - static WidgetModel fromPo(WidgetPo po) { - return WidgetModel( - id: po.id, - name: po.name, - nameCN: po.nameCN, - family: Convert.toFamily(po.family), - image: convertImage(po.name), - lever: po.lever, - deprecated: 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, type: $type, 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(); - } -} diff --git a/lib/painter_system/base/clock_widget.dart b/lib/painter_system/base/clock_widget.dart deleted file mode 100644 index 0c0483b00..000000000 --- a/lib/painter_system/base/clock_widget.dart +++ /dev/null @@ -1,339 +0,0 @@ -import 'dart:math'; -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; - -/// create by 张风捷特烈 on 2021/2/7 -/// contact me by email 1981462002@qq.com -/// 说明: - -class ClockWidget extends StatefulWidget { - final double radius; - - const ClockWidget({Key key, this.radius = 100}) : super(key: key); - - @override - _ClockWidgetState createState() => _ClockWidgetState(); -} - -class _ClockWidgetState extends State - with SingleTickerProviderStateMixin { - Ticker _ticker; - ValueNotifier time = ValueNotifier(DateTime.now()); - - @override - void initState() { - super.initState(); - _ticker = createTicker(_tick)..start(); - } - - @override - void dispose() { - _ticker.dispose(); - time.dispose(); - super.dispose(); - } - - void _tick(Duration duration) { - if (time.value.second != DateTime.now().second) { - time.value = DateTime.now(); - } - } - - @override - Widget build(BuildContext context) { - return Stack( - alignment: Alignment.center, - children: [ - CustomPaint( - size: Size(widget.radius * 2, widget.radius * 2), - painter: ClockBgPainter(radius: widget.radius), - ), - RepaintBoundary( - child: CustomPaint( - size: Size(widget.radius * 2, widget.radius * 2), - painter: ClockPainter(listenable: time, radius: widget.radius), - ), - ) - ], - ); - } -} - -class ClockBgPainter extends CustomPainter { - final Paint _paint = Paint()..style = PaintingStyle.stroke; - - final double radius; - - ClockBgPainter({this.radius = 100}); - - @override - void paint(Canvas canvas, Size size) { - - canvas.translate(size.width / 2, size.height / 2); - drawOuterCircle(canvas); - drawScale(canvas); - drawText(canvas); - } - - @override - bool shouldRepaint(covariant ClockBgPainter oldDelegate) { - return oldDelegate.radius != radius; - } - - void drawOuterCircle(Canvas canvas) { - _paint - ..strokeWidth = 4 - ..color = const Color(0xffD5D5D5); - for (int i = 0; i < 4; i++) { - _paintArc(canvas); - canvas.rotate(pi / 2); - } - } - - final Paint arcPaint = Paint() - ..style = PaintingStyle.fill - ..color = const Color(0xff00abf2); - - void _paintArc(Canvas canvas) { - arcPaint.maskFilter = MaskFilter.blur(BlurStyle.solid, logic1); - final Path circlePath = Path() - ..addArc( - Rect.fromCenter( - center: Offset(0, 0), width: radius * 2, height: radius * 2), - 10 / 180 * pi, - pi / 2 - 20 / 180 * pi); - - Path circlePath2 = Path() - ..addArc( - Rect.fromCenter( - center: Offset(-logic1, 0), - width: radius * 2, - height: radius * 2), - 10 / 180 * pi, - pi / 2 - 20 / 180 * pi); - //联合路径 - Path result = - Path.combine(PathOperation.difference, circlePath, circlePath2); - canvas.drawPath(result, arcPaint); //绘制 - } - - void drawScale(Canvas canvas) { - _paint - ..strokeCap = StrokeCap.round - ..style = PaintingStyle.fill; - - double count = 60; - double perAngle = 2 * pi / count; - - for (int i = 0; i < count; i++) { - if (i % 5 == 0) { - _paint - ..strokeWidth = longLineWidth - ..color = Colors.blue; - canvas.drawLine(Offset(radius - scaleSpace, 0), - Offset(radius - scaleSpace - longScaleLen, 0), _paint); - canvas.drawCircle( - Offset(radius - scaleSpace - longScaleLen - logic1 * 5, 0), - longLineWidth, - _paint..color = Colors.orange); - } else { - _paint - ..strokeWidth = shortLenWidth - ..color = Colors.black; - canvas.drawLine(Offset(radius - scaleSpace, 0), - Offset(radius - scaleSpace - shortScaleLen, 0), _paint); - } - canvas.rotate(perAngle); - } - } - - double get logic1 => radius * 0.01; - - // 刻度与外圈的间隔 - double get scaleSpace => logic1 * 11; - - // 短刻度线长 - double get shortScaleLen => logic1 * 7; - - // 短刻度线长 - double get shortLenWidth => logic1; - - // 长刻度线长 - double get longScaleLen => logic1 * 11; - - // 长刻度线宽 - double get longLineWidth => logic1 * 2; - - final TextPainter _textPainter = TextPainter( - textAlign: TextAlign.center, textDirection: TextDirection.ltr); - - void drawText(Canvas canvas) { - _drawCircleText(canvas, 'Ⅸ', offsetX: -radius); - _drawCircleText(canvas, 'Ⅲ', offsetX: radius); - _drawCircleText(canvas, 'Ⅵ', offsetY: radius); - _drawCircleText(canvas, 'Ⅻ', offsetY: -radius); - _drawLogoText(canvas, offsetY: -radius * 0.5); - } - - _drawCircleText(Canvas canvas, String text, - {double offsetX = 0, double offsetY = 0}) { - _textPainter.text = TextSpan( - text: text, - style: TextStyle(fontSize: radius * 0.15, color: Colors.blue)); - _textPainter.layout(); - _textPainter.paint( - canvas, - Offset.zero.translate(-_textPainter.size.width / 2 + offsetX, - -_textPainter.height / 2 + offsetY)); - } - - _drawLogoText(Canvas canvas, {double offsetX = 0, double offsetY = 0}) { - _textPainter.text = TextSpan( - text: 'Toly', - style: TextStyle( - fontSize: radius * 0.2, color: Colors.blue, fontFamily: 'CHOPS')); - _textPainter.layout(); - _textPainter.paint( - canvas, - Offset.zero.translate(-_textPainter.size.width / 2 + offsetX, - -_textPainter.height / 2 + offsetY)); - } -} - -class ClockPainter extends CustomPainter { - final Paint _paint = Paint()..style = PaintingStyle.stroke; - - final double radius; - final ValueListenable listenable; - - ClockPainter({this.listenable, this.radius = 100}) - : super(repaint: listenable); - - @override - void paint(Canvas canvas, Size size) { - - canvas.translate(size.width / 2, size.height / 2); - drawArrow(canvas, listenable.value); - } - - void drawArrow(Canvas canvas, DateTime time) { - int sec = time.second; - int min = time.minute; - int hour = time.hour; - - double perAngle = 360 / 60; - - double secondRad = (sec * perAngle) / 180 * pi; - double minusRad = ((min + sec / 60) * perAngle) / 180 * pi; - double hourRad = ((hour + min / 60 + sec / 3600) * perAngle * 5) / 180 * pi; - - canvas.save(); - canvas.rotate(-pi / 2); - canvas.save(); - canvas.rotate(minusRad); - drawMinus(canvas); - canvas.restore(); - - canvas.save(); - canvas.rotate(hourRad); - drawHour(canvas); - canvas.restore(); - - canvas.save(); - canvas.rotate(secondRad); - drawSecond(canvas); - canvas.restore(); - canvas.restore(); - } - - @override - bool shouldRepaint(covariant ClockPainter oldDelegate) { - return oldDelegate.radius != radius || oldDelegate.listenable != listenable; - } - - // 分针长 - double get minusLen => logic1 * 60; - - // 时针长 - double get hourLen => logic1 * 45; - - // 秒针长 - double get secondLen => logic1 * 68; - - // 时针线宽 - double get hourLineWidth => logic1 * 3; - - // 分针线宽 - double get minusLineWidth => logic1 * 2; - - // 秒针线宽 - double get logic1 => radius * 0.01; - - double get secondLineWidth => logic1; - - // 长刻度线宽 - double get longLineWidth => logic1 * 2; - - void drawHour(Canvas canvas) { - _paint - ..strokeWidth = hourLineWidth - ..color = Color(0xff8FC552) - ..strokeCap = StrokeCap.round; - canvas.drawLine(Offset.zero, Offset(hourLen, 0), _paint); - } - - void drawMinus(Canvas canvas) { - _paint - ..strokeWidth = minusLineWidth - ..color = Color(0xff87B953) - ..strokeCap = StrokeCap.round; - - canvas.drawLine( - Offset.zero, - Offset( - minusLen, - 0, - ), - _paint); - } - - void drawSecond(Canvas canvas) { - _paint - ..strokeWidth = logic1 * 2.5 - ..color = Color(0xff6B6B6B) - ..strokeCap = StrokeCap.square - ..style = PaintingStyle.stroke; - Path path = Path(); - - canvas.save(); - canvas.rotate((360 - 270) / 2 / 180 * pi); - path.addArc( - Rect.fromPoints( - Offset(-logic1 * 9, -logic1 * 9), Offset(logic1 * 9, logic1 * 9)), - 0, - 270 / 180 * pi); - canvas.drawPath(path, _paint); - canvas.restore(); - - _paint..strokeCap = StrokeCap.round; - canvas.drawLine(Offset(-logic1 * 9, 0), Offset(-logic1 * 20, 0), _paint); - - _paint - ..strokeWidth = logic1 - ..color = Colors.black; - canvas.drawLine(Offset.zero, Offset(secondLen, 0), _paint); - - _paint - ..strokeWidth = logic1 * 3 - ..color = const Color(0xff6B6B6B); - canvas.drawCircle(Offset.zero, logic1 * 5, _paint); - - _paint - ..color = const Color(0xff8FC552) - ..style = PaintingStyle.fill; - canvas.drawCircle(Offset.zero, logic1 * 4, _paint); - } -} diff --git a/lib/painter_system/gallery.dart b/lib/painter_system/gallery.dart deleted file mode 100644 index 6d0f3929b..000000000 --- a/lib/painter_system/gallery.dart +++ /dev/null @@ -1,191 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -import 'art/circle_packing.dart'; -import 'art/cubic_disarray.dart'; -import 'base/draw_picture.dart'; -import 'art/hypnotic_squares.dart'; -import 'art/joy_division.dart'; -import 'art/piet_mondrian.dart'; -import 'fun/random_portrait.dart'; -import 'art/tiled_lines.dart'; -import 'art/triangular_mesh.dart'; -import 'art/un_deux_trois.dart'; -import 'anim/draw_path.dart'; -import 'picture_frame.dart'; - -/// create by 张风捷特烈 on 2020/10/10 -/// contact me by email 1981462002@qq.com -/// 说明: - -class Gallery extends StatefulWidget { - @override - _GalleryState createState() => _GalleryState(); -} - -class _GalleryState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - body: Column(children: [ - // _buildTitle(), - Expanded( - child: Container( - // color: Colors.lightBlueAccent, - child: Center( - child: PageView( - children: [ - FrameShower( - title: "The Chaos", - author: "张风捷特烈", - info: - " 本样例介绍如何进行图片的绘制。通过加载图片并将图片资源绘制到指定的区域。在上层绘制一批45°倾角的栅格线,可以练习基本的绘制。", - content: DrawPicture()), - FrameShower( - title: "Draw Curve", - author: "张风捷特烈", - info: - " 本样例介绍如何使用路径绘制函数曲线,并使用路径测量进行动画", - content: DrawPath()), - FrameShower( - title: "Random Portrait", - author: "张风捷特烈", - info: - " 本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。其中对点的随机生成和对称处理能让你练习对数据的控制力。", - content: RandomPortrait()), - FrameShower( - title: "Tiled Line", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的tiled-lines,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: TiledLines(), - ), - FrameShower( - title: "Joy Division", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的joy-division,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: JoyDivision(), - ), - FrameShower( - title: "Cubic Disarray", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的cubic-disarray,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: CubicDisarray(), - ), - FrameShower( - title: "Triangular Mesh", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的triangular-mesh,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: TriangularMesh(), - ), - FrameShower( - title: "Un Deux Trois", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的un-deux-trois,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: UnDeuxTrois(), - ), - FrameShower( - title: "Circle Packing", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的circle-packing,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: CirclePacking(), - ), - FrameShower( - title: "Hypnotic Squares", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的hypnotic-squares,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: HypnoticSquares(), - ), - FrameShower( - title: "Piet Mondrian", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的piet-mondrian,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: PietMondrian(), - ) - ], - ), - ), - ), - ), - ])); - } -} - -class FrameShower extends StatelessWidget { - final String title; - final String author; - final String srcUrl; - final String info; - final Widget content; - - FrameShower( - {Key key, - this.title = "", - this.author = "", - this.srcUrl = "", - this.info = "", - @required this.content}) - : super(key: key); - - @override - Widget build(BuildContext context) { - return Align( - alignment: Alignment.topCenter, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox(height: 15,), - Text( - title, - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - PictureFrame( - width: MediaQuery.of(context).size.shortestSide, - height: MediaQuery.of(context).size.shortestSide, - child: content, - ), - - Container( - padding: EdgeInsets.symmetric(horizontal: 12), - child: Row( - children: [ - Text( - "作者: $author ", - style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold), - ), - Spacer(), - Text( - "源码地址 ", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: Colors.blueAccent), - ), - ], - ), - ), - SizedBox( - height: 10, - ), - Container( - padding: EdgeInsets.symmetric(horizontal: 12), - alignment: Alignment.topLeft, - child: Text( - info, - style: TextStyle( - fontSize: 12, - color: Colors.grey, - fontWeight: FontWeight.bold), - )), - ], - ), - ); - } -} diff --git a/lib/painter_system/gallery_factory.dart b/lib/painter_system/gallery_factory.dart deleted file mode 100644 index e97901f9c..000000000 --- a/lib/painter_system/gallery_factory.dart +++ /dev/null @@ -1,175 +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 '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/draw_grid_axis.dart'; -import 'base/draw_path_fun.dart'; -import 'base/draw_picture.dart'; -import 'fun/random_portrait.dart'; -import 'fun/stemp/stamp_paper.dart'; -import 'gallery.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 [ - FrameShower( - title: "The Chaos", - author: "张风捷特烈", - srcUrl: "", - info: - " 本样例介绍如何进行图片的绘制:通过加载图片并将图片资源绘制到指定的区域。在上层绘制一批 45° 倾角的栅格线,来练习线条的绘制。", - content: DrawPicture()), - FrameShower( - title: "The Coordinate", - author: "张风捷特烈", - srcUrl: "", - info: - " 本样例介绍如何使用线路径和文字绘制网格坐标系,并将绘制对象进行封装,方便重用。坐标系也会在绘制时提供参考,入门必备。", - content: DrawGridAxis()), - FrameShower( - title: "曲线拟合", - author: "张风捷特烈", - info: " 本样例介绍如何使用路径对函数曲线进行绘制,通过函数曲线上的少量点通过贝塞尔曲线进行拟合。", - content: DrawPathFun()), - FrameShower( - title: "随机对称图", - author: "张风捷特烈", - info: - " 本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。可以练习对数据的控制力。", - content: RandomPortrait()), FrameShower( - title: "简单时钟", - author: "张风捷特烈", - info: - " 本样例通过时钟的绘制,练习 Flutter 中旋转刻度类型的绘制技巧,并通过动画使表盘指针转动。", - content: ClockWidget()), - ]; - case GalleryType.anim: - return [ - FrameShower( - title: "手势弹簧", - author: "张风捷特烈", - info: " 本样例介绍如何绘制弹簧,通过触点竖直拖拽拉伸、压缩,放手时进行恢复动画,是一个很好的综合小案例。", - content: const SpringWidget()), - FrameShower( - title: "动画曲线散点图", - author: "张风捷特烈", - info: " 本样例通过直观的方式,来查看动画曲线 curve 的作用效果,让大家对动画有更深的理解。", - content: const CurveAnimShower()), - FrameShower( - title: "流光", - author: "张风捷特烈", - info: " 本样例介绍如何在绘制中使用着色器和过滤器,并通过动画进行数值变化达到旋转流光效果。", - content: const CircleHalo()), - FrameShower( - title: "Draw Curve", - author: "张风捷特烈", - info: " 本样例介绍如何使用路径绘制函数曲线,并使用路径测量进行动画", - content: DrawPath()), - FrameShower( - title: "Bezier3 演示 (双击清除)", - author: "张风捷特烈", - info: " 本样例介绍如何绘制三次贝塞尔曲线,通过触点判断某点是否激活,据此控制点的位置达到拖动控制效果。", - content: Bezier3Player()), - ]; - case GalleryType.particle: - return []; - case GalleryType.fun: - return [ - FrameShower( - title: "Random Portrait", - author: "张风捷特烈", - info: - " 本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。其中对点的随机生成和对称处理能让你练习对数据的控制力。", - content: RandomPortrait()), - FrameShower( - title: "井字棋", - author: "张风捷特烈", - info: - " 本例通过井字棋的绘制与逻辑校验,集合了手势、绘制、动画、校验等重要的技能,是一个非常好的联系案例。", - content: StampPaper()), - ]; - case GalleryType.art: - return [ - FrameShower( - title: "Tiled Line", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的tiled-lines,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: TiledLines(), - ), - FrameShower( - title: "Joy Division", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的joy-division,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: JoyDivision(), - ), - FrameShower( - title: "Cubic Disarray", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的cubic-disarray,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: CubicDisarray(), - ), - FrameShower( - title: "Triangular Mesh", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的triangular-mesh,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: TriangularMesh(), - ), - FrameShower( - title: "Un Deux Trois", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的un-deux-trois,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: UnDeuxTrois(), - ), - FrameShower( - title: "Circle Packing", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的circle-packing,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: CirclePacking(), - ), - FrameShower( - title: "Hypnotic Squares", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的hypnotic-squares,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: HypnoticSquares(), - ), - FrameShower( - title: "Piet Mondrian", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的piet-mondrian,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: PietMondrian(), - ) - ]; - default: - return []; - } - } -} diff --git a/lib/painter_system/gallery_unit.dart b/lib/painter_system/gallery_unit.dart deleted file mode 100644 index 3abc4eb2d..000000000 --- a/lib/painter_system/gallery_unit.dart +++ /dev/null @@ -1,210 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/res/str_unit.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/project/items/gallery/gallery_card_item.dart'; -import 'package:flutter_unit/painter_system/gallery_factory.dart'; - -import 'gallery_detail_page.dart'; - -/// create by 张风捷特烈 on 2020/11/28 -/// contact me by email 1981462002@qq.com -/// 说明: - -class GalleryUnit extends StatefulWidget { - @override - _GalleryUnitState createState() => _GalleryUnitState(); -} - -class _GalleryUnitState extends State { - final ValueNotifier factor = ValueNotifier(0); - - 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(() { - 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 => BlocProvider.of(context).state.color; - - Color get nextColor => BlocProvider.of(context).state.nextColor; - - BoxDecoration get boxDecoration => BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.only( - topLeft: Radius.circular(40), topRight: Radius.circular(40))); - - @override - Widget build(BuildContext context) { - return Scaffold( - body: ValueListenableBuilder( - child: Column( - //使用 child 属性优化 - children: [ - _buildTitle(context), - Expanded( - child: Container( - margin: const EdgeInsets.only(left: 8, right: 8), - child: _buildContent(), - decoration: boxDecoration, - )) - ], - ), - valueListenable: factor, - builder: (_, value, child) => Container( - color: Color.lerp( - color, - nextColor, - value, - ), - child: child, - ), - ), - ); - } - - Widget _buildTitle(BuildContext context) { - return Container( - alignment: Alignment(0, 0.3), - height: MediaQuery.of(context).size.height * 0.2, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - FlutterLogo( - size: 40, - ), - SizedBox( - width: 10, - ), - Text( - "绘制集录", - style: TextStyle(fontSize: 26, color: Colors.white), - ), - ], - ), - ); - } - - Widget _buildContent() { - final List widgets = - (json.decode(StrUnit.galleryInfo) as List).map((e) { - GalleryInfo info = GalleryInfo.fromJson(e); - List children = GalleryFactory.getGalleryByName(info.type); - - return FeedbackWidget( - a: 0.95, - onPressed: () { - Navigator.of(context).push(MaterialPageRoute( - builder: (ctx) => GalleryDetailPage( - galleryInfo: info, - children: children, - ))); - }, - child: GalleryCardItem( - galleryInfo: info, - count: children.length, - ), - ); - }).toList(); - - return Container( - padding: const EdgeInsets.only(bottom: 80, top: 40), - child: Column( - children: [ - Expanded( - child: PageView.builder( - controller: _ctrl, - itemBuilder: (_, index) { - return AnimatedBuilder( - child: Padding( - padding: const EdgeInsets.all(6.0), - child: widgets[ - _fixPosition(index, _firstOffset, widgets.length)], - ), - animation: _ctrl, - builder: (context, child) => - _buildAnimItemByIndex(context, child, index), - ); - }, - onPageChanged: (index) { - _position = index; - }, - ), - ), - _buildDiver(), - ], - )); - } - - Widget _buildAnimItemByIndex(BuildContext context, Widget child, int index) { - double value; - if (_ctrl.position.haveDimensions) { - value = _ctrl.page - index; - } else { - value = (_position - index).toDouble(); - } - value = (1 - ((value.abs()) * .5)).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(6.0), - child: child, - ), - ); - } - - Widget _buildDiver() => Container( - margin: EdgeInsets.only(bottom: 12, left: 48, right: 48, top: 10), - height: 2, - child: ValueListenableBuilder( - valueListenable: factor, - builder: (context, value, widget) { - return LinearProgressIndicator( - backgroundColor: Colors.black, - value: factor.value, - valueColor: AlwaysStoppedAnimation( - Color.lerp( - color, - nextColor, - factor.value, - ), - ), - ); - }, - ), - ); - - int _fixPosition(int realPos, int initPos, int length) { - final int offset = realPos - initPos; - int result = offset % length; - return result < 0 ? length + result : result; - } -} diff --git a/lib/painter_system/particle/out/clock_widget.dart b/lib/painter_system/particle/out/clock_widget.dart deleted file mode 100644 index 6cd50390f..000000000 --- a/lib/painter_system/particle/out/clock_widget.dart +++ /dev/null @@ -1,88 +0,0 @@ -import 'dart:math'; -import 'dart:ui'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; - -import 'clock_fx.dart'; - -/// create by 张风捷特烈 on 2021/2/7 -/// contact me by email 1981462002@qq.com -/// 说明: - -class ClockWidget extends StatefulWidget { - final double radius; - - const ClockWidget({Key key, this.radius = 100}) : super(key: key); - - @override - _ClockWidgetState createState() => _ClockWidgetState(); -} - -class _ClockWidgetState extends State - with SingleTickerProviderStateMixin { - Ticker _ticker; - ClockFx _fx; - - @override - void initState() { - super.initState(); - _ticker = createTicker(_tick)..start(); - _fx = ClockFx( - size: Size(widget.radius * 2, widget.radius * 2), - time: DateTime.now(), - ); - } - - @override - void dispose() { - _ticker.dispose(); - _fx.dispose(); - super.dispose(); - } - - void _tick(Duration duration) { - _fx.tick(duration); - if (_fx.time.second != DateTime.now().second) { - _fx.setTime(DateTime.now()); - } - } - - @override - Widget build(BuildContext context) { - return CustomPaint( - size: Size(widget.radius * 2, widget.radius * 2), - painter: ClockFxPainter(fx: _fx), - ); - } -} - -/// Alpha value for noise particles. -const double noiseAlpha = 160; - -class ClockFxPainter extends CustomPainter { - final ClockFx fx; - - ClockFxPainter({@required this.fx}) : super(repaint: fx); - - @override - void paint(Canvas canvas, Size size) { - fx.particles.forEach((p) { - double a; - a = max(0.0, (p.distFrac - .13) / p.distFrac) * 255; - a = min(a, min(noiseAlpha, p.lifeLeft * 3 * 255)); - int alpha = a.floor(); - - Paint circlePaint = Paint() - ..style = PaintingStyle.fill - ..color = p.color.withAlpha(alpha); - - canvas.drawCircle(Offset(p.x, p.y), p.size, circlePaint); - }); - } - - @override - bool shouldRepaint(covariant ClockFxPainter oldDelegate) => - oldDelegate.fx != fx; -} diff --git a/lib/painter_system/particle/out/particle.dart b/lib/painter_system/particle/out/particle.dart deleted file mode 100644 index e106040a8..000000000 --- a/lib/painter_system/particle/out/particle.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; - -class Particle { - - double x; // x 坐标 - double y; // y 坐标 - double vx; // x 速度 - double vy; // y 速度 - double a; // 发射弧度 - double dist; // 距离画布中心的长度 - double distFrac;// 距离画布中心的百分比 - double size;// 粒子大小 - double life; // 粒子寿命 - double lifeLeft; // 粒子剩余寿命 - bool isFilled; // 是否填充 - Color color; // 颜色 - int distribution; // 分配情况 - - - Particle({ - this.x = 0, - this.y = 0, - this.a = 0, - this.vx = 0, - this.vy = 0, - this.dist = 0, - this.distFrac = 0, - this.size = 0, - this.life = 0, - this.lifeLeft = 0, - this.isFilled = false, - this.color = Colors.blueAccent, - this.distribution = 0, - }); -} - - diff --git a/lib/painter_system/picture_frame.dart b/lib/painter_system/picture_frame.dart deleted file mode 100644 index 9d2fc78ab..000000000 --- a/lib/painter_system/picture_frame.dart +++ /dev/null @@ -1,107 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; - -class PictureFrame extends StatelessWidget { - final Widget child; - final double width; - final double height; - final Color color; - final Alignment alignment; - final EdgeInsetsGeometry marge; - - const PictureFrame( - {this.child, - this.width, - this.height, - this.alignment, - this.color = Colors.transparent, - this.marge}); - - @override - Widget build(BuildContext context) { - double size = MediaQuery.of(context).size.shortestSide; - return Container( - alignment: alignment, - width: width ?? size, - height: height ?? size, - padding: marge ?? EdgeInsets.all(20), - child: CustomPaint( - painter: FramePainter(), - child: Container( - margin: EdgeInsets.all(14), - decoration: BoxDecoration( - color: color, - border: Border.all( - color: Colors.black12, - width: 1, - ), - ), - child: child, - ), - ), - ); - } -} - -class FramePainter extends CustomPainter { - @override - void paint(Canvas canvas, Size size) { - Path path = Path() - ..relativeLineTo(0, size.height) - ..relativeLineTo(size.width, 0) - ..relativeLineTo(0, -size.height) - ..close(); - Paint myPaint = Paint() - ..style = PaintingStyle.stroke - ..strokeWidth = 10; - canvas.drawPath(path, myPaint); - Path shadowPath = Path() - ..addRect(Rect.fromPoints(Offset.zero, Offset(size.width, size.height))); - // canvas.drawShadow(shadowPath, Colors.grey, 1, false); - - canvas.save(); - canvas.translate(size.width / 2, size.height / 2); - canvas.scale(-1, 1); - canvas.translate(-size.width / 2, -size.height / 2); - drawCorner(myPaint, canvas); - canvas.restore(); - - canvas.save(); - canvas.translate(size.width / 2, size.height / 2); - canvas.scale(1, -1); - canvas.translate(-size.width / 2, -size.height / 2); - drawCorner(myPaint, canvas); - canvas.restore(); - - canvas.save(); - canvas.translate(size.width, size.height); - canvas.scale(-1, -1); - drawCorner(myPaint, canvas); - canvas.restore(); - drawCorner(myPaint, canvas); - canvas.drawShadow(shadowPath, Colors.grey, 1, false); - } - - void drawCorner(Paint myPaint, Canvas canvas) { - myPaint - ..style = PaintingStyle.fill - ..color = Colors.white - ..strokeCap = StrokeCap.butt; - canvas.drawPoints( - PointMode.polygon, - [ - Offset(0, 0), - Offset(18, 0), - Offset(0, 18), - Offset(0, 0), - ], - myPaint); - canvas.drawCircle(Offset(8, 8), 3, myPaint..color = Colors.black); - } - - @override - bool shouldRepaint(covariant CustomPainter oldDelegate) { - return false; - } -} diff --git a/lib/repositories/app_start.dart b/lib/repositories/app_start.dart deleted file mode 100644 index 6194feaf6..000000000 --- a/lib/repositories/app_start.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; - -import 'package:flutter/services.dart'; -import 'package:flutter_unit/app/res/cons.dart'; -import 'package:flutter_unit/app/res/sp.dart'; -import 'package:flutter_unit/blocs/global/global_state.dart'; -import 'package:flutter_unit/repositories/local_db.dart'; -import 'package:path/path.dart' as path; -import 'package:shared_preferences/shared_preferences.dart'; -import 'package:sqflite/sqflite.dart'; - -import 'local_storage.dart'; - -/// create by 张风捷特烈 on 2020-03-04 -/// contact me by email 1981462002@qq.com -/// 说明: 本地存储访问对象,用于读取配置数据,及初始化Database和SharedPreferences - -class AppStart { - // 初始化 App 固化的配置数据 - Future initApp() async { - SharedPreferences prefs = await LocalStorage.sp; - //数据库不存在,执行拷贝 - String databasesPath = await getDatabasesPath(); - String dbPath = path.join(databasesPath, "flutter.db"); - - bool shouldCopy = await _checkShouldCopy(dbPath); - - if (shouldCopy) { - await _doCopyAssetsDb(dbPath); - } else { - print("=====flutter.db 已存在===="); - } - - await LocalDb.instance.initDb(); - - bool showBg = prefs.getBool(SP.showBackground) ?? true; - int themeIndex = prefs.getInt(SP.themeColorIndex) ?? 4; - int fontIndex = prefs.getInt(SP.fontFamily) ?? 1; - int codeIndex = prefs.getInt(SP.codeStyleIndex) ?? 0; - int itemStyleIndex = prefs.getInt(SP.itemStyleIndex) ?? 0; - - return GlobalState( - showBackGround: showBg, - themeColor: Cons.themeColorSupport.keys.toList()[themeIndex], - fontFamily: Cons.fontFamilySupport[fontIndex], - itemStyleIndex: itemStyleIndex, - codeStyleIndex: codeIndex); - } - - Future _checkShouldCopy(String dbPath) async { - bool shouldCopy = false; - String versionStr = await rootBundle.loadString('assets/version.json'); - int dbVersion = await json.decode(versionStr)['dbVersion']; - int versionInSP = - await LocalStorage.getInt(LocalStorage.dbVersionKey) ?? -1; - - // 版本升级,执行拷贝 - if (dbVersion > versionInSP) { - shouldCopy = true; - await LocalStorage.saveInt(LocalStorage.dbVersionKey,dbVersion); - } - - //非 release模式,执行拷贝 - const isPro = bool.fromEnvironment('dart.vm.product'); - if (!isPro) { - shouldCopy = true; - } - - //数据库不存在,执行拷贝 - if (!File(dbPath).existsSync()) { - shouldCopy = true; - } - - return shouldCopy; - } - - 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 ======拷贝完成===="); - } -} diff --git a/lib/repositories/bean/category_po.dart b/lib/repositories/bean/category_po.dart deleted file mode 100644 index 1f31230ab..000000000 --- a/lib/repositories/bean/category_po.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:equatable/equatable.dart'; - -/// 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 '' -// );"""; //建表语句 - -class CategoryPo extends Equatable { - final int id; - final String name; - final String color; - final String info; - final DateTime created; - final DateTime updated; - final String image; - final int count; - final int priority; - - const CategoryPo( - {this.id, - this.name, - this.color = '#FFF2F2F2', - this.created, - this.updated, - this.count = 0, - this.priority = 0, - this.info = '这里什么都没有...', - this.image = ''}); - - factory CategoryPo.fromJson(Map map) { - return CategoryPo( - id: map['id'], - name: map['name'], - color: map["color"], - created: DateTime.parse(map["created"]), - image: map["image"], - priority: map["priority"], - count: map["count"], - updated: DateTime.parse(map["updated"]), - info: map["info"]); - } - - factory CategoryPo.fromNetJson(Map map) { - return CategoryPo( - id: map['id'], - name: map['name'], - color: map["color"], - created: DateTime.fromMillisecondsSinceEpoch(map["created"]), - image: map["image"], - priority: map["priority"], - count: map["count"], - updated: DateTime.fromMillisecondsSinceEpoch(map["updated"]), - info: map["info"]); - } - - Map toJson() => { - "id": this.id, - "name": this.name, - "info": this.info, - "created": this.created.millisecondsSinceEpoch, - "updated": this.updated.millisecondsSinceEpoch, - "image": this.image, - "count": this.count, - "color": this.color, - "priority": this.priority, - }; - - @override - String toString() { - return 'CategoryPo{id: $id, name: $name, color: $color, info: $info, created: $created, updated: $updated, image: $image, count: $count, priority: $priority}'; - } - - @override - List get props => - [id, name, color, created, image, info, updated, priority,count]; -} diff --git a/lib/repositories/bean/widget_po.dart b/lib/repositories/bean/widget_po.dart deleted file mode 100644 index fcb895337..000000000 --- a/lib/repositories/bean/widget_po.dart +++ /dev/null @@ -1,61 +0,0 @@ - -/// create by 张风捷特烈 on 2020-03-04 -/// contact me by email 1981462002@qq.com -/// 说明: 组件信息-数据库-数据模型 -/// -import 'package:equatable/equatable.dart'; - -class WidgetPo extends Equatable { - 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( - {this.id, - this.name, - this.nameCN, - this.deprecated, - this.family, - this.lever, - this.linkWidget, - 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": this.id, - "name": this.name, - "nameCN": this.nameCN, - "family": this.family, - "deprecated": this.deprecated, - "lever": this.lever, - "linkWidget": this.linkWidget, - "info": this.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/lib/repositories/dao/category_dao.dart b/lib/repositories/dao/category_dao.dart deleted file mode 100644 index 941036be4..000000000 --- a/lib/repositories/dao/category_dao.dart +++ /dev/null @@ -1,202 +0,0 @@ - -import 'package:sqflite/sqflite.dart'; - -import '../app_start.dart'; -import '../bean/category_po.dart'; - - -//""" -// 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 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.length > 0) { - 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.length > 0) { - 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"]).toList(); - } -} diff --git a/lib/repositories/dao/like_dao.dart b/lib/repositories/dao/like_dao.dart deleted file mode 100644 index 605ed4848..000000000 --- a/lib/repositories/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']).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) { - // 如果 like ,直接取消,不执行 like 操作 - 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) { - // 如果未 like ,直接取消,不执行 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.length > 0) { - var result = data[0]; - return result['count'] as int > 0; - } - return false; - } -} diff --git a/lib/repositories/dao/node_dao.dart b/lib/repositories/dao/node_dao.dart deleted file mode 100644 index e295f715f..000000000 --- a/lib/repositories/dao/node_dao.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:flutter_unit/repositories/bean/node_po.dart'; -import 'package:sqflite/sqflite.dart'; - -import '../app_start.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/lib/repositories/dao/widget_dao.dart b/lib/repositories/dao/widget_dao.dart deleted file mode 100644 index 11b776787..000000000 --- a/lib/repositories/dao/widget_dao.dart +++ /dev/null @@ -1,67 +0,0 @@ - -import 'package:flutter_unit/model/enums.dart'; -import 'package:sqflite/sqflite.dart'; - -import '../app_start.dart'; -import '../bean/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 await 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 await db.rawQuery("SELECT * FROM widget"); - } - - Future>> queryByFamily(WidgetFamily family) async { - return await db.rawQuery( - "SELECT * " - "FROM widget WHERE family = ? ORDER BY lever DESC", - [family.index]); - } - - Future>> queryByIds(List ids) async { - if (ids.length == 0) { - return []; - } - String sql = "SELECT * " - "FROM widget WHERE id in (${'?,' * (ids.length - 1)}?) "; - - return await db.rawQuery(sql, [...ids]); - } - - Future>> search(SearchArgs arguments) async { - return await db.rawQuery( - "SELECT * " - "FROM widget WHERE name like ? AND lever IN(?,?,?,?,?) ORDER BY lever DESC", - ["%${arguments.name}%", ...arguments.stars]); - } -} - -class SearchArgs { - final String name; - final List stars; - - const SearchArgs({this.name = '', this.stars = const [-1, -1, -1, -1, -1]}); -} diff --git a/lib/repositories/local_db.dart b/lib/repositories/local_db.dart deleted file mode 100644 index 9434647c7..000000000 --- a/lib/repositories/local_db.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:flutter_unit/repositories/dao/like_dao.dart'; -import 'package:path/path.dart' as path; -import 'package:sqflite/sqflite.dart'; - -import 'dao/category_dao.dart'; -import 'dao/node_dao.dart'; -import 'dao/widget_dao.dart'; - -class LocalDb { - Database _database; - - LocalDb._(); - - static LocalDb instance = LocalDb._(); - - WidgetDao _widgetDao; - CategoryDao _categoryDao; - NodeDao _nodeDao; - LikeDao _likeDao; - - WidgetDao get widgetDao => _widgetDao; - - CategoryDao get categoryDao => _categoryDao; - - NodeDao get nodeDao => _nodeDao; - - LikeDao get likeDao => _likeDao; - - Database get db => _database; - - Future initDb({String name = "flutter.db"}) async { - if (_database != null) return; - String databasesPath = await getDatabasesPath(); - String dbPath = path.join(databasesPath, name); - - _database = await openDatabase(dbPath); - - _widgetDao = WidgetDao(_database); - _categoryDao = CategoryDao(_database); - _nodeDao = NodeDao(_database); - _likeDao = LikeDao(_database); - - print('初始化数据库....'); - } - - Future closeDb() async { - await _database.close(); - _database = null; - } -} diff --git a/lib/repositories/local_storage.dart b/lib/repositories/local_storage.dart deleted file mode 100644 index 3eb9afe78..000000000 --- a/lib/repositories/local_storage.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:shared_preferences/shared_preferences.dart'; - -/// create by 张风捷特烈 on 2020/6/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -class LocalStorage { - - static String tokenKey= "token_key"; - static String userKey= "user_key"; - static String dbVersionKey= "db_version_key"; - - static SharedPreferences _sp; - - // 如果_sp已存在,直接返回,为null时创建 - static Future get sp async { - if (_sp == null) { - _sp = await SharedPreferences.getInstance(); - } - return _sp; - } - - static Future save(String key, String value) async { - return (await sp).setString(key, value); - } - - static dynamic get(String key) async { - return (await sp).get(key); - } - - static Future saveInt(String key, int value) async { - return (await sp).setInt(key, value); - } - - static Future getInt(String key) async { - return (await sp).getInt(key); - } - - static Future remove(String key) async { - return (await sp).remove(key); - } -} \ No newline at end of file diff --git a/lib/repositories/rep/impl/catagory_db_repository.dart b/lib/repositories/rep/impl/catagory_db_repository.dart deleted file mode 100644 index 784156a48..000000000 --- a/lib/repositories/rep/impl/catagory_db_repository.dart +++ /dev/null @@ -1,120 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:flutter_unit/model/category_model.dart'; -import 'package:flutter_unit/model/widget_model.dart'; -import 'package:flutter_unit/repositories/bean/category_po.dart'; -import 'package:flutter_unit/repositories/bean/widget_po.dart'; -import 'package:flutter_unit/repositories/dao/category_dao.dart'; -import 'package:flutter_unit/repositories/local_db.dart'; -import 'package:flutter_unit/repositories/rep/category_repository.dart'; -import 'package:sqflite/sqflite.dart'; - - -/// create by 张风捷特烈 on 2020-04-21 -/// contact me by email 1981462002@qq.com -/// 说明: - -class CategoryDbRepository implements CategoryRepository { - - CategoryDao get _categoryDao => LocalDb.instance.categoryDao; - - Database get db => LocalDb.instance.db; - - @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.length == 0) { - 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) { - 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 LocalDb.instance.likeDao.like(likeWidgets[i]); - } - return true; - } catch (e) { - print(e); - return false; - } - } -} diff --git a/lib/repositories/rep/impl/widget_db_repository.dart b/lib/repositories/rep/impl/widget_db_repository.dart deleted file mode 100644 index fa5aa5ef2..000000000 --- a/lib/repositories/rep/impl/widget_db_repository.dart +++ /dev/null @@ -1,79 +0,0 @@ - -import 'dart:convert'; -import 'dart:io'; - - -import 'package:flutter_unit/repositories/app_start.dart'; -import 'package:flutter_unit/repositories/bean/widget_po.dart'; -import 'package:flutter_unit/repositories/dao/like_dao.dart'; -import 'package:flutter_unit/repositories/local_db.dart'; -import 'package:flutter_unit/repositories/dao/node_dao.dart'; -import 'package:flutter_unit/repositories/dao/widget_dao.dart'; -import 'package:flutter_unit/model/enums.dart'; - -import 'package:flutter_unit/model/node_model.dart'; -import 'package:flutter_unit/model/widget_model.dart'; -import 'package:flutter_unit/repositories/rep/widget_repository.dart'; -import 'package:path_provider/path_provider.dart'; - - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明 : Widget数据仓库 - -class WidgetDbRepository implements WidgetRepository { - - WidgetDao get _widgetDao => LocalDb.instance.widgetDao; - NodeDao get _nodeDao => LocalDb.instance.nodeDao; - LikeDao get _likeDao => LocalDb.instance.likeDao; - - @override - Future> loadWidgets(WidgetFamily family) async { - List> data = await _widgetDao.queryByFamily(family); - List widgets = data.map((e) => WidgetPo.fromJson(e)).toList(); - return widgets.map(WidgetModel.fromPo).toList(); - } - - @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(SearchArgs 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> loadNode(WidgetModel widgetModel) async { - List> data = await _nodeDao.queryById(widgetModel.id); - List nodes = data.map((e) => NodeModel.fromJson(e)).toList(); - return nodes; - } - - @override - Future> loadWidget(List id) async { - List> data = await _widgetDao.queryByIds(id); - List widgets = data.map((e) => WidgetPo.fromJson(e)).toList(); - if (widgets.length > 0) return widgets.map(WidgetModel.fromPo).toList(); - return null; - } - - @override - Future toggleLike( - int id, - ) { - return _likeDao.toggleCollect(id); - } - - - @override - Future collected(int id) async{ - return await _likeDao.like(id); - } -} diff --git a/lib/repositories/rep/widget_repository.dart b/lib/repositories/rep/widget_repository.dart deleted file mode 100644 index 507195cbb..000000000 --- a/lib/repositories/rep/widget_repository.dart +++ /dev/null @@ -1,23 +0,0 @@ -import 'package:flutter_unit/model/enums.dart'; -import 'package:flutter_unit/model/node_model.dart'; -import 'package:flutter_unit/model/widget_model.dart'; -import 'package:flutter_unit/repositories/dao/widget_dao.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); - - Future> searchWidgets(SearchArgs args); - - Future> loadNode(WidgetModel widgetModel); - - Future toggleLike(int id); - - Future> loadLikeWidgets(); - - Future collected(int id); -} 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/src/starter/view/splash/flutter_unit_text.dart b/lib/src/starter/view/splash/flutter_unit_text.dart new file mode 100644 index 000000000..427cf92e6 --- /dev/null +++ b/lib/src/starter/view/splash/flutter_unit_text.dart @@ -0,0 +1,122 @@ +import 'dart:math'; + +import 'package:flutter/foundation.dart'; +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 + State createState() => _FlutterUnitTextState(); +} + +class _FlutterUnitTextState extends State + with SingleTickerProviderStateMixin { + + late AnimationController _ctrl; + + final TextPainter _textPainter = + TextPainter(textDirection: TextDirection.ltr); + + late Animation animation; + + @override + void initState() { + super.initState(); + TextSpan text = TextSpan( + text: widget.text, style: TextStyle(fontSize: widget.fontSize, color: Colors.blue)); + _textPainter.text = text; + _textPainter.layout(); // 进行布局 + + _ctrl = AnimationController( + vsync: this, duration: const Duration(milliseconds: 800)); + animation = CurvedAnimation(parent: _ctrl, curve: const Interpolator()); + _ctrl.forward(); + } + + @override + void dispose() { + _ctrl.dispose(); + super.dispose(); + } + + @override + void didUpdateWidget(FlutterUnitText oldWidget) { + super.didUpdateWidget(oldWidget); + _ctrl.forward(); + } + + String msg = ''; + + @override + Widget build(BuildContext context) { + return CustomPaint( + size: _textPainter.size, + painter: SpringPainter( + fontSize: widget.fontSize, + textPainter: _textPainter, + color: widget.color, + skew: animation, + )); + } +} + +class Interpolator extends Curve { + const Interpolator(); + + @override + double transformInternal(double t) { + t -= 1.0; + return t * t * t * t * t + 1.0; + } +} + +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.fontSize}) + : super(repaint: skew) { + _text = textPainter.text?.toPlainText() ?? ''; + } + + @override + void paint(Canvas canvas, Size size) { + canvas.translate(size.width / 2, size.height / 2); + TextSpan text = + TextSpan(text: _text, style: TextStyle(fontSize: fontSize, color: color)); + textPainter.text = text; + textPainter.layout(); // 进行布局 + Size textSize = textPainter.size; // 尺寸必须在布局后获取 + + canvas.save(); + canvas.translate(-textSize.width / 2, -textSize.height / 2); + textPainter.paint(canvas, Offset.zero); + TextSpan textShadow = TextSpan( + text: _text, + style: TextStyle(fontSize: fontSize, color: color.withAlpha(88))); + textPainter.text = textShadow; + textPainter.layout(); // 进行布局 + Matrix4 matrix4 = Matrix4.skewX((6 / 180 * pi) * skew.value); + canvas.transform(matrix4.storage); + textPainter.paint(canvas, Offset.zero); + canvas.restore(); + } + + @override + bool shouldRepaint(covariant SpringPainter oldDelegate) => + oldDelegate.textPainter != textPainter || oldDelegate.skew != skew; +} diff --git a/lib/user_system/api/system_api.dart b/lib/user_system/api/system_api.dart deleted file mode 100644 index 69363e9a2..000000000 --- a/lib/user_system/api/system_api.dart +++ /dev/null @@ -1,21 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter_unit/app/res/path_unit.dart'; -import 'package:flutter_unit/app/utils/http_utils/http_util.dart'; -import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; - -/// create by 张风捷特烈 on 2021/1/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -class SystemApi { - static Future> sendEmail({@required String email}) async { - var result = - await HttpUtil.getInstance().client.post(PathUnit.sendEmail + "$email"); - - if (result.data != null) { - return ResultBean.fromData(result.data); - } - - return ResultBean.error('请求错误'); - } -} diff --git a/lib/user_system/api/user_api.dart b/lib/user_system/api/user_api.dart deleted file mode 100644 index 03dee8874..000000000 --- a/lib/user_system/api/user_api.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:flutter_unit/app/res/path_unit.dart'; -import 'package:flutter_unit/app/utils/http_utils/http_util.dart'; -import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; -import 'package:flutter_unit/user_system/model/user.dart'; - -/// create by 张风捷特烈 on 2021/1/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -class UserApi { - static Future> register({String email, String code}) async { - String errorMsg = ""; - - var result = await HttpUtil.getInstance().client.post(PathUnit.register, - data: {"email": email, "activeCode": code}).catchError((err) { - errorMsg = err.toString(); - }); - - if (result.data != null) { - return ResultBean.fromData(result.data); - } - - return ResultBean.error('请求错误: $errorMsg'); - } - - static Future> login( - {String username, String password}) async { - String errorMsg = ""; - - var result = await HttpUtil.getInstance().client.post(PathUnit.login, - data: {"username": username, "password": password}).catchError((err) { - errorMsg = err.toString(); - }); - - if (result.data != null) { - if (result.data['status']) { - return ResultBean( - msg: result.data['msg'], - data: User.fromJson(result.data['data']), - status: result.data['status'], - ); - } else { - return ResultBean( - msg: result.data['msg'], - data: null, - status: false, - ); - } - } - - return ResultBean.error('请求错误: $errorMsg'); - } -} diff --git a/lib/user_system/bloc/authentic/bloc.dart b/lib/user_system/bloc/authentic/bloc.dart deleted file mode 100644 index 3c9328b17..000000000 --- a/lib/user_system/bloc/authentic/bloc.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:bloc/bloc.dart'; -import 'package:flutter_unit/app/utils/http_utils/http_util.dart'; -import 'package:flutter_unit/repositories/local_storage.dart'; -import 'package:flutter_unit/user_system/model/user.dart'; -import 'package:jwt_decoder/jwt_decoder.dart'; - -import 'event.dart'; -import 'state.dart'; - -class AuthenticBloc extends Bloc { - AuthenticBloc() : super(AuthInitial()); - - @override - Stream mapEventToState( - AuthEvent event, - ) async* { - if (event is AppStarted) { - String token = await LocalStorage.get(LocalStorage.tokenKey); - String userJson = await LocalStorage.get(LocalStorage.userKey); - if (token != null && userJson != null) { - bool disable = JwtDecoder.isExpired(token); - if (!disable) { - HttpUtil.getInstance().setToken(token); - yield AuthSuccess(User.fromJson(json.decode(userJson))); - }else{ - // 说明 token 过期 - await _removeToken(); - await _removeUser(); - } - } - } - - if (event is LoginOver) { - HttpUtil.getInstance().setToken(event.token); - await _persistToken(event.token); - await _persistUser(event.user); - yield AuthSuccess(event.user); - } - - if (event is LoggedOut) {} - } - - // 持久化 token - Future _persistToken(String token) async { - await LocalStorage.save(LocalStorage.tokenKey, token); - } - - - // 持久化 token - Future _removeToken() async { - await LocalStorage.remove(LocalStorage.tokenKey); - } - - // 持久化 token - Future _removeUser() async { - await LocalStorage.remove(LocalStorage.userKey); - } - - // 持久化 user - Future _persistUser(User user) async { - await LocalStorage.save(LocalStorage.userKey, json.encode(user)); - } - - -} diff --git a/lib/user_system/bloc/authentic/event.dart b/lib/user_system/bloc/authentic/event.dart deleted file mode 100644 index 974a60a0d..000000000 --- a/lib/user_system/bloc/authentic/event.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_unit/user_system/model/user.dart'; - - - -///********************************验证行为******************************** - -abstract class AuthEvent extends Equatable { - const AuthEvent(); - - @override - List get props => []; -} - -class AppStarted extends AuthEvent { - const AppStarted(); -} - -class LoginOver extends AuthEvent { - final String token; - final User user; - - const LoginOver({@required this.token,this.user}); - - @override - List get props => [token]; - - @override - String toString() => 'LoginOver { token: $token }'; -} - -class LoggedOut extends AuthEvent { - - final bool clearUser; - final bool tokenDisable; - - LoggedOut({this.clearUser=true,this.tokenDisable=false}); -} - -class TokenDisabled extends AuthEvent { - - TokenDisabled(); -} \ No newline at end of file diff --git a/lib/user_system/bloc/authentic/state.dart b/lib/user_system/bloc/authentic/state.dart deleted file mode 100644 index 24834cd76..000000000 --- a/lib/user_system/bloc/authentic/state.dart +++ /dev/null @@ -1,39 +0,0 @@ - -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/user_system/model/user.dart'; - - - -///********************************校验状态******************************** -// -abstract class AuthenticState extends Equatable { - @override - List get props => []; -} - - -class AuthInitial extends AuthenticState {} - - - -class AuthFailure extends AuthenticState { - -} - - -class LogOuted extends AuthenticState {} - -class AuthSuccess extends AuthenticState { - final User user; - - AuthSuccess(this.user); - - @override - String toString() { - return 'AuthSuccess{loginResult: $user}'; - } -} - -class AuthLoading extends AuthenticState { - -} \ No newline at end of file diff --git a/lib/user_system/bloc/login/bloc.dart b/lib/user_system/bloc/login/bloc.dart deleted file mode 100644 index 9062ba52d..000000000 --- a/lib/user_system/bloc/login/bloc.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/user_system/api/user_api.dart'; -import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; -import 'package:flutter_unit/user_system/bloc/authentic/bloc.dart'; -import 'package:flutter_unit/user_system/bloc/authentic/event.dart'; -import 'package:flutter_unit/user_system/model/user.dart'; - -import 'event.dart'; -import 'state.dart'; - -/// create by 张风捷特烈 on 2021/1/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -class LoginBloc extends Bloc { - final AuthenticBloc authenticBloc; - - LoginBloc({ @required this.authenticBloc}) : super(LoginNone()); - - @override - Stream mapEventToState(LoginEvent event) async* { - if (event is DoLogin) { - yield LoginLoading(); - await Future.delayed(Duration(milliseconds: 500)); - ResultBean result = await UserApi.login(username: event.username, password: event.password); - - if (result.status) { - // 注册成功 - authenticBloc.add(LoginOver(token: result.msg,user: result.data)); - yield LoginSuccess(result.data); - } else { - yield LoginError('用户名和密码不匹配'); - } - } - } -} diff --git a/lib/user_system/bloc/login/event.dart b/lib/user_system/bloc/login/event.dart deleted file mode 100644 index a213165d3..000000000 --- a/lib/user_system/bloc/login/event.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:equatable/equatable.dart'; - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明: - -abstract class LoginEvent extends Equatable { - const LoginEvent(); - - @override - List get props => []; -} - -// 发送 邮箱验证 -class DoLogin extends LoginEvent { - final String username; - final String password; - - DoLogin({this.username, this.password}); -} diff --git a/lib/user_system/bloc/login/state.dart b/lib/user_system/bloc/login/state.dart deleted file mode 100644 index f7aef395a..000000000 --- a/lib/user_system/bloc/login/state.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/user_system/model/user.dart'; - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明: 主页 Widget 列表 状态类 - -abstract class LoginState extends Equatable { - const LoginState(); - - @override - List get props => []; -} - -class LoginLoading extends LoginState { - @override - List get props => []; -} - -class LoginError extends LoginState { - final String message; - - const LoginError(this.message); - - @override - List get props => [message]; - - @override - String toString() { - return 'LoginError{message: $message}'; - } -} - -class LoginSuccess extends LoginState { - final User user; - - const LoginSuccess(this.user); - - @override - List get props => [user]; -} - -class LoginNone extends LoginState { - - - const LoginNone(); - - @override - List get props => []; -} diff --git a/lib/user_system/bloc/register/bloc.dart b/lib/user_system/bloc/register/bloc.dart deleted file mode 100644 index 67dcf7d14..000000000 --- a/lib/user_system/bloc/register/bloc.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/user_system/api/user_api.dart'; -import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; - -import 'event.dart'; -import 'state.dart'; - -/// create by 张风捷特烈 on 2021/1/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -class RegisterBloc extends Bloc { - RegisterBloc() : super(RegisterNone()); - - @override - Stream mapEventToState(RegisterEvent event) async* { - if (event is DoRegister) { - yield RegisterLoading(); - ResultBean result = - await UserApi.register(email: event.email, code: event.code); - - if(result.data == null){ - yield RegisterError('注册失败'); - }else{ - if (result.data) { - // 注册成功 - yield RegisterSuccess(event.email); - }else{ - yield RegisterError(result.msg); - } - } - } - } -} diff --git a/lib/user_system/bloc/register/event.dart b/lib/user_system/bloc/register/event.dart deleted file mode 100644 index fdc1c17f3..000000000 --- a/lib/user_system/bloc/register/event.dart +++ /dev/null @@ -1,20 +0,0 @@ -import 'package:equatable/equatable.dart'; - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明: - -abstract class RegisterEvent extends Equatable { - const RegisterEvent(); - - @override - List get props => []; -} - -// 发送 邮箱验证 -class DoRegister extends RegisterEvent { - final String email; - final String code; - - DoRegister(this.email, this.code); -} diff --git a/lib/user_system/bloc/register/state.dart b/lib/user_system/bloc/register/state.dart deleted file mode 100644 index bd1342c49..000000000 --- a/lib/user_system/bloc/register/state.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'package:equatable/equatable.dart'; - -/// create by 张风捷特烈 on 2020-03-03 -/// contact me by email 1981462002@qq.com -/// 说明: 主页 Widget 列表 状态类 - -abstract class RegisterState extends Equatable { - const RegisterState(); - - @override - List get props => []; -} - -class RegisterLoading extends RegisterState { - @override - List get props => []; -} - -class RegisterNone extends RegisterState { - @override - List get props => []; -} - - -class RegisterError extends RegisterState { - final String message; - - const RegisterError(this.message); - - @override - List get props => [message]; - - @override - String toString() { - return 'RegisterError{message: $message}'; - } -} - -class RegisterSuccess extends RegisterState { - final String username; - - const RegisterSuccess(this.username); - - @override - List get props => [username]; -} diff --git a/lib/user_system/component/authentic_widget.dart b/lib/user_system/component/authentic_widget.dart deleted file mode 100644 index 69b4137cd..000000000 --- a/lib/user_system/component/authentic_widget.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/user_system/bloc/authentic/bloc.dart'; -import 'package:flutter_unit/user_system/bloc/authentic/state.dart'; - -/// create by 张风捷特烈 on 2021/2/24 -/// contact me by email 1981462002@qq.com -/// 说明: - -class AuthenticWidget extends StatelessWidget { - final Widget authentic; - final Widget noAuthentic; - - const AuthenticWidget({this.authentic, this.noAuthentic}); - - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (_, state) { - return state is AuthSuccess ? authentic : noAuthentic; - }); - } - - factory AuthenticWidget.just(Widget authentic){ - return AuthenticWidget( - authentic: authentic, - noAuthentic: const SizedBox(), - ); - } -} diff --git a/lib/user_system/model/user.dart b/lib/user_system/model/user.dart deleted file mode 100644 index 2b670f198..000000000 --- a/lib/user_system/model/user.dart +++ /dev/null @@ -1,47 +0,0 @@ -import 'package:equatable/equatable.dart'; - -/// create by 张风捷特烈 on 2021/1/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -// "userId": 1302422300380954625, -// "username": "toly", -// "email": "1981462001@qq.com", -// "activeCode": 1, -// "roles": "admin", -// "createAt": "2020-09-06T01:44:09.000+00:00", -// "updateAt": "2020-12-12T06:35:22.000+00:00" - -class User extends Equatable{ - final String username; - final String userAvatar; - final String email; - final String roles; - final int userId; - - const User({this.username, this.email, this.roles, this.userId,this.userAvatar}); - - factory User.fromJson(Map map) { - return User( - username: map['username'], - email: map['email'], - roles: map['roles'], - userId: map['userId'], - userAvatar: map['userAvatar'], - ); - } - - bool get isHonour => roles.contains('honour'); - - @override - List get props => [username,email,roles,userId,userAvatar]; - - Map toJson() => { - "username": this.username, - "email": this.email, - "roles": this.roles, - "userId": this.userId, - "userAvatar": this.userAvatar, - }; - -} diff --git a/lib/user_system/pages/login/login_form.dart b/lib/user_system/pages/login/login_form.dart deleted file mode 100644 index 8d5847b47..000000000 --- a/lib/user_system/pages/login/login_form.dart +++ /dev/null @@ -1,267 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/app/utils/Toast.dart'; -import 'package:flutter_unit/user_system/bloc/login/bloc.dart'; -import 'package:flutter_unit/user_system/bloc/login/event.dart'; -import 'package:flutter_unit/user_system/bloc/login/state.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; - -class LoginFrom extends StatefulWidget { - @override - _LoginFromState createState() => _LoginFromState(); -} - -class _LoginFromState extends State { - final _usernameController = TextEditingController(text: '张风捷特烈'); - final _passwordController = TextEditingController(text: '111111'); - - bool _showPwd = false; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - "FlutterUnit 登录", - style: TextStyle(fontSize: 25), - ), - SizedBox( - height: 5, - ), - Text( - "更多精彩,更多体验 ~", - style: TextStyle(color: Colors.grey), - ), - SizedBox( - height: 20, - ), - buildUsernameInput(), - Stack( - alignment: Alignment(.8, 0), - children: [ - buildPasswordInput(), - FeedbackWidget( - onPressed: () => setState(() => _showPwd = !_showPwd), - child: Icon(_showPwd ? TolyIcon.icon_show : TolyIcon.icon_hide)) - ], - ), - Row( - children: [ - Checkbox(value: true, onChanged: (e) => {}), - Text( - "自动登录", - style: TextStyle(color: Color(0xff444444), fontSize: 14), - ), - Spacer(), - FeedbackWidget( - onEnd: () { - Navigator.of(context).pushReplacementNamed(UnitRouter.register); - }, - child: Text( - "用户注册", - style: TextStyle( - color: Colors.blue, - fontSize: 14, - decoration: TextDecoration.underline), - ), - ) - ], - ), - BlocConsumer( - listener: _listenLoginState, - builder: _buildBtnByState, - ), - buildOtherLogin(), - const Spacer(flex: 4), - ], - ); - } - - void _doLogIn() { - print( - '---用户名:${_usernameController.text}------密码:${_passwordController.text}---'); - String username = _usernameController.text; - String password = _passwordController.text; - - if (!_preValidate(username, password)) return; - - BlocProvider.of(context).add(DoLogin(username:username, password: password)); - } - - Widget buildUsernameInput() { - return Column( - children: [ - Container( - decoration: BoxDecoration( - border: Border.all( - color: Colors.grey.withOpacity(0.5), - width: 1.0, - ), - borderRadius: BorderRadius.circular(15.0), - ), - margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0), - child: Row( - children: [ - Padding( - padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), - child: Icon( - Icons.person_outline, - color: Colors.grey, - ), - ), - Container( - height: 20.0, - width: 1.0, - color: Colors.grey.withOpacity(0.5), - margin: const EdgeInsets.only(left: 00.0, right: 10.0), - ), - Expanded( - child: TextField( - controller: _usernameController, - decoration: InputDecoration( - border: InputBorder.none, - hintText: '请输入用户名...', - hintStyle: TextStyle(color: Colors.grey), - ), - ), - ) - ], - ), - ) - ], - ); - } - - Widget buildPasswordInput() { - return Column( - children: [ - Container( - decoration: BoxDecoration( - border: Border.all( - color: Colors.grey.withOpacity(0.5), - width: 1.0, - ), - borderRadius: BorderRadius.circular(15.0), - ), - margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0), - child: Row( - children: [ - Padding( - padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), - child: Icon( - Icons.lock_outline, - color: Colors.grey, - ), - ), - Container( - height: 30.0, - width: 1.0, - color: Colors.grey.withOpacity(0.5), - margin: const EdgeInsets.only(left: 00.0, right: 10.0), - ), - Expanded( - child: TextField( - obscureText: !_showPwd, - controller: _passwordController, - decoration: InputDecoration( - border: InputBorder.none, - hintText: '请输入密码...', - hintStyle: TextStyle(color: Colors.grey), - ), - ), - ) - ], - ), - ) - ], - ); - } - - Widget buildOtherLogin() { - return Wrap( - alignment: WrapAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.only(top: 30.0), - child: Row( - children: [ - Expanded( - child: Divider( - height: 20, - )), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - '第三方登录', - style: TextStyle(color: Colors.grey), - ), - ), - Expanded( - child: Divider( - height: 20, - )), - ], - ), - ), - Icon( - TolyIcon.icon_github, - color: Colors.black, - size: 30, - ) - ], - ); - } - - Widget _buildBtnByState(BuildContext context, LoginState state) { - if(state is LoginLoading){ - return Container( - margin: EdgeInsets.only(top: 10, bottom: 0), - height: 40, - width: 40, - child: RaisedButton( - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20))), - color: Colors.blue.withOpacity(0.4), - onPressed: _doLogIn, - child: CupertinoActivityIndicator(), - )); - } - return Container( - margin: EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 0), - height: 40, - width: MediaQuery.of(context).size.width, - child: RaisedButton( - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20))), - color: Colors.blue, - onPressed: _doLogIn, - child: Text("进入 Unit 世界", - style: TextStyle(color: Colors.white, fontSize: 18)), - )); - } - - void _listenLoginState(BuildContext context, LoginState state) { - if (state is LoginSuccess) { - Navigator.of(context).pop(); - } - if (state is LoginError) { - Toast.toast(context, '登录失败 : ${state.message}!', - color: Colors.red, duration: Duration(seconds: 2)); - } - } - - bool _preValidate(String username, String password) { - if (username.isEmpty || password.isEmpty) { - Toast.toast(context, '登录失败 : 用户名和密码不能为空!', - color: Colors.orange, duration: Duration(seconds: 2)); - return false; - } - return true; - } -} diff --git a/lib/user_system/pages/login/login_page.dart b/lib/user_system/pages/login/login_page.dart deleted file mode 100644 index 19b25f3a5..000000000 --- a/lib/user_system/pages/login/login_page.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_unit/user_system/pages/register/arc_clipper.dart'; -import 'login_form.dart'; - -/// create by 张风捷特烈 on 2020/4/24 -/// contact me by email 1981462002@qq.com -/// 说明: - -class LoginPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - Size winSize = MediaQuery.of(context).size; - - return Scaffold( - body: SingleChildScrollView( - child: Wrap(children: [ - Stack(children:[ - UnitArcBackground(height: winSize.height * 0.32), - Positioned( - top: 20, - child: BackButton(color: Colors.white)), - ]), - Container( - // color: Colors.green, - height: winSize.height * 0.68, - width: MediaQuery.of(context).size.width, - padding: const EdgeInsets.only(left: 20.0, right: 20, top: 20), - child: Stack( - alignment: Alignment.center, - children: [ - LoginFrom(), - ], - )) - ]), - )); - } -} diff --git a/lib/user_system/pages/register/register_page.dart b/lib/user_system/pages/register/register_page.dart deleted file mode 100644 index a36a36eb8..000000000 --- a/lib/user_system/pages/register/register_page.dart +++ /dev/null @@ -1,206 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/user_system/api/system_api.dart'; -import 'package:flutter_unit/app/utils/Toast.dart'; -import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; -import 'package:flutter_unit/user_system/bloc/authentic/bloc.dart'; -import 'package:flutter_unit/user_system/bloc/authentic/state.dart'; -import 'package:flutter_unit/user_system/bloc/login/bloc.dart'; -import 'package:flutter_unit/user_system/bloc/login/event.dart'; -import 'package:flutter_unit/user_system/bloc/register/bloc.dart'; -import 'package:flutter_unit/user_system/bloc/register/event.dart'; -import 'package:flutter_unit/user_system/bloc/register/state.dart'; -import 'package:flutter_unit/views/components/permanent/icon_input.dart'; - -import 'arc_clipper.dart'; -import 'send_code.dart'; - -/// create by 张风捷特烈 on 2020/4/24 -/// contact me by email 1981462002@qq.com -/// 说明: - -class RegisterPage extends StatefulWidget { - @override - _RegisterPageState createState() => _RegisterPageState(); -} - -class _RegisterPageState extends State { - final _emailCtrl = TextEditingController(text: '1981462002@qq.com'); - final _codeCtrl = TextEditingController(text: ''); - - ValueNotifier _enableRegister= ValueNotifier(false); - - bool get enable => _emailCtrl.text.isNotEmpty && _codeCtrl.text.isNotEmpty; - - @override - void initState() { - super.initState(); - _emailCtrl.addListener(() { - _enableRegister.value = enable; - }); - - _codeCtrl.addListener(() { - _enableRegister.value = enable; - }); - } - - - @override - Widget build(BuildContext context) { - Size winSize = MediaQuery.of(context).size; - - return Scaffold( - body: SingleChildScrollView( - child: Wrap(children: [ - Stack(children:[ - UnitArcBackground(height: winSize.height * 0.32), - Positioned( - top: 20, - child: BackButton(color: Colors.white)), - ]), - Container( - width: winSize.width, - height: winSize.height * 0.68, - padding: const EdgeInsets.only(left: 20.0, right: 20, top: 20), - child: Container( - // color: Colors.green, - child: Column( - children: [ - const Text( - "FlutterUnit 注册", - style: TextStyle(fontSize: 25), - ), - const SizedBox( - height: 5, - ), - const Text( - "更多精彩,更多体验 ~", - style: TextStyle(color: Colors.grey), - ), - const Spacer( - flex: 1, - ), - IconInput( - icon: Icons.person_outline, - textFiled: TextField( - controller: _emailCtrl, - decoration: InputDecoration( - border: InputBorder.none, - hintText: '请输入邮箱', - hintStyle: TextStyle(color: Colors.grey), - ), - ), - ), - const SizedBox(height: 10), - buildInputWithSend(), - const Spacer(flex: 1), - _buildBtn(), - const Spacer(flex: 4), - ], - ), - )) - ]), - )); - } - - Stack buildInputWithSend() { - return Stack( - alignment: Alignment(.8, 0), - children: [ - IconInput( - icon: Icons.code_outlined, - textFiled: TextField( - controller: _codeCtrl, - decoration: InputDecoration( - border: InputBorder.none, - hintText: '请输入验证码', - hintStyle: TextStyle(color: Colors.grey), - ), - ), - ), - CountDownWidget( - onPress: _sendEmail, - ) - ], - ); - } - - _sendEmail(BuildContext context) async { - if (!_checkEmail(_emailCtrl.text)) { - Toast.toast(context, '邮箱格式校验错误,请重试!', - color: Colors.orange, duration: Duration(seconds: 2)); - return; - } - - ResultBean result = - await SystemApi.sendEmail(email: _emailCtrl.text); - print(result); - if (result.status) { - Toast.toast(context, '验证码发送成功,请注意邮箱查收!', duration: Duration(seconds: 2)); - } else { - Toast.toast(context, '验证码发送失败: ${result.msg}!', - color: Colors.red, duration: Duration(seconds: 2)); - } - } - - bool _checkEmail(String email) { - if (email == null) return false; - RegExp exp = - RegExp(r'^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$'); - return exp.hasMatch(email); - } - - Widget _buildBtn() => Container( - margin: EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 0), - height: 40, - width: MediaQuery.of(context).size.width, - child: BlocConsumer( - builder: _build, - listener: _listener, - ), - ); - - void _doRegister() { - BlocProvider.of(context) - .add(DoRegister(_emailCtrl.text, _codeCtrl.text)); - } - - Widget _build(BuildContext context, state) { - bool enable = state is RegisterLoading || state is RegisterSuccess; - String info = enable ? '注册中...' : '开启 Unit 新世界'; - return BlocListener( - listener: _listenerLogin, - child: ValueListenableBuilder( - valueListenable: _enableRegister, - builder: (ctx,value,child){ - return RaisedButton( - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20))), - color: Colors.blue, - disabledColor: Colors.blue.withOpacity(0.6), - onPressed: (enable||!value) ? null : _doRegister, - child: Text(info, - style: TextStyle(color: Colors.white, fontSize: 18))); - }, - )); - } - - void _listener(BuildContext context, RegisterState state) { - if (state is RegisterError) { - Toast.toast(context, '注册失败 : ${state.message}!', - color: Colors.red, duration: Duration(seconds: 2)); - } - - if (state is RegisterSuccess) { - BlocProvider.of(context) - .add(DoLogin(username:_emailCtrl.text, password:_codeCtrl.text)); - } - } - - void _listenerLogin(BuildContext context, AuthenticState state) { - if (state is AuthSuccess) { - Navigator.pop(context); - } - } -} diff --git a/lib/views/components/drop_selectable_widget.dart b/lib/views/components/drop_selectable_widget.dart deleted file mode 100644 index 799cf26f2..000000000 --- a/lib/views/components/drop_selectable_widget.dart +++ /dev/null @@ -1,199 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -typedef OnDropSelected = void Function(int index); - -class DropSelectableWidget extends StatefulWidget { - final List data; - final OnDropSelected onDropSelected; - final Color disableColor; - final double iconSize; - final double height; - final double width; - final double fontSize; - - const DropSelectableWidget( - {Key key, - this.data = const [], - this.onDropSelected, - this.disableColor = Colors.black, - this.iconSize = 24, - this.height = 30, - this.width = 200, - this.fontSize = 14, - }) - : super(key: key); - - @override - _DropSelectableWidgetState createState() => _DropSelectableWidgetState(); -} - -class _DropSelectableWidgetState extends State - with SingleTickerProviderStateMixin { - FocusNode _node; - bool _focused = false; - FocusAttachment _nodeAttachment; - Color _color = Colors.white; - OverlayEntry _overlayEntry; - AnimationController _ctrl; - Animation animation; - final LayerLink layerLink = LayerLink(); - - int _selectedIndex = 0; - - @override - void initState() { - super.initState(); - - _ctrl = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 200), - ); - - animation = Tween(begin: 0, end: pi).animate(_ctrl); - _node = FocusNode() - ..addListener(() { - if (_node.hasFocus != _focused) { - if (!_focused) { - _ctrl.forward(); - _showOverlay(); - } else { - _hideOverlay(); - _ctrl.reverse(); - } - setState(() { - _focused = _node.hasFocus; - }); - } - }); - _nodeAttachment = _node.attach(context); - } - - @override - void dispose() { - _node.dispose(); - _ctrl.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - _nodeAttachment.reparent(); - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - if (_focused) { - _node.unfocus(); - } else { - _node.requestFocus(); - } - }, - child: CompositedTransformTarget( - link: layerLink, - child: buildTarget(), - ), - ); - } - - void _showOverlay() { - _overlayEntry = _createOverlayEntry(); - Overlay.of(context).insert(_overlayEntry); - } - - void _hideOverlay() { - _overlayEntry?.remove(); - } - - Widget buildTarget() { - return Container( - width: widget.width, - height: widget.height, - padding: EdgeInsets.only(left: 10, right: 10), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - border: Border.all( - color: _focused ? Colors.blue : widget.disableColor, - )), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '${widget.data.isNotEmpty ? widget.data[_selectedIndex] : "暂无数据"}',style: TextStyle( - height: 1, - fontSize: widget.fontSize - ),), - AnimatedBuilder( - animation: animation, - builder: (_, child) => Transform.rotate( - angle: animation.value, - child: child, - ), - child: Icon( - Icons.keyboard_arrow_down, - size: widget.iconSize, - ), - ), - ], - ), - ); - } - - OverlayEntry _createOverlayEntry() => OverlayEntry( - builder: (BuildContext context) => UnconstrainedBox( - child: CompositedTransformFollower( - link: layerLink, - targetAnchor: Alignment.bottomCenter, - followerAnchor: Alignment.topCenter, - child: Padding( - padding: const EdgeInsets.only(top: 4.0), - child: Material( - shape: RoundedRectangleBorder( - side: BorderSide.none, - borderRadius: BorderRadius.all(Radius.circular(5))), - elevation: 1, - child: ClipRRect( - borderRadius: BorderRadius.circular(5), - child: Container( - height: 200, - // alignment: Alignment.center, - decoration: BoxDecoration( - color: Color(0xffDAE3FF), - ), - // padding: const EdgeInsets.only(top: 5), - width: widget.width, - child: CupertinoScrollbar( - child: ListView.builder( - padding: EdgeInsets.zero, - // shrinkWrap: true, - itemCount: widget.data.length, - itemBuilder: _buildItem), - ), - ), - ), - ), - ), - ), - ), - ); - - Widget _buildItem(BuildContext context, int index) { - return Material( - child: InkWell( - onTap: () { - if (_selectedIndex != index) widget.onDropSelected?.call(index); - _selectedIndex = index; - _overlayEntry.markNeedsBuild(); - _node.unfocus(); - }, - child: Container( - padding: EdgeInsets.all(8), - color: index == _selectedIndex - ? Colors.blue.withOpacity(0.2) - : Colors.transparent, - child: Text('${widget.data[index]}',style: TextStyle(fontSize: widget.fontSize),)), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/components/permanent/circle.dart b/lib/views/components/permanent/circle.dart deleted file mode 100644 index e50a58f61..000000000 --- a/lib/views/components/permanent/circle.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; - -class Circle extends StatelessWidget { - final Color color; - final double radius; - final bool showShadow; - final Widget child; - - const Circle({this.color=Colors.blue, this.radius=6,this.showShadow=true,this.child}); - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - child: child==null?Container():child, - width: 2*radius, - height: 2*radius, - decoration: BoxDecoration( - color: color, - shape: BoxShape.circle, - boxShadow: [ - if (showShadow) - BoxShadow( - color: Colors.grey, - offset: Offset(.5,.5), - blurRadius: .5, - )] - ), - ); - } -} diff --git a/lib/views/components/permanent/circle_image.dart b/lib/views/components/permanent/circle_image.dart deleted file mode 100644 index 08c628f19..000000000 --- a/lib/views/components/permanent/circle_image.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -class CircleImage extends StatelessWidget { - 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) { - Widget headIcon = Container( - width: size, - height: size, - decoration: BoxDecoration( - shape: BoxShape.circle, //圆形装饰线 - color: roundColor ?? Colors.white, - boxShadow: [ - BoxShadow( - //阴影 - color: shadowColor ?? Colors.grey.withOpacity(0.3), - offset: Offset(0.0, 0.0), blurRadius: 3.0, spreadRadius: 0.0, - ), - ], - ), - child: Padding( - padding: EdgeInsets.all(borderSize), - child: - CircleAvatar( - backgroundImage: image, - ), - ), - ); - return headIcon; - } -} diff --git a/lib/views/components/permanent/circle_text.dart b/lib/views/components/permanent/circle_text.dart deleted file mode 100644 index f453082cd..000000000 --- a/lib/views/components/permanent/circle_text.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/material.dart'; - -class CircleText extends StatelessWidget { - CircleText( - {Key key, - @required this.text, - this.size = 70, - this.fontSize = 24, - this.color = Colors.white, - this.shadowColor, - this.backgroundColor, - this.roundColor}) - : super(key: key); - final String text; //图片 - final double size; //大小 - final double fontSize; //大小 - final Color shadowColor; //阴影颜色 - final Color color; //阴影颜色 - final Color roundColor; //边框颜色 - final Color backgroundColor; //边框颜色 - @override - Widget build(BuildContext context) { - Widget headIcon = Container( - width: size, - height: size, - decoration: BoxDecoration( - shape: BoxShape.circle, //圆形装饰线 - color: roundColor ?? Colors.white, - boxShadow: [ - BoxShadow( - //阴影 - color: shadowColor ?? Colors.grey.withOpacity(0.3), - offset: Offset(0.0, 0.0), blurRadius: 3.0, spreadRadius: 0.0, - ), - ], - ), - child: Padding( - padding: EdgeInsets.all(3), - child: Container( - alignment: Alignment.center, - width: size, - height: size, - decoration: BoxDecoration( - shape: BoxShape.circle, //圆形装饰线 - color: backgroundColor??Color(0xffD8F5FF), - ), - child: Text( - text.length>2?text.substring(0, 2):text, - style: TextStyle( - fontSize: fontSize, - color: color, - fontWeight: FontWeight.bold, - shadows: [ - Shadow( - //阴影 - color: Colors.grey, - offset: Offset(1.0, 1.0), blurRadius: 1.0, - ) - ], - ), - )), - ), - ); - return headIcon; - } -} \ No newline at end of file diff --git a/lib/views/components/permanent/code/code_widget.dart b/lib/views/components/permanent/code/code_widget.dart deleted file mode 100644 index 2d787eb51..000000000 --- a/lib/views/components/permanent/code/code_widget.dart +++ /dev/null @@ -1,55 +0,0 @@ - -/// create by 张风捷特烈 on 2020-04-15 -/// contact me by email 1981462002@qq.com -/// 说明: - -import 'package:flutter/material.dart'; -import 'language/dart_languge.dart'; - -import 'high_light_code.dart'; -import 'highlighter_style.dart'; - -class CodeWidget extends StatelessWidget { - CodeWidget({Key key, @required this.code, 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; - if (code == null) { - return Container(); - } else { - 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: EdgeInsets.all(10), - decoration: BoxDecoration( - color: style.backgroundColor ?? Color(0xffF6F8FA), - borderRadius: BorderRadius.all(Radius.circular(5.0))), - ), - ); - } - return body; - } -} \ No newline at end of file diff --git a/lib/views/components/permanent/code/high_light_code.dart b/lib/views/components/permanent/code/high_light_code.dart deleted file mode 100644 index d0dbabc35..000000000 --- a/lib/views/components/permanent/code/high_light_code.dart +++ /dev/null @@ -1,272 +0,0 @@ -/// create by 张风捷特烈 on 2020-04-15 -/// contact me by email 1981462002@qq.com -/// 说明: - -// Copyright 2016 The Chromium 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 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/permanent/code/language/dart_languge.dart'; -import 'package:flutter_unit/views/components/permanent/code/language/language.dart'; -import 'package:string_scanner/string_scanner.dart'; - -import 'highlighter_style.dart'; - -/// final SyntaxHighlighterStyle style = SyntaxHighlighterStyle.lightThemeStyle(); -/// DartSyntaxHighlighter(style).format(source) - -abstract class Highlighter { - // ignore: one_member_abstracts - Language language; - - TextSpan format(String src); -} - -//暗黑模式下的高亮样式 -class CodeHighlighter extends Highlighter { - CodeHighlighter( - {Language language = const DartLanguage(), HighlighterStyle style}) { - _spans = <_HighlightSpan>[]; - this.language = language; - _style = style ?? HighlighterStyle.fromColors(HighlighterStyle.lightColor); - } - - HighlighterStyle _style; - - String _src; - StringScanner _scanner; - - List<_HighlightSpan> _spans; - - @override - TextSpan format(String src) { - _src = src; - _scanner = StringScanner(_src); - - if (_generateSpans()) { - // Successfully parsed the code - final List formattedText = []; - int currentPosition = 0; - - for (_HighlightSpan span in _spans) { - if (currentPosition != span.start) - formattedText - .add(TextSpan(text: _src.substring(currentPosition, span.start))); - - formattedText.add(TextSpan( - style: span.textStyle(_style), text: span.textForSpan(_src))); - - currentPosition = span.end; - } - - if (currentPosition != _src.length) - formattedText - .add(TextSpan(text: _src.substring(currentPosition, _src.length))); - - return TextSpan(style: _style.baseStyle, children: formattedText); - } else { - // Parsing failed, return with only basic formatting - return TextSpan(style: _style.baseStyle, text: src); - } - } - - bool _generateSpans() { - int lastLoopPosition = _scanner.position; - - while (!_scanner.isDone) { - // Skip White space - _scanner.scan(RegExp(r'\s+')); - - // Block comments - if (_scanner.scan(RegExp(r'/\*(.|\n)*\*/'))) { - _spans.add(_HighlightSpan(_HighlightType.comment, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Line comments - if (_scanner.scan('//')) { - final int startComment = _scanner.lastMatch.start; - - bool eof = false; - int endComment; - if (_scanner.scan(RegExp(r'.*\n'))) { - endComment = _scanner.lastMatch.end - 1; - } else { - eof = true; - endComment = _src.length; - } - - _spans.add( - _HighlightSpan(_HighlightType.comment, startComment, endComment)); - - if (eof) break; - continue; - } - - // Raw r"String" - if (_scanner.scan(RegExp(r'r".*"'))) { - _spans.add(_HighlightSpan(_HighlightType.string, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Raw r'String' - if (_scanner.scan(RegExp(r"r'.*'"))) { - _spans.add(_HighlightSpan(_HighlightType.string, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Multiline """String""" - if (_scanner.scan(RegExp(r'"""(?:[^"\\]|\\(.|\n))*"""'))) { - _spans.add(_HighlightSpan(_HighlightType.string, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Multiline '''String''' - if (_scanner.scan(RegExp(r"'''(?:[^'\\]|\\(.|\n))*'''"))) { - _spans.add(_HighlightSpan(_HighlightType.string, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // "String" - if (_scanner.scan(RegExp(r'"(?:[^"\\]|\\.)*"'))) { - _spans.add(_HighlightSpan(_HighlightType.string, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // 'String' - if (_scanner.scan(RegExp(r"'(?:[^'\\]|\\.)*'"))) { - _spans.add(_HighlightSpan(_HighlightType.string, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Double - if (_scanner.scan(RegExp(r'\d+\.\d+'))) { - _spans.add(_HighlightSpan(_HighlightType.number, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Integer - if (_scanner.scan(RegExp(r'\d+'))) { - _spans.add(_HighlightSpan(_HighlightType.number, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Punctuation - if (_scanner.scan(RegExp(r'[\[\]{}().!=<>&\|\?\+\-\*/%\^~;:,]'))) { - _spans.add(_HighlightSpan(_HighlightType.punctuation, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Meta data - if (_scanner.scan(RegExp(r'@\w+'))) { - _spans.add(_HighlightSpan(_HighlightType.keyword, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Words - if (_scanner.scan(RegExp(r'\w+'))) { - _HighlightType type; - String word = _scanner.lastMatch[0]; - - if (word.startsWith('_')) word = word.substring(1); - - if (language.containsKeywords(word)) - type = _HighlightType.keyword; - else if (language.containsInTypes(word)) - type = _HighlightType.keyword; - else if (_firstLetterIsUpperCase(word)) - type = _HighlightType.klass; - else if (word.length >= 2 && - word.startsWith('k') && - _firstLetterIsUpperCase(word.substring(1))) - type = _HighlightType.constant; - - if (type != null) { - _spans.add(_HighlightSpan( - type, _scanner.lastMatch.start, _scanner.lastMatch.end)); - } - } - // Check if this loop did anything - if (lastLoopPosition == _scanner.position) { - // Failed to parse this file, abort gracefully - return false; - } - lastLoopPosition = _scanner.position; - } - - _simplify(); - return true; - } - - void _simplify() { - for (int i = _spans.length - 2; i >= 0; i -= 1) { - if (_spans[i].type == _spans[i + 1].type && - _spans[i].end == _spans[i + 1].start) { - _spans[i] = - _HighlightSpan(_spans[i].type, _spans[i].start, _spans[i + 1].end); - _spans.removeAt(i + 1); - } - } - } - - bool _firstLetterIsUpperCase(String str) { - if (str.isNotEmpty) { - final String first = str.substring(0, 1); - return first == first.toUpperCase(); - } - return false; - } -} - -enum _HighlightType { - number, - comment, - keyword, - string, - punctuation, - klass, - constant -} - -class _HighlightSpan { - _HighlightSpan(this.type, this.start, this.end); - - final _HighlightType type; - final int start; - final int end; - - String textForSpan(String src) { - return src.substring(start, end); - } - - TextStyle textStyle(HighlighterStyle style) { - if (type == _HighlightType.number) - return style.numberStyle; - else if (type == _HighlightType.comment) - return style.commentStyle; - else if (type == _HighlightType.keyword) - return style.keywordStyle; - else if (type == _HighlightType.string) - return style.stringStyle; - else if (type == _HighlightType.punctuation) - return style.punctuationStyle; - else if (type == _HighlightType.klass) - return style.classStyle; - else if (type == _HighlightType.constant) - return style.constantStyle; - else - return style.baseStyle; - } -} diff --git a/lib/views/components/permanent/code/language/dart_languge.dart b/lib/views/components/permanent/code/language/dart_languge.dart deleted file mode 100644 index 4694cfd9e..000000000 --- a/lib/views/components/permanent/code/language/dart_languge.dart +++ /dev/null @@ -1,49 +0,0 @@ -import 'package:flutter_unit/views/components/permanent/code/language/language.dart'; - -/// create by 张风捷特烈 on 2021/1/21 -/// contact me by email 1981462002@qq.com -/// 说明: - -class DartLanguage extends Language{ - - const DartLanguage() : super('Dart'); - - static const List _kDartKeywords = [ - 'abstract', 'as', 'assert', 'async', 'await', 'break', 'case', 'catch', - 'class', 'const', 'continue', 'default', 'deferred', 'do', 'dynamic', 'else', - 'enum', 'export', 'external', 'extends', 'factory', 'false', 'final', - 'finally', 'for', 'get', 'if', 'implements', 'import', 'in', 'is', 'library', - 'new', 'null', 'operator', 'part', 'rethrow', 'return', 'set', 'static', - 'super', 'switch', 'sync', 'this', 'throw', 'true', 'try', 'typedef', 'var', - 'void', 'while', 'with', 'yield' - ]; - - static const List _kDartInTypes = [ - 'int', 'double', 'num', 'bool' - ]; - - @override - List get keywords => [ - 'abstract', 'as', 'assert', 'async', 'await', 'break', 'case', 'catch', - 'class', 'const', 'continue', 'default', 'deferred', 'do', 'dynamic', 'else', - 'enum', 'export', 'external', 'extends', 'factory', 'false', 'final', - 'finally', 'for', 'get', 'if', 'implements', 'import', 'in', 'is', 'library', - 'new', 'null', 'operator', 'part', 'rethrow', 'return', 'set', 'static', - 'super', 'switch', 'sync', 'this', 'throw', 'true', 'try', 'typedef', 'var', - 'void', 'while', 'with', 'yield' - ]; - - @override - List get inTypes => [ - 'int', 'double', 'num', 'bool' - ]; - - @override - bool containsInTypes(String word) =>_kDartKeywords.contains(word); - - @override - bool containsKeywords(String word)=>_kDartInTypes.contains(word); - - - -} \ No newline at end of file diff --git a/lib/views/components/permanent/code/language/language.dart b/lib/views/components/permanent/code/language/language.dart deleted file mode 100644 index c2ea52d46..000000000 --- a/lib/views/components/permanent/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/lib/views/components/permanent/feedback_widget.dart b/lib/views/components/permanent/feedback_widget.dart deleted file mode 100644 index 235e87cdf..000000000 --- a/lib/views/components/permanent/feedback_widget.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-04-10 -/// contact me by email 1981462002@qq.com -/// 说明: - -enum FeedMode { - scale, - fade, - rotate, -} - -class FeedbackWidget extends StatefulWidget { - final Widget child; - final FeedMode mode; - final Duration duration; - final Function() onPressed; - final Function() onEnd; - final Function() onLongPressed; - final a; - - FeedbackWidget({ - @required this.child, - this.mode = FeedMode.scale, - this.a = 0.9, - this.onLongPressed, - this.duration = const Duration(milliseconds: 150), - this.onPressed, - this.onEnd, - }); - - @override - _FeedBackState createState() => _FeedBackState(); -} - -class _FeedBackState extends State with SingleTickerProviderStateMixin { - AnimationController _controller; - - @override - void initState() { - super.initState(); - _controller = AnimationController( - vsync: this, - duration: widget.duration, - )..addStatusListener((s) { - if (s == AnimationStatus.completed) { - _controller.reverse().then((value) { - if (widget.onEnd != null) widget.onEnd(); - }); - } - }); - } - - // 当父层状态执行 setState, 当前 State 不会执行 initState,而是 didUpdateWidget, - // 因此如果上层状态对某些 widget 配置进行修改,那么当前状态对象便无法知晓,比如 duration 、 - // 如果配置不同了需要在 didUpdateWidget 回调中更新 - // - @override - void didUpdateWidget(FeedbackWidget oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.duration != oldWidget.duration) { - _controller.duration = widget.duration; - } - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onLongPress: widget.onLongPressed, - onTap: () { - _controller.forward(); - if (widget.onPressed != null) { - widget.onPressed(); - } - }, - child: AnimatedBuilder( - animation: _controller, - child: widget.child, - builder: (ctx, child) => _buildByMode(child, widget.mode), - )); - } - - Widget _buildByMode(Widget child, FeedMode mode) { - double rate = (widget.a - 1) * _controller.value + 1; - switch (mode) { - case FeedMode.scale: - return Transform.scale(scale: rate, child: widget.child); - case FeedMode.fade: - return Opacity(opacity: rate, child: widget.child); - case FeedMode.rotate: - return Transform.rotate(angle: rate * pi * 2, child: widget.child); - } - return Container(); - } -} diff --git a/lib/views/components/permanent/input_button.dart b/lib/views/components/permanent/input_button.dart deleted file mode 100644 index d82b261aa..000000000 --- a/lib/views/components/permanent/input_button.dart +++ /dev/null @@ -1,138 +0,0 @@ -import 'package:flutter/material.dart'; - -typedef SubmitCallback = void Function(String str); - -class InputButtonConfig { - final double height; //高度 - final IconData iconData; //图标 - final String hint; //提示文字 - final double fontSize; //文字大小 - final Widget front; //前面图标 - final bool submitClear; //是否提交清空 - - - const InputButtonConfig( - {this.height = 36, - - this.iconData = Icons.add, - this.fontSize = 14, - this.submitClear = true, - this.front, - this.hint = "I want to say..."}); -} - -class InputButton extends StatefulWidget { - final SubmitCallback onSubmit; - final ValueChanged onChanged; - final VoidCallback onTap; - final InputButtonConfig config; - final String defaultText; - - InputButton( - {Key key, - this.onSubmit, - this.onChanged, - this.defaultText, - this.onTap, - this.config = const InputButtonConfig()}) - : super(key: key); - - @override - _InputButtonState createState() => _InputButtonState(); -} - -class _InputButtonState extends State { - double _height; - double _fontSize; - Radius _radius; - - TextEditingController _controller; - - @override - void initState() { - super.initState(); - _height = widget.config.height; - _fontSize = widget.config.fontSize; - _radius = Radius.circular(_height / 3.6); - _controller = TextEditingController(text: widget.defaultText??''); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - Widget textField = TextField( - controller: _controller, - maxLines: 1, - style: TextStyle( - fontSize: _fontSize, - color: Colors.lightBlue, - backgroundColor: Colors.white), - decoration: InputDecoration( - filled: true, - fillColor: Colors.white, - hintText: widget.config.hint, - hintStyle: TextStyle(color: Colors.black26, fontSize: _fontSize), - contentPadding: EdgeInsets.only(left: 14.0, top: -_fontSize), - focusedBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.white), - borderRadius: - BorderRadius.only(topLeft: _radius, bottomLeft: _radius), - ), - enabledBorder: UnderlineInputBorder( - borderSide: BorderSide(color: Colors.white), - borderRadius: - BorderRadius.only(topLeft: _radius, bottomLeft: _radius), - ), - ), - onChanged: (str) { - if (widget.onChanged != null) widget.onChanged(str); - }, - onTap: widget.onTap, - ); - Widget btn = RaisedButton( - - elevation: 0, - child: Icon(widget.config.iconData,color: Theme.of(context).primaryColor,), - color: Color(0x99E0E0E0), - padding: EdgeInsets.zero, - onPressed: () { - FocusScope.of(context).requestFocus(FocusNode()); //收起键盘 - if (widget.onSubmit != null) widget.onSubmit(_controller.text); - if (widget.config.submitClear) { - setState(() { - _controller.clear(); - }); - } - }, - ); - Widget inputBtn = Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: Container( - child: textField, - height: _height, - ), - ), - ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.zero, - bottomLeft: Radius.zero, - topRight: _radius, - bottomRight: _radius), - child: Container( - child: btn, - width: _height, - height: _height, - ), - ), - ], - ); - return inputBtn; - } -} diff --git a/lib/views/components/permanent/loading/planet_loading.dart b/lib/views/components/permanent/loading/planet_loading.dart deleted file mode 100644 index 59cbc3c68..000000000 --- a/lib/views/components/permanent/loading/planet_loading.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/permanent/circle.dart'; - -import '../math_runner.dart'; - -/// create by 张风捷特烈 on 2020/10/24 -/// contact me by email 1981462002@qq.com -/// 说明: - -class PlateLoading extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - width: 150, - height: 150, - child: Stack(alignment: Alignment.center, children: [ - Text('loading ...'), - MathRunner( - reverse: false, - f: (t) => 0.4*cos(t * pi), - g: (t) => 0.7 * sin(t * pi), - child: Circle(color: Colors.blue,radius: 8,)), - - MathRunner( - reverse: false, - f: (t) => 0.7 * cos(t * pi), - g: (t) => 1 * sin(t * pi), - child: Circle(color: Colors.yellow,radius: 8,)), - MathRunner( - reverse: false, - f: (t) => -0.8 *cos(t * pi), - g: (t) => 1 * sin(t * pi), - child: Circle(color: Colors.red,radius: 8,)), - MathRunner( - reverse: false, - f: (t) => 1*cos(t * pi), - g: (t) => 0.7 * sin(t * pi), - child: Circle(color: Colors.green,radius: 8,)), - - MathRunner( - reverse: false, - f: (t) => 1 * cos(t * pi), - g: (t) => -0.7 * sin(t * pi), - child: Circle(color: Colors.purple,radius: 8,)), - ]), - ); - } -} diff --git a/lib/views/components/permanent/markdown/markdown_widget.dart b/lib/views/components/permanent/markdown/markdown_widget.dart deleted file mode 100644 index 188038ce3..000000000 --- a/lib/views/components/permanent/markdown/markdown_widget.dart +++ /dev/null @@ -1,187 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_markdown/flutter_markdown.dart'; -import 'package:flutter_unit/app/res/style/unit_text_style.dart'; -import 'package:flutter_unit/app/res/style/unit_color.dart'; -import 'package:flutter_unit/views/components/permanent/markdown/syntax_high_lighter.dart'; - - -class MarkdownWidget extends StatelessWidget { - static const int kWhite = 0; - static const int kDarkLight = 1; - static const int kDarkTheme = 2; - - final String markdownData; - final int style; - - MarkdownWidget({this.markdownData = "", this.style = kWhite}); - - MarkdownStyleSheet _getCommonSheet(BuildContext context, Color codeBackground) { - MarkdownStyleSheet markdownStyleSheet = MarkdownStyleSheet.fromTheme(Theme.of(context)); - return markdownStyleSheet.copyWith( - codeblockDecoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(4.0)), - color: codeBackground, - border: Border.all( - color: UnitColor.subTextColor, width: 0.3))) - .copyWith( - blockquoteDecoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(4.0)), - color: UnitColor.subTextColor, - border: Border.all( - color: UnitColor.subTextColor, width: 0.3)), - blockquote: TStyleUnit.smallTextWhite); - } - - _getStyleSheetDark(BuildContext context) { - return _getCommonSheet(context, Color.fromRGBO(40, 44, 52, 1.00)).copyWith( - p: TStyleUnit.smallTextWhite, - h1: TStyleUnit.largeLargeTextWhite, - h2: TStyleUnit.largeTextWhiteBold, - h3: TStyleUnit.normalTextMitWhiteBold, - h4: TStyleUnit.middleTextWhite, - h5: TStyleUnit.smallTextWhite, - h6: TStyleUnit.smallTextWhite, - em: const TextStyle(fontStyle: FontStyle.italic), - strong: TStyleUnit.middleTextWhiteBold, - code: TStyleUnit.smallSubText, - ); - } - - MarkdownStyleSheet _getStyleSheetWhite(BuildContext context) { - return _getCommonSheet(context, Color.fromRGBO(40, 44, 52, 1.00)).copyWith( - p: TStyleUnit.smallText, - h1: TStyleUnit.largeLargeText, - h2: TStyleUnit.largeTextBold, - h3: TStyleUnit.normalTextBold, - h4: TStyleUnit.middleText, - h5: TStyleUnit.smallText, - h6: TStyleUnit.smallText, - strong: TStyleUnit.middleTextBold, - code: TStyleUnit.smallSubText, - ); - } - - _getStyleSheetTheme(BuildContext context) { - return _getCommonSheet(context, Color.fromRGBO(40, 44, 52, 1.00)).copyWith( - p: TStyleUnit.smallTextWhite, - h1: TStyleUnit.largeLargeTextWhite, - h2: TStyleUnit.largeTextWhiteBold, - h3: TStyleUnit.normalTextMitWhiteBold, - h4: TStyleUnit.middleTextWhite, - h5: TStyleUnit.smallTextWhite, - h6: TStyleUnit.smallTextWhite, - em: const TextStyle(fontStyle: FontStyle.italic), - strong: TStyleUnit.middleTextWhiteBold, - code: TStyleUnit.smallSubText, - ); - } - - _getBackgroundColor(context) { - Color background = UnitColor.white; - switch (style) { - case kDarkLight: - background = UnitColor.primaryLightValue; - break; - case kDarkTheme: - background = Theme.of(context).primaryColor; - break; - } - return background; - } - - _getStyle(BuildContext context) { - MarkdownStyleSheet styleSheet = _getStyleSheetWhite(context); - switch (style) { - case kDarkLight: - styleSheet = _getStyleSheetDark(context); - break; - case kDarkTheme: - styleSheet = _getStyleSheetTheme(context); - break; - } - return styleSheet; - } - - _getMarkDownData(String markdownData) { - ///优化图片显示 - RegExp exp = RegExp(r'!\[.*\]\((.+)\)'); - RegExp expImg = RegExp("|\/>)"); - RegExp expSrc = RegExp("src=[\'\"]?([^\'\"]*)[\'\"]?"); - - String mdDataCode = markdownData; - try { - Iterable tags = exp.allMatches(markdownData); - if (tags != null && tags.length > 0) { - for (Match m in tags) { - String imageMatch = m.group(0); - if (imageMatch != null && !imageMatch.contains(".svg")) { - String match = imageMatch.replaceAll("\)", "?raw=true)"); - if (!match.contains(".svg") && match.contains("http")) { - ///增加点击 - String src = match - .replaceAll( RegExp(r'!\[.*\]\('), "") - .replaceAll(")", ""); - String actionMatch = "[$match]($src)"; - match = actionMatch; - } else { - match = ""; - } - mdDataCode = mdDataCode.replaceAll(m.group(0), match); - } - } - } - - ///优化img标签的src资源 - tags = expImg.allMatches(markdownData); - if (tags != null && tags.length > 0) { - for (Match m in tags) { - String imageTag = m.group(0); - String match = imageTag; - if (imageTag != null) { - Iterable srcTags = expSrc.allMatches(imageTag); - for (Match srcMatch in srcTags) { - String srcString = srcMatch.group(0); - if (srcString != null && srcString.contains("http")) { - String newSrc = srcString.substring( - srcString.indexOf("http"), srcString.length - 1) + - "?raw=true"; - match = "[![]($newSrc)]($newSrc)"; - } - } - } - mdDataCode = mdDataCode.replaceAll(imageTag, match); - } - } - } catch (e) { - print(e.toString()); - } - return mdDataCode; - } - - @override - Widget build(BuildContext context) { - return Container( - color: _getBackgroundColor(context), - padding: const EdgeInsets.all(5.0), - child: SingleChildScrollView( - child: MarkdownBody( - styleSheet: _getStyle(context), - syntaxHighlighter: Highlighter(), - data: _getMarkDownData(markdownData), - onTapLink: (String text, String href, String title) { -// CommonUtils.launchUrl(context, source); - }, - ), - ), - ); - } -} - -class Highlighter extends SyntaxHighlighter { - @override - TextSpan format(String source) { - String showSource = source.replaceAll("<", "<"); - showSource = showSource.replaceAll(">", ">"); - return DartSyntaxHighlighter().format(showSource); - } -} diff --git a/lib/views/components/permanent/markdown/syntax_high_lighter.dart b/lib/views/components/permanent/markdown/syntax_high_lighter.dart deleted file mode 100644 index 305bb11c0..000000000 --- a/lib/views/components/permanent/markdown/syntax_high_lighter.dart +++ /dev/null @@ -1,408 +0,0 @@ -import 'package:string_scanner/string_scanner.dart'; -import 'package:flutter/material.dart'; - -class SyntaxHighlighterStyle { - SyntaxHighlighterStyle( - {this.baseStyle, - this.numberStyle, - this.commentStyle, - this.keywordStyle, - this.stringStyle, - this.punctuationStyle, - this.classStyle, - this.constantStyle}); - -//123 - static SyntaxHighlighterStyle defaultStyle() { - return SyntaxHighlighterStyle( - baseStyle: TextStyle(color: Color.fromRGBO(212, 212, 212, 1.0)), - numberStyle: TextStyle(color: Colors.blue[800]), - commentStyle: TextStyle(color: Color.fromRGBO(124, 126, 120, 1.0)), - keywordStyle: TextStyle(color: Color.fromRGBO(228, 125, 246, 1.0)), - stringStyle: TextStyle(color: Color.fromRGBO(150, 190, 118, 1.0)), - punctuationStyle: - TextStyle(color: Color.fromRGBO(212, 212, 212, 1.0)), - classStyle: TextStyle(color: Color.fromRGBO(150, 190, 118, 1.0)), - constantStyle: TextStyle(color: Colors.brown[500])); - } - - final TextStyle baseStyle; - final TextStyle numberStyle; - final TextStyle commentStyle; - final TextStyle keywordStyle; - final TextStyle stringStyle; - final TextStyle punctuationStyle; - final TextStyle classStyle; - final TextStyle constantStyle; -} - -abstract class SyntaxCostomHighlighter { - TextSpan format(String src); -} - -class DartSyntaxHighlighter extends SyntaxCostomHighlighter { - DartSyntaxHighlighter([this._style]) { - _spans = <_HighlightSpan>[]; - - if (_style == null) _style = SyntaxHighlighterStyle.defaultStyle(); - } - - SyntaxHighlighterStyle _style; - - static const List _kKeywords = const [ - 'abstract', - 'as', - 'assert', - 'async', - 'await', - 'break', - 'case', - 'catch', - 'class', - 'const', - 'continue', - 'default', - 'deferred', - 'do', - 'dynamic', - 'else', - 'enum', - 'export', - 'external', - 'extends', - 'factory', - 'false', - 'final', - 'finally', - 'for', - 'get', - 'if', - 'implements', - 'import', - 'in', - 'is', - 'library', - '', - 'null', - 'operator', - 'part', - 'rethrow', - 'return', - 'set', - 'static', - 'super', - 'switch', - 'sync', - 'this', - 'throw', - 'true', - 'try', - 'typedef', - 'var', - 'void', - 'while', - 'with', - 'yield', - 'print', - 'function', - 'public', - 'protected', - 'private', - 'namespace', - 'using', - 'extends', - 'let', - 'export', - 'default', - 'import', - 'from', - 'PureCommponent', - 'constructor', - 'render', - '\$sudo', - 'console', - 'instanceof' - ]; - - static const List _kBuiltInTypes = const [ - 'int', - 'double', - 'num', - 'bool' - ]; - - String _src; - StringScanner _scanner; - - List<_HighlightSpan> _spans; - - TextSpan format(String src) { - _src = src; - _scanner = StringScanner(_src); - - if (_generateSpans()) { - // Successfully parsed the code - List formattedText = []; - int currentPosition = 0; - - for (_HighlightSpan span in _spans) { - if (currentPosition != span.start) - formattedText.add( - TextSpan(text: _src.substring(currentPosition, span.start))); - - formattedText.add( TextSpan( - style: span.textStyle(_style), text: span.textForSpan(_src))); - - currentPosition = span.end; - } - - if (currentPosition != _src.length) - formattedText.add( - TextSpan(text: _src.substring(currentPosition, _src.length))); - - return TextSpan(style: _style.baseStyle, children: formattedText); - } else { - // Parsing failed, return with only basic formatting - return TextSpan(style: _style.baseStyle, text: src); - } - } - - bool _generateSpans() { - int lastLoopPosition = _scanner.position; - - try { - while (!_scanner.isDone) { - // Skip White space - _scanner.scan( RegExp(r"\s+")); - - // Block comments - if (_scanner.scan( RegExp(r"/\*(.|\n)*\*/"))) { - _spans.add( _HighlightSpan(_HighlightType.comment, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Line comments - if (_scanner.scan("//")) { - int startComment = _scanner.lastMatch.start; - - bool eof = false; - int endComment; - if (_scanner.scan( RegExp(r".*\n"))) { - endComment = _scanner.lastMatch.end - 1; - } else { - eof = true; - endComment = _src.length; - } - - _spans.add( _HighlightSpan( - _HighlightType.comment, startComment, endComment)); - - if (eof) break; - - continue; - } - - if (_scanner.scan("#")) { - int startComment = _scanner.lastMatch.start; - - bool eof = false; - int endComment; - - if (_scanner.scan( RegExp(r".*\n"))) { - endComment = _scanner.lastMatch.end - 1; - } else { - eof = true; - endComment = _src.length; - } - - _spans.add( _HighlightSpan( - _HighlightType.comment, startComment, endComment)); - - if (eof) break; - - continue; - } - // Raw r"String" - if (_scanner.scan( RegExp(r'r".*"'))) { - _spans.add( _HighlightSpan(_HighlightType.string, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Raw r'String' - if (_scanner.scan( RegExp(r"r'.*'"))) { - _spans.add( _HighlightSpan(_HighlightType.string, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Multiline """String""" - if (_scanner.scan( RegExp(r'"""(?:[^"\\]|\\(.|\n))*"""'))) { - _spans.add( _HighlightSpan(_HighlightType.string, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Multiline '''String''' - if (_scanner.scan( RegExp(r"'''(?:[^'\\]|\\(.|\n))*'''"))) { - _spans.add( _HighlightSpan(_HighlightType.string, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // "String" - if (_scanner.scan( RegExp(r'"(?:[^"\\]|\\.)*"'))) { - _spans.add( _HighlightSpan(_HighlightType.string, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // 'String' - if (_scanner.scan( RegExp(r"'(?:[^'\\]|\\.)*'"))) { - _spans.add( _HighlightSpan(_HighlightType.string, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Double - if (_scanner.scan( RegExp(r"\d+\.\d+"))) { - _spans.add( _HighlightSpan(_HighlightType.number, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Integer - if (_scanner.scan( RegExp(r"\d+"))) { - _spans.add( _HighlightSpan(_HighlightType.number, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Punctuation - if (_scanner.scan( RegExp(r"[\[\]{}().!=<>&\|\?\+\-\*/%\^~;:,]"))) { - _spans.add( _HighlightSpan(_HighlightType.punctuation, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - //中文 - if (_scanner.scan( RegExp(r"[\u4e00-\u9fa5]"))) { - _spans.add( _HighlightSpan(_HighlightType.punctuation, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Metadata - if (_scanner.scan( RegExp(r"@\w+"))) { - _spans.add( _HighlightSpan(_HighlightType.keyword, - _scanner.lastMatch.start, _scanner.lastMatch.end)); - continue; - } - - // Words - if (_scanner.scan( RegExp(r"\w+"))) { - _HighlightType type; - - String word = _scanner.lastMatch[0]; - if (word.startsWith("_")) word = word.substring(1); - - if (_kKeywords.contains(word)) - type = _HighlightType.keyword; - else if (_kBuiltInTypes.contains(word)) - type = _HighlightType.keyword; - else if (_firstLetterIsUpperCase(word)) - type = _HighlightType.klass; - else if (word.length >= 2 && - word.startsWith("k") && - _firstLetterIsUpperCase(word.substring(1))) - type = _HighlightType.constant; - - if (type != null) { - _spans.add( _HighlightSpan( - type, _scanner.lastMatch.start, _scanner.lastMatch.end)); - } - } - - // Check if this loop did anything - if (lastLoopPosition == _scanner.position) { - // Failed to parse this file, abort gracefully - if (_spans.length > 0) { - _spans.add( _HighlightSpan(_HighlightType.punctuation, - lastLoopPosition, _scanner.string.length - 1)); - _simplify(); - return true; - } - return false; - } - - lastLoopPosition = _scanner.position; - } - } catch (e) { - print(e.toString()); - } - - _simplify(); - return true; - } - - void _simplify() { - for (int i = _spans.length - 2; i >= 0; i -= 1) { - if (_spans[i].type == _spans[i + 1].type && - _spans[i].end == _spans[i + 1].start) { - _spans[i] = _HighlightSpan( - _spans[i].type, _spans[i].start, _spans[i + 1].end); - _spans.removeAt(i + 1); - } - } - } - - bool _firstLetterIsUpperCase(String str) { - if (str.length > 0) { - String first = str.substring(0, 1); - return first == first.toUpperCase(); - } - return false; - } -} - -enum _HighlightType { - number, - comment, - keyword, - string, - punctuation, - klass, - constant -} - -class _HighlightSpan { - _HighlightSpan(this.type, this.start, this.end); - - final _HighlightType type; - final int start; - final int end; - - String textForSpan(String src) { - return src.substring(start, end); - } - - TextStyle textStyle(SyntaxHighlighterStyle style) { - if (type == _HighlightType.number) - return style.numberStyle; - else if (type == _HighlightType.comment) - return style.commentStyle; - else if (type == _HighlightType.keyword) - return style.keywordStyle; - else if (type == _HighlightType.string) - return style.stringStyle; - else if (type == _HighlightType.punctuation) - return style.punctuationStyle; - else if (type == _HighlightType.klass) - return style.classStyle; - else if (type == _HighlightType.constant) - return style.constantStyle; - else - return style.baseStyle; - } -} diff --git a/lib/views/components/permanent/math_runner.dart b/lib/views/components/permanent/math_runner.dart deleted file mode 100644 index dcf611ba7..000000000 --- a/lib/views/components/permanent/math_runner.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/10/24 -/// contact me by email 1981462002@qq.com -/// 说明: - -typedef FunNum1 = Function(double t); - -class MathRunner extends StatefulWidget { - MathRunner({Key key, this.child, this.f, this.g, this.reverse = true}) - : super(key: key); - final Widget child; - final FunNum1 f; - final FunNum1 g; - final bool reverse; - - @override - _MathRunnerState createState() => _MathRunnerState(); -} - -class _MathRunnerState extends State - with SingleTickerProviderStateMixin { - AnimationController _controller; - Animation animationX; - double _x = -1.0; - double _y = 0; - - @override - void initState() { - _controller = - AnimationController(vsync: this, duration: Duration(seconds: 3))..repeat(reverse: widget.reverse); - animationX = Tween(begin: -1.0, end: 1.0).animate(_controller) - ..addListener(() { - setState(() { - _x = widget.f(animationX.value); - _y = widget.g(animationX.value); - }); - }); - super.initState(); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Container( - child: Align( - alignment: Alignment(_x, _y), - child: widget.child, - ) - ); - } -} diff --git a/lib/views/components/permanent/panel.dart b/lib/views/components/permanent/panel.dart deleted file mode 100644 index 86f1a9024..000000000 --- a/lib/views/components/permanent/panel.dart +++ /dev/null @@ -1,24 +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; - - Panel({this.radius = 5.0, this.color, this.child}); - - @override - Widget build(BuildContext context) { - return Container( - child: child, - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: color ?? Color(0xffF6F8FA), - borderRadius: BorderRadius.all(Radius.circular(radius))), - ); - } -} - - diff --git a/lib/views/components/project/default/error_shower.dart b/lib/views/components/project/default/error_shower.dart deleted file mode 100644 index baae7008e..000000000 --- a/lib/views/components/project/default/error_shower.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/res/color_unit.dart'; -import 'package:flutter_unit/app/res/style_unit.dart'; - -/// create by 张风捷特烈 on 2020/11/17 -/// contact me by email 1981462002@qq.com -/// 说明: 默认 错误视图 - -class ErrorShower extends StatelessWidget { - final String error; - - ErrorShower({Key key, this.error = "出现异常"}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Center( - child: Wrap( - spacing: 10, - direction: Axis.vertical, - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Container( - width: 80, - height: 80, - child: Icon( - Icons.error, - size: 80, - color: ColorUnit.error_color, - )), - Text( - error, - style: TextStyleUnit.headTextStyle.copyWith(color: ColorUnit.error_color), - textAlign: TextAlign.center, - ) - ], - ), - ); - } -} diff --git a/lib/views/components/project/dialogs/delete_category_dialog.dart b/lib/views/components/project/dialogs/delete_category_dialog.dart deleted file mode 100644 index 1296f23ce..000000000 --- a/lib/views/components/project/dialogs/delete_category_dialog.dart +++ /dev/null @@ -1,110 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; - -/// create by 张风捷特烈 on 2020-04-21 -/// contact me by email 1981462002@qq.com -/// 说明: - -class DeleteCategoryDialog extends StatelessWidget { - final String title; - final String content; - final Function() onSubmit; - - - DeleteCategoryDialog({ - this.title='', this.content ='',this.onSubmit - }); - - @override - Widget build(BuildContext context) { - return Container( - - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildBar(context), - _buildTitle(context), - _buildContent(), - _buildFooter(context), - ], - ), - ); - } - - Widget _buildTitle(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - title, - style: TextStyle(color: Colors.red, fontSize: 20), - ), - ], - ); - } - - Widget _buildContent() { - return Padding( - padding: const EdgeInsets.all(15.0), - child: Text(content, - style: TextStyle(color: Colors.grey, 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: [ - FeedbackWidget( - onPressed: onSubmit, - child: Container( - alignment: Alignment.center, - height: 40, - width: 100, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(30)), - color: Theme.of(context).primaryColor), - child: Text('确 定', - style: TextStyle(color: Colors.white, fontSize: 16)), - ), - ), - FeedbackWidget( - onPressed: ()=>Navigator.of(context).pop(), - child: Container( - alignment: Alignment.center, - height: 40, - width: 100, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(30)), - color: Colors.orangeAccent), - child: Text('取 消', - style: TextStyle(color: Colors.white, fontSize: 16)), - ), - ) - ], - ), - ); - } - - _buildBar(context) => Row( - children: [ - Spacer(), - GestureDetector( - onTap: () => Navigator.of(context).pop(), - child: Container( - height: 30, - alignment: Alignment.centerRight, - padding: EdgeInsets.only(right: 10, top: 5), - child: Icon( - Icons.close, - color:Theme.of(context).primaryColor, - ), - ), - ), - ], - ); -} \ No newline at end of file diff --git a/lib/views/components/project/dialogs/dialog_about.dart b/lib/views/components/project/dialogs/dialog_about.dart deleted file mode 100644 index da91816f9..000000000 --- a/lib/views/components/project/dialogs/dialog_about.dart +++ /dev/null @@ -1,60 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/res/str_unit.dart'; - -class DialogAbout extends StatelessWidget { - static show(BuildContext context){ - showDialog(//内置方法,创建对话弹框 - context: context, - builder: (_) => DialogAbout()); - } - @override - Widget build(BuildContext context) { - Widget title = Row( - //标题 - children: [ - Image.asset( - "assets/images/icon_head.webp", - width: 30, - height: 30, - ), - SizedBox( - width: 10, - ), - Expanded(child: Text("关于",style: TextStyle(fontSize: 18),)), - InkWell( - child: Icon(Icons.close), - onTap: ()=>Navigator.of(context).pop(), - ) - ], - ); - Widget content = Column( - //内容 - mainAxisSize: MainAxisSize.min, - children: [ -// 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: [ - Text( - "Power By GF·J·Toly\n张风捷特烈", - textAlign: TextAlign.center, - ), - ], - )) - ]); - } -} diff --git a/lib/views/components/project/items/category_list_item.dart b/lib/views/components/project/items/category_list_item.dart deleted file mode 100644 index 81681c7f2..000000000 --- a/lib/views/components/project/items/category_list_item.dart +++ /dev/null @@ -1,118 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/permanent/circle_text.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/model/category_model.dart'; - -/// create by 张风捷特烈 on 2020-04-21 -/// contact me by email 1981462002@qq.com -/// 说明: - -class CategoryListItem extends StatelessWidget { - final CategoryModel data; - final Function(CategoryModel) onDeleteItemClick; - final Function(CategoryModel) onEditItemClick; - - CategoryListItem({this.data, this.onDeleteItemClick,this.onEditItemClick}); - - @override - Widget build(BuildContext context) { - return Material( - shadowColor: data.color.withAlpha(88), - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), - elevation: 2, - child: Container( - child: _buildChild(context), - padding: const EdgeInsets.all(10), - - decoration:const BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all(Radius.circular(10)), - ), - ), - ); - - } - - _buildChild(BuildContext context) { - final Color themeColor = Theme.of(context).primaryColor; - return Column( - children: [ - _buildTitle(themeColor), - const Divider(), - Expanded( - child: Stack( - fit: StackFit.expand, - children: [ - Container( - padding: const EdgeInsets.only(top: 10, left: 10, right: 10), - child: Text(data.info, - style: TextStyle( - color: Colors.grey, - shadows: [ - Shadow(color: themeColor, offset:const Offset(.2,.2),blurRadius: .5) - ])), - ), - Positioned( - bottom: 5, - right: 5, - child: - FeedbackWidget( - onPressed: () { - if (onEditItemClick != null) { - onEditItemClick(data); - } - }, - child: Icon( - Icons.edit, - size: 20, - color: themeColor, - ), - ) - ) - ], - )), - const Divider(), - Padding( - padding: const EdgeInsets.all(5.0), - child: Text( - '创建于 ${data.createDate}', - style: const TextStyle(color: Colors.grey, fontSize: 12), - ), - ), - ], - ); - } - - Row _buildTitle(Color themeColor) { - return Row( - children: [ - CircleText( - text: "${data.count}", - size: 35, - fontSize: 14, - backgroundColor: data.color, - ), - const SizedBox( - width: 10, - ), - Expanded( - child: Text( - data.name, - style: const TextStyle(fontWeight: FontWeight.bold), - )), - FeedbackWidget( - onPressed: () { - if (onDeleteItemClick != null) { - onDeleteItemClick(data); - } - }, - child: const Icon( - CupertinoIcons.delete_solid, - color: Colors.red, - ), - ) - ], - ); - } -} \ No newline at end of file diff --git a/lib/views/components/project/items/collect_widget_list_item.dart b/lib/views/components/project/items/collect_widget_list_item.dart deleted file mode 100644 index 48ea5a529..000000000 --- a/lib/views/components/project/items/collect_widget_list_item.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_star/flutter_star.dart'; -import 'package:flutter_unit/app/res/cons.dart'; -import 'package:flutter_unit/app/res/style/shape/techno_shape.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/circle_text.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/model/widget_model.dart'; - -class CollectWidgetListItem extends StatelessWidget { - final WidgetModel data; - final Function(WidgetModel) onDeleteItemClick; - - CollectWidgetListItem({this.data, this.onDeleteItemClick}); - - @override - Widget build(BuildContext context) { - return Stack( - children: [ - Material( - color: itemColor.withAlpha(66), - shape: TechnoShapeBorder(color: itemColor), - child: Row( - children: [ - _buildLeading(), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _buildTitle(), - _buildSummary(), - StarScore( - star: Star( - emptyColor: Colors.white, - size: 12, - fillColor: itemColor), - score: data.lever, - ) - ], - ), - ), - ], - ), - ), - Positioned( - bottom: 5, - right: 5, - child: FeedbackWidget( - onPressed: () { - if (onDeleteItemClick != null) { - onDeleteItemClick(data); - } - }, - child: const Icon( - CupertinoIcons.delete_solid, - color: Colors.red, - ), - )) - ], - ); - } - - Widget _buildLeading() => Padding( - padding: const EdgeInsets.only(left: 5, right: 5), - child: data.image == null - ? Material( - color: Colors.transparent, - child: CircleText( - text: data.name, - size: 50, - color: itemColor, - ), - ) - : CircleImage( - image: data.image, - size: 50, - ), - ); - - Color get itemColor => Color(Cons.tabColors[data.family.index]); - - Widget _buildTitle() => Row( - children: [ - Expanded( - child: Text(data.name, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontSize: 17, - fontWeight: FontWeight.bold, - shadows: [ - const Shadow( - color: Colors.white, offset: const Offset(.3, .3)) - ])), - ), - ], - ); - - Widget _buildSummary() => Padding( - padding: const EdgeInsets.only(left: 5, bottom: 5, top: 5), - child: Container( - child: Text( - data.nameCN, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle( - color: Colors.grey[600], - fontSize: 14, - shadows: [const Shadow(color: Colors.white, offset: const Offset(.5, .5))]), - ), - ), - ); -} diff --git a/lib/views/components/project/items/gallery/gallery_card_item.dart b/lib/views/components/project/items/gallery/gallery_card_item.dart deleted file mode 100644 index 5ea88a04c..000000000 --- a/lib/views/components/project/items/gallery/gallery_card_item.dart +++ /dev/null @@ -1,125 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/utils/convert.dart'; -import 'package:flutter_unit/views/components/permanent/color_wrapper.dart'; -import 'package:flutter_unit/painter_system/gallery_factory.dart'; - -/// create by 张风捷特烈 on 2020/11/28 -/// contact me by email 1981462002@qq.com -/// 说明: - -class GalleryCardItem extends StatelessWidget { - final GalleryInfo galleryInfo; - final int count; - GalleryCardItem({Key key, this.galleryInfo,this.count=0}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(20))), - child: Container( - height: double.maxFinite, - alignment: Alignment.topCenter, - child: Column( - children: [ - Hero( - tag: galleryInfo.name, - child: Container( - height: 160, - decoration: BoxDecoration( - image: DecorationImage( - fit: BoxFit.cover, - image: AssetImage(galleryInfo.image)), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20), - topRight: Radius.circular(20), - )) - // color: Colors.green, - - ), - ), - Padding( - padding: const EdgeInsets.only( - top: 20, left: 15, right: 15, bottom: 10), - child: Row( - children: [ - Text( - galleryInfo.name, - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - shadows: [ - Shadow( - color: Theme.of(context).primaryColor, - offset: const Offset(.2, .2), - blurRadius: .5) - ]), - ), - Spacer(), - WrapColor( - radius: 6, - padding: - EdgeInsets.only(left: 6, right: 6, top: 2, bottom: 3), - child: Text( - "$count 幅", - style: TextStyle( - fontWeight: FontWeight.bold, - color: Colors.white, - fontSize: 12), - ), - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(left: 15, right: 15, top: 10), - child: Text( - galleryInfo.info, - style: TextStyle(color: Colors.grey, shadows: [ - Shadow( - color: Theme.of(context).primaryColor, - offset: const Offset(.2, .2), - blurRadius: .5) - ]), - ), - ) - ], - ), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.all( - Radius.circular(20), - ))), - ); - } -} - -class GalleryInfo { - final int count; - final String name; - final String info; - final String image; - final String router; - - GalleryType get type { - GalleryType galleryType = GalleryType.base; - Convert.galleryTypeMap.forEach((key, value) { - if (value == name) { - galleryType = key; - } - }); - return galleryType; - } - - const GalleryInfo( - {this.count = 0, this.name, this.info, this.image, this.router}); - - factory GalleryInfo.fromJson(Map map) { - return GalleryInfo( - count: map['count'] ?? 0, - name: map["name"] ?? "", - image: map["image"] ?? "assets/images/draw_bg4.webp", - router: map["router"] ?? "", - info: map["info"] ?? ""); - } -} diff --git a/lib/views/components/project/items/widget/coupon_widget_list_item.dart b/lib/views/components/project/items/widget/coupon_widget_list_item.dart deleted file mode 100644 index edf2a040f..000000000 --- a/lib/views/components/project/items/widget/coupon_widget_list_item.dart +++ /dev/null @@ -1,156 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_star/flutter_star.dart'; -import 'package:flutter_unit/app/res/cons.dart'; -import 'package:flutter_unit/app/res/style/shape/coupon_shape_border.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/circle_text.dart'; -import 'package:flutter_unit/views/components/permanent/tag.dart'; -import 'package:flutter_unit/model/widget_model.dart'; - -class CouponWidgetListItem extends StatelessWidget { - final WidgetModel data; - final bool hasTopHole; - final bool hasBottomHole; - final bool isClip; - - CouponWidgetListItem( - {this.data, - this.hasTopHole = true, - this.hasBottomHole = false, - this.isClip = true}); - - final List colors = Cons.tabColors; - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.only(bottom: 10,top:2,left: 10,right: 10), - - child: Stack( - children: [ - isClip - ? ClipPath( - clipper: ShapeBorderClipper( - shape: CouponShapeBorder( - hasTopHole: hasTopHole, - hasBottomHole: hasBottomHole, - hasLine: false, - edgeRadius: 25, - lineRate: 0.20)), - child: buildContent(), - ) - : buildContent(), - _buildCollectTag(Theme.of(context).primaryColor) - ], - ), - ); - } - - Widget buildContent() => Container( - color: Color(colors[data.family.index]).withAlpha(66), - height: 95, - padding: const EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 5), - child: Row( - children: [ - buildLeading(), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [_buildTitle(), _buildSummary()], - ), - ), - ], - ), - ); - - Widget buildLeading() => Padding( - padding: const EdgeInsets.all(5.0), - child: Hero( - tag: "hero_widget_image_${data.id}", - child: data.image == null - ? Material( - color: Colors.transparent, - child: CircleText( - text: data.name, - size: 60, - color: invColor, - ), - ) - : CircleImage( - image: data.image, - size: 60, - ), - ), - ); - - Color get invColor { - return Color(colors[data.family.index]); - } - - Widget _buildCollectTag(Color color) { - return Positioned( - top: 0, - right: 40, - child: BlocBuilder(builder: (_, s) { - bool show = s.widgets.contains(data); - return Opacity( - opacity: show ? 1.0 : 0.0, - child: SizedOverflowBox( - alignment: Alignment.bottomCenter, - size: const Size(0, 20 - 6.0), - child: Tag( - color: color, - shadowHeight: 6.0, - size: const Size(15, 20), - ), - ), - ); - })); - } - - Widget _buildTitle() { - return Expanded( - child: Row( - children: [ - const SizedBox(width: 10), - Expanded( - child: Text(data.name, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 17, - fontWeight: FontWeight.bold, - decoration: (data.deprecated??false)?TextDecoration.lineThrough:TextDecoration.none, - decorationThickness: 2, - shadows: [ - Shadow(color: Colors.white, offset: Offset(.3, .3)) - ])), - ), - StarScore( - star: Star(emptyColor: Colors.white, size: 15, fillColor: invColor), - score: data.lever, - ), - ], - ), - ); - } - - Widget _buildSummary() { - return Padding( - padding: const EdgeInsets.only(left: 10, bottom: 10, top: 5), - child: Container( - child: Text( - //尾部摘要 - data.info, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle(color: Colors.grey[600], fontSize: 14, shadows: [ - const Shadow(color: Colors.white, offset: const Offset(.5, .5)) - ]), - ), - ), - ); - } -} diff --git a/lib/views/components/project/items/widget/home_item_support.dart b/lib/views/components/project/items/widget/home_item_support.dart deleted file mode 100644 index 7f91e9a1c..000000000 --- a/lib/views/components/project/items/widget/home_item_support.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/material.dart'; -import 'package:flutter_unit/model/enums.dart'; -import 'package:flutter_unit/model/widget_model.dart'; -import 'package:flutter_unit/views/components/project/items/widget/simple_widget_list_item.dart'; - -import 'coupon_widget_list_item.dart'; -import 'techno_widget_list_item.dart'; - -/// create by 张风捷特烈 on 2020/4/28 -/// contact me by email 1981462002@qq.com -/// 说明: - -class HomeItemSupport { - static Widget get( - WidgetModel model, - int index, - ) { - switch (index) { - case 1: - return TechnoWidgetListItem(data: model); - case 2: - return SimpleWidgetListItem(data: model); - case 3: - return CouponWidgetListItem(data: model); - case 4: - return CouponWidgetListItem(hasTopHole: false, data: model); - case 5: - return CouponWidgetListItem( - hasTopHole: true, hasBottomHole: true, data: model); - case 6: - return CouponWidgetListItem(isClip: false, data: model); - } - return TechnoWidgetListItem(data: model); - } - - static List itemSimples() => [ - Container( - height: 10, - ), - TechnoWidgetListItem(data: getContainer()), - SimpleWidgetListItem(data: getContainer()), - CouponWidgetListItem(data: getContainer()), - CouponWidgetListItem(hasTopHole: false, data: getContainer()), - CouponWidgetListItem( - hasTopHole: true, hasBottomHole: true, data: getContainer()), - CouponWidgetListItem(isClip: false, data: getContainer()), - ]; - - static WidgetModel getContainer() => WidgetModel( - id: Random().nextInt(10000), - name: 'Container', - nameCN: "", - lever: 5, - family: WidgetFamily.statelessWidget, - info: '用于容纳单个子组件的容器组件。集成了若干个单子组件的功能,如内外边距、形变、装饰、约束等...'); -} diff --git a/lib/views/components/project/items/widget/simple_widget_list_item.dart b/lib/views/components/project/items/widget/simple_widget_list_item.dart deleted file mode 100644 index 5afc4999b..000000000 --- a/lib/views/components/project/items/widget/simple_widget_list_item.dart +++ /dev/null @@ -1,148 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_star/star.dart'; -import 'package:flutter_star/star_score.dart'; -import 'package:flutter_unit/app/res/cons.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/circle_text.dart'; -import 'package:flutter_unit/views/components/permanent/tag.dart'; -import 'package:flutter_unit/model/widget_model.dart'; - -/// create by 张风捷特烈 on 2020/11/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -class SimpleWidgetListItem extends StatelessWidget { - final WidgetModel data; - - SimpleWidgetListItem({ - this.data, - }); - - @override - Widget build(BuildContext context) { - return Container( - - decoration: BoxDecoration( - color: itemColor.withAlpha(66), - borderRadius: BorderRadius.circular(6) - ), - margin: const EdgeInsets.only(bottom: 10,top:2,left: 10,right: 10), - child: Stack( - children: [ - Container( - height: 95, - padding: - const EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 5), - child: Row( - children: [ - Wrap( - spacing: 5, - direction: Axis.vertical, - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Hero( - tag: "hero_widget_image_${data.id}", - child: _buildLeading()), - StarScore( - star: Star( - emptyColor: Colors.white, - size: 12, - fillColor: itemColor), - score: data.lever, - ) - ], - ), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.start, - children: [_buildTitle(), _buildSummary()], - ), - ), - ], - ), - ), - _buildCollectTag(Theme.of(context).primaryColor) - ], - ), - ); - } - - Widget _buildCollectTag(Color color) { - return Positioned( - top: 0, - right: 40, - child: BlocBuilder(builder: (_, s) { - bool show = s.widgets.contains(data); - return Opacity( - opacity: show ? 1.0 : 0.0, - child: SizedOverflowBox( - alignment: Alignment.bottomCenter, - size: const Size(0, 30 - 8.0), - child: Tag( - color: color, - shadowHeight: 8.0, - size: const Size(20, 30), - ), - ), - ); - })); - } - - Widget _buildLeading() => Padding( - padding: const EdgeInsets.only(left: 5, right: 5), - child: data.image == null - ? Material( - color: Colors.transparent, - child: CircleText( - text: data.name, - size: 60, - color: itemColor, - ), - ) - : CircleImage( - image: data.image, - size: 55, - ), - ); - - Color get itemColor => Color(Cons.tabColors[data.family.index]); - - Widget _buildTitle() { - return Row( - children: [ - const SizedBox(width: 10), - Expanded( - child: Text(data.name, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 17, - fontWeight: FontWeight.bold, - decoration: (data.deprecated??false)?TextDecoration.lineThrough:TextDecoration.none, - decorationThickness: 2, - shadows: [ - Shadow(color: Colors.white, offset: Offset(.3, .3)) - ])), - ), - ], - ); - } - - Widget _buildSummary() { - return Container( - padding: const EdgeInsets.only(left: 10), - child: Text( - //尾部摘要 - data.info, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle(color: Colors.grey[600], fontSize: 14, shadows: [ - const Shadow(color: Colors.white, offset: const Offset(.5, .5)) - ]), - ), - ); - } -} diff --git a/lib/views/components/project/items/widget/techno_widget_list_item.dart b/lib/views/components/project/items/widget/techno_widget_list_item.dart deleted file mode 100644 index 1c132904d..000000000 --- a/lib/views/components/project/items/widget/techno_widget_list_item.dart +++ /dev/null @@ -1,142 +0,0 @@ -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:flutter_unit/app/res/cons.dart'; -import 'package:flutter_unit/app/res/style/shape/techno_shape.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/circle_text.dart'; -import 'package:flutter_unit/views/components/permanent/tag.dart'; -import 'package:flutter_unit/model/widget_model.dart'; - -class TechnoWidgetListItem extends StatelessWidget { - final WidgetModel data; - - TechnoWidgetListItem({this.data}); - - @override - Widget build(BuildContext context) { - return Container( - margin: const EdgeInsets.only(bottom: 10,top:2,left: 10,right: 10), - child: Stack( - children: [ - Material( - color: itemColor.withAlpha(66), - shape: TechnoShapeBorder(color: itemColor), - child: Container( - height: 95, - padding: const EdgeInsets.only( - top: 10, left: 10, right: 10, bottom: 5), - child: Row( - children: [ - Wrap( - spacing: 5, - direction: Axis.vertical, - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Hero( - tag: "hero_widget_image_${data.id}", - child: _buildLeading()), - StarScore( - star: Star( - emptyColor: Colors.white, - size: 12, - fillColor: itemColor), - score: data.lever, - ) - ], - ), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - crossAxisAlignment: CrossAxisAlignment.start, - children: [_buildTitle(), _buildSummary()], - ), - ), - ], - ), - ), - ), - _buildCollectTag(Theme.of(context).primaryColor) - ], - ), - ); - } - - Widget _buildCollectTag(Color color) { - return Positioned( - top: 0, - right: 40, - child: BlocBuilder(builder: (_, s) { - bool show = s.widgets.contains(data); - return Opacity( - opacity: show ? 1.0 : 0.0, - child: SizedOverflowBox( - alignment: Alignment.bottomCenter, - size: const Size(0, 30 - 8.0), - child: Tag( - color: color, - shadowHeight: 8.0, - size: const Size(20, 30), - ), - ), - ); - })); - } - - Widget _buildLeading() => Padding( - padding: const EdgeInsets.only(left: 5, right: 5), - child: data.image == null - ? Material( - color: Colors.transparent, - child: CircleText( - text: data.name, - size: 60, - color: itemColor, - ), - ) - : CircleImage( - image: data.image, - size: 55, - ), - ); - - Color get itemColor => Color(Cons.tabColors[data.family.index]); - - Widget _buildTitle() { - return Row( - children: [ - const SizedBox(width: 10), - Expanded( - child: Text(data.name, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 17, - fontWeight: FontWeight.bold, - decoration: (data.deprecated??false)?TextDecoration.lineThrough:TextDecoration.none, - decorationThickness: 2, - shadows: [ - Shadow(color: Colors.white, offset: Offset(.3, .3)) - ])), - ), - ], - ); - } - - Widget _buildSummary() { - return Container( - padding: const EdgeInsets.only(left: 10), - child: Text( - //尾部摘要 - data.info, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle(color: Colors.grey[600], fontSize: 14, shadows: [ - const Shadow(color: Colors.white, offset: const Offset(.5, .5)) - ]), - ), - ); - } -} diff --git a/lib/views/components/project/nav/unit_bottom_bar.dart b/lib/views/components/project/nav/unit_bottom_bar.dart deleted file mode 100644 index 6c5d615e2..000000000 --- a/lib/views/components/project/nav/unit_bottom_bar.dart +++ /dev/null @@ -1,177 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/res/size_unit.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; - -/// create by 张风捷特烈 on 2020-04-11 -/// contact me by email 1981462002@qq.com -/// 说明: 自定义底部导航栏 - -typedef IndexTapCallback = void Function(int); -typedef IndexLongTapCallback = void Function(BuildContext, int); - -class UnitBottomBar extends StatefulWidget { - final Color color; - - // item 点击事件 - final IndexTapCallback onItemTap; - - // item 长按事件 - final IndexLongTapCallback onItemLongTap; - - UnitBottomBar({ - this.color = Colors.blue, - @required this.onItemTap, - this.onItemLongTap, - }); - - @override - _UnitBottomBarState createState() => _UnitBottomBarState(); -} - -class _UnitBottomBarState extends State { - int _position = 0; - - final NotchedShape shape = const CircularNotchedRectangle(); - - final borderTR = const BorderRadius.only( - topRight: Radius.circular(10), - ); - - final borderTL = const BorderRadius.only(topLeft: Radius.circular(10)); - final paddingTR = const EdgeInsets.only(top: 2, right: 2); - final paddingTL = const EdgeInsets.only(top: 2, left: 2); - final paddingL = const EdgeInsets.only(left: 8); - final paddingR = const EdgeInsets.only(right: 8); - - @override - Widget build(BuildContext context) { - return BottomAppBar( - elevation: 0, - shape: shape, - notchMargin: 5, - color: widget.color, - child: IconTheme( - data: IconTheme.of(context).copyWith(color: Colors.white, size: 25), - child: Stack( - children: [ - _buildLeftBtn(context), - Positioned( - right: 0, - child: _buildRightBtn(context), - ), - ], - ), - )); - } - - Widget _buildLeftBtn(BuildContext context) => Material( - shape: RoundedRectangleBorder(borderRadius: borderTR), - child: Container( - margin: paddingTR, - alignment: Alignment.center, - decoration: BoxDecoration( - color: widget.color.withAlpha(88), borderRadius: borderTR), - height: SizeUnit.bottom_nav_height, - width: SizeUnit.bottom_nav_right_width, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: FeedbackWidget( - onPressed: () => _updateIndex(0), - onLongPressed: () => _onLongPress(context, 0), - child: Container( - padding: paddingL, - height: SizeUnit.bottom_nav_height, - child: Icon( - TolyIcon.icon_layout, - size: getIconSizeByPosition(0), - color: getIconColorByPosition(0), - )), - ), - ), - Expanded( - child: FeedbackWidget( - onPressed: () => _updateIndex(1), - child: Container( - height: SizeUnit.bottom_nav_height, - padding: paddingR, - child: Icon( - TolyIcon.dingzhi1, - size: getIconSizeByPosition(1), - color: getIconColorByPosition(1), - )), - ), - ), - ], - ), - ), - ); - - Widget _buildRightBtn(BuildContext context) => Material( - shape: RoundedRectangleBorder(borderRadius: borderTL), - child: Container( - margin: paddingTL, - alignment: Alignment.center, - decoration: BoxDecoration( - color: widget.color.withAlpha(88), borderRadius: borderTL), - height: SizeUnit.bottom_nav_height, - width: SizeUnit.bottom_nav_right_width, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: FeedbackWidget( - onPressed: () => _updateIndex(2), - child: Container( - padding: paddingL, - height: SizeUnit.bottom_nav_height, - child: Icon( - TolyIcon.icon_collect, - size: getIconSizeByPosition(2), - color: getIconColorByPosition(2), - )), - ), - ), - Expanded( - child: FeedbackWidget( - onPressed: () => _updateIndex(3), - onLongPressed: () => _onLongPress(context, 3), - child: Container( - padding: paddingR, - height: SizeUnit.bottom_nav_height, - child: Icon( - TolyIcon.yonghu, - size: getIconSizeByPosition(3), - color: getIconColorByPosition(3), - )), - ), - ), - ], - ), - ), - ); - - double getIconSizeByPosition(int position) => _position == position - ? SizeUnit.active_bottom_nav_icon - : SizeUnit.default_bottom_nav_icon; - - Color getIconColorByPosition(int position) => - _position == position ? widget.color : Colors.white; - - void _updateIndex(int index) { - setState(() { - _position = index; - if (widget.onItemTap != null) { - widget.onItemTap(_position); - } - }); - } - - void _onLongPress(BuildContext context, int index) { - if (widget.onItemLongTap != null) { - widget.onItemLongTap(context, index); - } - } -} diff --git a/lib/views/components/project/no_more_widget.dart b/lib/views/components/project/no_more_widget.dart deleted file mode 100644 index 643522df2..000000000 --- a/lib/views/components/project/no_more_widget.dart +++ /dev/null @@ -1,17 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/11/25 -/// contact me by email 1981462002@qq.com -/// 说明: - -class NoMoreWidget extends StatelessWidget { - - const NoMoreWidget(); - - @override - Widget build(BuildContext context) { - return Container(height: 56); - } - - -} diff --git a/lib/views/components/project/unit_drawer_header.dart b/lib/views/components/project/unit_drawer_header.dart deleted file mode 100644 index 9ac12943c..000000000 --- a/lib/views/components/project/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; - - - UnitDrawerHeader({this.color}); - - @override - Widget build(BuildContext context) { - return DrawerHeader( - padding: EdgeInsets.only(top: 10, left: 15), - decoration: 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: [ - FlutterLogo( - size: 35, - ), - Text( - 'Flutter Unit', - style: TextStyle(fontSize: 24, color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(1, 1), - blurRadius: 3) - ]), - ), - ], - ), - SizedBox( - height: 15, - ), - Text( - 'The Unity Of Flutter, The Unity Of Coder.', - style: TextStyle(fontSize: 15, color: Colors.white, shadows: [ - Shadow(color: color, offset: Offset(.5, .5), blurRadius: 1) - ]), - ), - SizedBox( - height: 5, - ), - Text( - 'Flutter的联合,编程者的联合。', - style: TextStyle(fontSize: 15, color: Colors.white, shadows: [ - Shadow(color: color, offset: Offset(.5, .5), blurRadius: 1) - ]), - ), - SizedBox( - height: 10, - ), - 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/views/components/project/widget_node_panel.dart b/lib/views/components/project/widget_node_panel.dart deleted file mode 100644 index 6ca496418..000000000 --- a/lib/views/components/project/widget_node_panel.dart +++ /dev/null @@ -1,154 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:flutter_unit/app/res/toly_icon.dart'; - -import 'package:flutter_unit/views/components/permanent/circle.dart'; -import 'package:flutter_unit/views/components/permanent/code/code_widget.dart'; -import 'package:flutter_unit/views/components/permanent/panel.dart'; -import 'package:share/share.dart'; -import 'package:toggle_rotate/toggle_rotate.dart'; - -import '../permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/permanent/code/highlighter_style.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; - - WidgetNodePanel( - {this.text, - this.subText, - this.code, - this.show, - this.codeStyle, - this.codeFamily}); - - @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: EdgeInsets.all(10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - buildNodeTitle(), - SizedBox( - height: 20, - ), - _buildCode(context), - Padding( - padding: const EdgeInsets.only(top: 10, bottom: 20), - child: widget.show, - ), - _buildNodeInfo(), - Divider(), - ], - ), - ); - } - - Widget buildNodeTitle() => Row( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Circle( - color: themeColor, - radius: 5, - ), - ), - Expanded( - child: Text( - '${widget.text}', - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), - ), - ), - _buildShareButton(), - _buildCodeButton() - ], - ); - - Widget _buildNodeInfo() => Container( - width: double.infinity, - child: Panel( - child: Text( - '${widget.subText}', - style: TextStyle(fontSize: 14), - )), - ); - - 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: Container(), - secondChild: Container( - width: MediaQuery.of(context).size.width, - child: CodeWidget( - fontFamily: widget.codeFamily, - code: isFirst?'':widget.code, - style: widget.codeStyle ?? - HighlighterStyle.fromColors(HighlighterStyle.lightColor), - ), - ), - duration: Duration(milliseconds: 200), - crossFadeState: _crossFadeState, - ); - - //执行分享 - _doShare() { - Share.share(widget.code); - } - - // 折叠代码面板 - _toggleCodePanel() { - setState(() { - _crossFadeState = - !isFirst ? CrossFadeState.showFirst : CrossFadeState.showSecond; - }); - } -} diff --git a/lib/views/pages/about/about_app_page.dart b/lib/views/pages/about/about_app_page.dart deleted file mode 100644 index 6918ee503..000000000 --- a/lib/views/pages/about/about_app_page.dart +++ /dev/null @@ -1,227 +0,0 @@ -import 'package:flutter/cupertino.dart'; -/// create by 张风捷特烈 on 2020-04-13 -/// contact me by email 1981462002@qq.com -/// 说明: - -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/views/components/permanent/circle.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/permanent/panel.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class AboutAppPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.white, - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - child: Stack( - children: [ - Column( - children: [ - Container( - height: 150, - width: MediaQuery.of(context).size.width, - margin: EdgeInsets.only(bottom: 50), - child: Image.asset( - 'assets/images/sabar.webp', - fit: BoxFit.cover, - ), - ), - ], - ), - _buildBar(context), - Positioned( - bottom: 0, - left: 50, - child: CircleImage( - size: 100, - shadowColor: Theme.of(context).primaryColor, - image: AssetImage('assets/images/icon_head.webp'), - )), - ], - ), - ), - Expanded( - child: SingleChildScrollView( - child: Container( - margin: EdgeInsets.all(24), - child: _buildInfo(), - ), - ), - ), - ], - ), - ); - } - - Widget _buildBar(BuildContext context) { - return Container( - height: kToolbarHeight, - margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top), - child: Row( - children: [ - GestureDetector( - onTap: () => Navigator.of(context).pop(), - child: Container( - padding: EdgeInsets.only(left: 10), - child: Icon( - Icons.arrow_back, - size: 30, - color: Theme.of(context).primaryColor, - ), - ), - ), - Spacer(), - FeedbackWidget( - onPressed: () => - _launchURL("mailto:1981462002@qq.com?subject=来自Flutter Unit"), - child: Icon( - TolyIcon.icon_email, - size: 20, - color: Theme.of(context).primaryColor, - ), - ), - SizedBox( - width: 20, - ) - ], - ), - ); - } - - _launchURL(String url) async { - if (await canLaunch(url)) { - await launch(url); - } else { - - } - } - - Widget _buildInfo() { - return Stack( - children: [ - Positioned( - right: 10, - top: 0, - child: Wrap( - spacing: 20, - children: [ - FeedbackWidget( - onPressed: () => - _launchURL("https://github.com/toly1994328/FlutterUnit"), - child: Wrap( - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Icon( - TolyIcon.icon_github, - size: 35, - ), - Text('Github') - ], - )), - ], - ), - ), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Flutter Unit', - style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold), - ), - SizedBox(height: 20), - Text( - 'The Unity Of Flutter, The Unity Of Coder.', - style: TextStyle(fontSize: 16), - ), - SizedBox(height: 10), - Text( - 'Flutter的联合,编程者的联合。', - style: TextStyle(fontSize: 16), - ), - Divider( - height: 20, - ), - InfoPanel( - title: '项目简介', - info: ' Flutter Unit 是一个非盈利性的开源项目,' - '旨在提供全面的Flutter学习指南及编程者的交流技术的接口。' - '由【张风捷特烈】提供技术支持和全权维护。唯一开源网站网址: ' - '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: 'Flutter Unit 2.0 尚在计划之中,如果说1.0是本王单枪匹马,' - '那2.0将是Flutter爱好者的共同努力。后面陆续会发布一些征集方案,' - '包括属性、布局、绘制、bug、要点集录等。吾想让Unit 成为一个Flutter的圣地,纯粹而强大,期待与你的共同携手。', - ) - ], - ), - ], - ); - } -} - -class InfoPanel extends StatelessWidget { - final String title; - final String info; - - - InfoPanel({this.title, this.info}); - - @override - Widget build(BuildContext context) { - return 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('$title',style: TextStyle(fontSize: 16,fontWeight: FontWeight.bold),), - ) - ], - ), - Panel( - color: Theme.of(context).primaryColor.withAlpha(33), - child: Text( - '$info', - style: TextStyle(color: Colors.grey, - fontSize: 13, - shadows: [ - Shadow( - color: Colors.white, - offset: Offset(1,1) - ) - ]), - ), - ), - ], - ); - } -} diff --git a/lib/views/pages/about/about_me_page.dart b/lib/views/pages/about/about_me_page.dart deleted file mode 100644 index 1b0667641..000000000 --- a/lib/views/pages/about/about_me_page.dart +++ /dev/null @@ -1,206 +0,0 @@ -/// create by 张风捷特烈 on 2020-04-13 -/// contact me by email 1981462002@qq.com -/// 说明: - -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:url_launcher/url_launcher.dart'; - -class AboutMePage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.white, - body: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Stack( - children: [ - Column( - children: [ - Container( - height: 180, - width: MediaQuery.of(context).size.width, - margin: EdgeInsets.only(bottom: 50), - child: Image.asset( - 'assets/images/sabar.webp', - fit: BoxFit.cover, - ), - ), - ], - ), - _buildBar(context), - Positioned( - bottom: 0, - left: 50, - child: CircleImage( - size: 100, - shadowColor: Theme.of(context).primaryColor, - image: AssetImage('assets/images/icon_head.webp'), - )), - ], - ), - Expanded( - child: SingleChildScrollView( - child: Container( - margin: EdgeInsets.all(24), - child: Stack(children: [ - Positioned( - right: 10, - top: 0, - child: _buildLinkIcon(), - ), - _buildInfo() - ]), - ), - ), - ), - ], - ), - ); - } - - Widget _buildBar(BuildContext context) { - return Container( - height: kToolbarHeight, - margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top), - child: Row( - children: [ - GestureDetector( - onTap: () => Navigator.of(context).pop(), - child: Container( - padding: EdgeInsets.only(left: 10), - child: Icon( - Icons.arrow_back, - size: 30, - color: Theme.of(context).primaryColor, - ), - ), - ), - Spacer(), - FeedbackWidget( - onPressed: () => - _launchURL("mailto:1981462002@qq.com?subject=来自Flutter Unit"), - child: Icon( - TolyIcon.icon_email, - size: 20, - color: Theme.of(context).primaryColor, - ), - ), - SizedBox( - width: 20, - ) - ], - ), - ); - } - - _launchURL(String url) async { - if (await canLaunch(url)) { - await launch(url); - } else { - debugPrint('Could not launch $url'); - } - } - - Widget _buildInfo() { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - '张风捷特烈', - style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold), - ), - SizedBox(height: 20), - Text( - 'The King Of Coder. 「编程之王」', - style: TextStyle(fontSize: 16), - ), - SizedBox(height: 10), - Text( - '海的彼岸有我未曾见证的风采。', - style: TextStyle(fontSize: 16), - ), - Divider( - height: 18, - ), - - Text( - '微信群: 编程技术交流圣地-【Flutter群】\n' - '愿青梅煮酒,与君天涯共话。', - style: TextStyle(color: Colors.grey), - ), - SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Container( - width: 190, - child: Column( - children: [ - Image.asset('assets/images/wechat.webp'), - Text( - '我的微信', - style: TextStyle(fontSize: 16), - ), - ], - ), - ), -// Container( -// width: 160, -// child: Column( -// children: [ -// Image.asset('assets/images/wei_x.jpg'), -// Text( -// '请我喝茶(慎扫)', -// style: TextStyle(fontSize: 16), -// ), -// ], -// ), -// ), - ], - ), - ], - ); - } - - Wrap _buildLinkIcon() { - return Wrap( - spacing: 20, - children: [ - FeedbackWidget( - onPressed: () => - _launchURL("https://juejin.im/user/5b42c0656fb9a04fe727eb37"), - child: Wrap( - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Icon( - TolyIcon.icon_juejin, - size: 35, - color: Colors.blue, - ), - Text('掘金') - ], - )), - FeedbackWidget( - onPressed: () => - _launchURL("https://github.com/toly1994328"), - child: Wrap( - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Icon( - TolyIcon.icon_github, - size: 35, - ), - SizedBox(height: 4,), - Text('Github') - ], - )), - ], - ); - } -} diff --git a/lib/views/pages/about/version/app_version_checker.dart b/lib/views/pages/about/version/app_version_checker.dart deleted file mode 100644 index fb6226013..000000000 --- a/lib/views/pages/about/version/app_version_checker.dart +++ /dev/null @@ -1,166 +0,0 @@ -import 'dart:io'; - -import 'package:dio/dio.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/api/app_info.dart'; -import 'package:flutter_unit/app/utils/Toast.dart'; -import 'package:flutter_unit/app/utils/convert.dart'; -import 'package:flutter_unit/app/utils/http_utils/http_util.dart'; -import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; -import 'package:install_plugin/install_plugin.dart'; -import 'package:package_info/package_info.dart'; -import 'package:path/path.dart' as path; -import 'package:path_provider/path_provider.dart'; - -class AppVersionChecker extends StatefulWidget { - const AppVersionChecker({Key key}) : super(key: key); - - @override - _AppVersionCheckerState createState() => _AppVersionCheckerState(); -} - -enum VersionState { none, loading, shouldUpdate, downloading } - -class _AppVersionCheckerState extends State { - final TextStyle labelStyle = TextStyle(fontSize: 13); - String oldVersion = ''; - String newVersion = ''; - int totalSize =0; - String url = 'http://toly1994.com/file/FlutterUnit.apk'; - ValueNotifier versionState = - ValueNotifier(VersionState.none); - - ValueNotifier progress = ValueNotifier(0); - - _doDownload() async { - Directory dir = await getExternalStorageDirectory(); - String dstPath = path.join(dir.path, 'FlutterUnit.apk'); - - if(File(dstPath).existsSync()){ - InstallPlugin.installApk(dstPath, 'com.toly1994.flutter_unit'); - return; - } - - versionState.value = VersionState.downloading; - - await HttpUtil.getInstance().client.download(url, dstPath, - onReceiveProgress: _onReceiveProgress, - options: Options(receiveTimeout: 24 * 60 * 60 * 1000)); - versionState.value = VersionState.none; - InstallPlugin.installApk(dstPath, 'com.toly1994.flutter_unit'); - } - - void _onReceiveProgress(int count, int total) { - totalSize = total; - progress.value = count / total; - } - - @override - Widget build(BuildContext context) { - return ListTile( - title: Text('检查新版本', style: labelStyle), - trailing: ValueListenableBuilder( - valueListenable: versionState, - builder: _buildTrailByState, - ), - onTap: () async { - if (versionState.value == VersionState.shouldUpdate && - Platform.isAndroid) { - _doDownload(); - return; - } - - if (versionState.value == VersionState.downloading) { - return; - } - - versionState.value = VersionState.loading; - ResultBean result = await AppInfoApi.getAppVersion(); - PackageInfo packageInfo = await PackageInfo.fromPlatform(); - - if (result.status) { - print('${result.data.appName}:${result.data.appVersion}'); - if (packageInfo.version == result.data.appVersion) { - Toast.success(context, '当前应用已是最新版本!'); - versionState.value = VersionState.none; - } else { - oldVersion = packageInfo.version; - newVersion = result.data.appVersion; - Toast.green(context, '检测到新版本【${result.data.appVersion}】,可点击更新!'); - versionState.value = VersionState.shouldUpdate; - } - } else { - print('${result.msg}'); - versionState.value = VersionState.none; - } - }, - ); - } - - Widget _buildTrailByState( - BuildContext context, VersionState value, Widget child) { - switch (value) { - case VersionState.none: - return const SizedBox(); - case VersionState.loading: - return const CupertinoActivityIndicator(); - case VersionState.shouldUpdate: - return Wrap( - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Text( - '$oldVersion --> $newVersion ', - style: TextStyle(height: 1, fontSize: 12, color: Colors.grey), - ), - const SizedBox( - width: 5, - ), - const Icon( - Icons.update, - color: Colors.green, - ) - ]); - case VersionState.downloading: - return ValueListenableBuilder( - valueListenable: progress, builder: _buildProgress); - } - return const SizedBox(); - } - - Widget _buildProgress(BuildContext context, double value, Widget child) { - return Wrap( - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Column( - children: [ - Text( - '${(value * 100).toStringAsFixed(2)} %', - style: TextStyle(height: 1, fontSize: 12, color: Colors.grey), - ), - const SizedBox( - height: 5, - ), - Text( - '${Convert.convertFileSize((totalSize * value).floor())}/${Convert.convertFileSize(totalSize)}', - style: 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: value, - ), - ) - ]); - } -} diff --git a/lib/views/pages/about/version/version_shower.dart b/lib/views/pages/about/version/version_shower.dart deleted file mode 100644 index 2a81a6d03..000000000 --- a/lib/views/pages/about/version/version_shower.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:package_info/package_info.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/views/pages/about/version_info.dart b/lib/views/pages/about/version_info.dart deleted file mode 100644 index 552a6cea7..000000000 --- a/lib/views/pages/about/version_info.dart +++ /dev/null @@ -1,126 +0,0 @@ - -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/api/app_info.dart'; -import 'package:flutter_unit/app/res/str_unit.dart'; -import 'package:flutter_unit/app/res/style/behavior/no_scroll_behavior.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import 'version/app_version_checker.dart'; -import 'version/version_shower.dart'; - -/// create by 张风捷特烈 on 2020/6/16 -/// contact me by email 1981462002@qq.com -/// 说明: - -class VersionInfo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.white, - appBar: AppBar( - backgroundColor: Colors.white, - elevation: 0, - iconTheme: IconThemeData( - color: Colors.grey - ), - ), - body:ConstrainedBox( - constraints: BoxConstraints.expand(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.only(top:28.0), - child: _buildTop(), - ), - - _buildCenter(context), - Spacer(), - Padding( - padding: const EdgeInsets.only(bottom:8.0), - child: buildBottom(), - ) - - ], - ), - ), - ); - } - - Widget _buildTop() { - return Wrap( - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - spacing: 10, - children: [ - CircleImage(image: AssetImage("assets/images/icon_head.webp"),size: 80,), - Text('Flutter Unit',style: TextStyle(fontSize: 20,fontWeight: FontWeight.bold),), - const VersionShower(), - ], - ); - } - - Widget _buildCenter(BuildContext context) { - final labelStyle= TextStyle(fontSize: 13); - return Padding( - padding: const EdgeInsets.only(left:20.0,right: 20,top: 20), - child: ScrollConfiguration( - behavior: NoScrollBehavior(), - child: ListView( - shrinkWrap: true, - children: [ - Divider(height: 1,), - ListTile( - title: Text('应用详情',style: labelStyle,), - trailing: _nextIcon(context), - onTap: () => Navigator.of(context).pushNamed(UnitRouter.about_app), - ), - Divider(height: 1,indent: 10), - const AppVersionChecker(), - Divider(height: 1,indent: 10), - ListTile( - title: Text('检查数据库新版本',style: labelStyle), - trailing: _nextIcon(context), - onTap: () async{ - - }, - ), - Divider(height: 1,), - ], - ), - ), - ); - } - - Widget _nextIcon(BuildContext context) => - Icon(Icons.chevron_right, color: Colors.grey); - - Widget buildBottom() { - return Wrap( - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - spacing: 4, - children: [ - FeedbackWidget( - onPressed: (){ - _launchURL("https://github.com/toly1994328/FlutterUnit"); - }, - child: Text('《查看本项目Github仓库》',style: TextStyle(fontSize: 12,color: Color(0xff616C84),),)), - Text('Power By 张风捷特烈',style: TextStyle(fontSize: 12,color: Colors.grey),), - Text('Copyright © 2008-2020 Toly1994',style: TextStyle(fontSize: 12,color: Colors.grey),), - ], - ); - } - - _launchURL(String url) async { - if (await canLaunch(url)) { - await launch(url); - } else { - - } - } -} diff --git a/lib/views/pages/app/bloc_wrapper.dart b/lib/views/pages/app/bloc_wrapper.dart deleted file mode 100644 index 0db8e8d53..000000000 --- a/lib/views/pages/app/bloc_wrapper.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/repositories/app_start.dart'; -import 'package:flutter_unit/repositories/local_db.dart'; -import 'package:flutter_unit/repositories/rep/impl/catagory_db_repository.dart'; -import 'package:flutter_unit/repositories/rep/impl/widget_db_repository.dart'; -import 'package:flutter_unit/repositories/rep/widget_repository.dart'; -import 'package:flutter_unit/user_system/bloc/authentic/bloc.dart'; -import 'package:flutter_unit/user_system/bloc/authentic/event.dart'; -import 'package:flutter_unit/user_system/bloc/login/bloc.dart'; -import 'package:flutter_unit/user_system/bloc/register/bloc.dart'; -import 'package:flutter_unit/model/enums.dart'; -import 'package:flutter_unit/blocs/bloc_exp.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; - - BlocWrapper({this.child}); - - @override - _BlocWrapperState createState() => _BlocWrapperState(); -} - -class _BlocWrapperState extends State { - final WidgetRepository repository = WidgetDbRepository(); - - final categoryBloc = CategoryBloc(repository: CategoryDbRepository()); - final authBloc = AuthenticBloc()..add(const AppStarted()); - - @override - Widget build(BuildContext context) { - return MultiBlocProvider( - //使用MultiBlocProvider包裹 - providers: [ - //Bloc提供器 - BlocProvider(create: (_) => GlobalBloc(storage)..add(const EventInitApp())), - - BlocProvider(create: (_) => WidgetsBloc(repository: repository)), - - BlocProvider( - create: (_) => DetailBloc(repository: repository)), - - BlocProvider( - create: (_) => categoryBloc), - - BlocProvider( - create: (_) => LikeWidgetBloc(repository: repository)), - - BlocProvider(create: (_) => RegisterBloc()), - - BlocProvider( - create: (_) => LoginBloc(authenticBloc: authBloc)), - - BlocProvider(create: (_) => authBloc), - - BlocProvider( - create: (_) => CategoryWidgetBloc(categoryBloc: categoryBloc)), - - BlocProvider( - create: (_) => SearchBloc(repository: repository)), - - BlocProvider(create: (_) => PointBloc()), - - BlocProvider(create: (_) => PointCommentBloc()), - ], child: widget.child); - } - - @override - void dispose() { - categoryBloc.close(); - authBloc.close(); - LocalDb.instance.closeDb(); - super.dispose(); - } -} diff --git a/lib/views/pages/app/flutter_unit.dart b/lib/views/pages/app/flutter_unit.dart deleted file mode 100644 index 6d6b4943b..000000000 --- a/lib/views/pages/app/flutter_unit.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/res/str_unit.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/pages/splash/unit_splash.dart'; - - -/// create by 张风捷特烈 on 2020/4/28 -/// contact me by email 1981462002@qq.com -/// 说明: 主程序 - -class FlutterUnit extends StatelessWidget { - @override - Widget build(BuildContext context) { - return BlocBuilder(builder: (_, state) { - return MaterialApp( - showPerformanceOverlay: state.showPerformanceOverlay, - title: StrUnit.appName, - debugShowCheckedModeBanner: false, - onGenerateRoute: UnitRouter.generateRoute, - theme: ThemeData( - primarySwatch: state.themeColor, - fontFamily: state.fontFamily, - ), - home: UnitSplash(), - ); - }); - } - -} diff --git a/lib/views/pages/app/unit_navigation.dart b/lib/views/pages/app/unit_navigation.dart deleted file mode 100644 index 2bbcaf8db..000000000 --- a/lib/views/pages/app/unit_navigation.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/painter_system/gallery_unit.dart'; -import 'package:flutter_unit/views/components/project/nav/unit_bottom_bar.dart'; -import 'package:flutter_unit/views/components/project/overlay_tool_wrapper.dart'; -import 'package:flutter_unit/views/pages/category/collect_page.dart'; -import 'package:flutter_unit/views/pages/category/home_right_drawer.dart'; -import 'package:flutter_unit/views/pages/user/user_page.dart'; -import 'package:flutter_unit/views/pages/widget_home/home_drawer.dart'; -import 'package:flutter_unit/views/pages/widget_home/home_page.dart'; - -/// create by 张风捷特烈 on 2020-04-11 -/// contact me by email 1981462002@qq.com -/// 说明: 主题结构 左右滑页 + 底部导航栏 - - -class UnitNavigation extends StatefulWidget { - @override - _UnitNavigationState createState() => _UnitNavigationState(); -} - -class _UnitNavigationState extends State { - //页面控制器,初始 0 - final PageController _controller = PageController(); - - // 禁止 PageView 滑动 - final ScrollPhysics neverScroll = const NeverScrollableScrollPhysics(); - - @override - void dispose() { - _controller.dispose(); //释放控制器 - super.dispose(); - } - - /// extendBody = true 凹嵌透明,需要处理底部 边距 - @override - Widget build(BuildContext context) { - return Scaffold( - extendBody: true, - drawer: HomeDrawer(), - endDrawer: HomeRightDrawer(), - floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, - floatingActionButton: _buildSearchButton(context), - body: wrapOverlayTool( - child: PageView( - physics: neverScroll, - controller: _controller, - children: [ - HomePage(), - GalleryUnit(), - CollectPage(), - UserPage(), - ], - ), - ), - bottomNavigationBar: _buildBottomNav(context), - ); - } - - // 构建悬浮按钮工具 - Widget wrapOverlayTool({Widget child}) => Builder( - builder: (ctx) => OverlayToolWrapper( - child: child, - )); - - // 主页搜索按钮 - // 由于 按钮 颜色需要随 点击头部栏 状态而改变, - // 使用 BlocBuilder 构建 - Widget _buildSearchButton(BuildContext context) => - BlocBuilder( - builder: (_, state) => FloatingActionButton( - elevation: 2, - backgroundColor: state.color, - child: const Icon(Icons.search), - onPressed: () => - Navigator.of(context).pushNamed(UnitRouter.search), - )); - - // 由于 bottomNavigationBar 颜色需要随 点击头部栏 状态而改变, - // 使用 BlocBuilder 构建 - Widget _buildBottomNav(BuildContext context) => - BlocBuilder( - builder: (_, state) => UnitBottomBar( - color: state.color, - onItemTap: _onTapBottomNav, - onItemLongTap: _onItemLongTap, - - )); - - // 点击底部按钮事件,切换页面 - _onTapBottomNav(int index) { - _controller.animateToPage(index, duration: const Duration(milliseconds: 200), curve: Curves.linear); - if (index == 2) { - BlocProvider.of(context).add(EventLoadLikeData()); - } - } - - // 两侧 - void _onItemLongTap(BuildContext context , int index) { - if (index == 0) { - Scaffold.of(context).openDrawer(); - } - if (index == 3) { - Scaffold.of(context).openEndDrawer(); - } - } -} diff --git a/lib/views/pages/category/category_detail.dart b/lib/views/pages/category/category_detail.dart deleted file mode 100644 index 45281d1b2..000000000 --- a/lib/views/pages/category/category_detail.dart +++ /dev/null @@ -1,153 +0,0 @@ -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:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/circle_text.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/model/category_model.dart'; -import 'package:flutter_unit/model/widget_model.dart'; - -/// create by 张风捷特烈 on 2020-04-22 -/// contact me by email 1981462002@qq.com -/// 说明: - -class CategoryShow extends StatelessWidget { - final CategoryModel model; - - CategoryShow({this.model}); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: Text(model.name)), - body: BlocBuilder( - builder: (_, state) { - if (state is CategoryWidgetLoadedState) { - return _buildWidgetList(state.widgets); - } - return const SizedBox(); - }), - ); - } - - Widget _buildWidgetList(List widgets) { - return ListView.separated( - separatorBuilder: (_, index) => const Divider(height: 1), - itemBuilder: (context, index) => Dismissible( - direction: DismissDirection.endToStart, - key: ValueKey(widgets[index].id), - background: Container( - padding: EdgeInsets.only(right: 20), - alignment: Alignment.centerRight, - color: Colors.red, - child: const Icon( - CupertinoIcons.delete_solid, - color: Colors.white, - size: 30, - ), - ), - onDismissed: (v) { - BlocProvider.of(context).add( - EventToggleCategoryWidget(model.id, widgets[index].id)); - }, - child: Container( - margin: EdgeInsets.symmetric(horizontal: 15, vertical: 8), - child: FeedbackWidget( - duration: Duration(milliseconds: 200), - onPressed: () => _toDetailPage(context, widgets[index]), - child: - // Container(height: 60,) - SimpleWidgetItem( - data: widgets[index], - )), - ), - ), - itemCount: widgets.length); - } - - _toDetailPage(BuildContext context, WidgetModel model) async { - BlocProvider.of(context).add(FetchWidgetDetail(model)); - Navigator.pushNamed(context, UnitRouter.widget_detail, arguments: model); - } -} - -class SimpleWidgetItem extends StatelessWidget { - final WidgetModel data; - - SimpleWidgetItem({this.data}); - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.transparent, - height: 75, - child: Row( - children: [ - _buildLeading(), - const SizedBox( - width: 20, - ), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [_buildTitle(), _buildSummary()], - ), - ) - ], - ), - ); - } - - Widget _buildTitle() { - return Row( - children: [ - Text(data.name, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontSize: 17, - fontWeight: FontWeight.bold, - shadows: [ - Shadow(color: Colors.white, offset: Offset(.3, .3)) - ])), - const SizedBox(width: 15), - StarScore( - star: Star(emptyColor: Colors.white, size: 12, fillColor: data.color), - score: data.lever, - ) - ], - ); - } - - Widget _buildLeading() => Padding( - padding: const EdgeInsets.only(left: 5, right: 5), - child: data.image == null - ? Material( - color: Colors.transparent, - child: CircleText( - text: data.name, - size: 60, - color: data.color, - ), - ) - : CircleImage( - image: data.image, - size: 60, - ), - ); - - Widget _buildSummary() { - return Text( - data.info, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - color: Color(0xFF757575), - fontSize: 14, - shadows: [Shadow(color: Colors.white, offset: Offset(.5, .5))]), - ); - } -} diff --git a/lib/views/pages/category/category_page.dart b/lib/views/pages/category/category_page.dart deleted file mode 100644 index 3f6596eab..000000000 --- a/lib/views/pages/category/category_page.dart +++ /dev/null @@ -1,135 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/model/category_model.dart'; -import 'package:flutter_unit/views/components/permanent/circle.dart'; -import 'package:flutter_unit/views/components/project/default/loading_shower.dart'; -import 'package:flutter_unit/views/components/project/dialogs/delete_category_dialog.dart'; -import 'package:flutter_unit/views/components/project/items/category_list_item.dart'; -import 'package:flutter_unit/views/components/project/no_more_widget.dart'; - -import 'edit_category_panel.dart'; -import 'empty_category.dart'; - -class CategoryPage extends StatelessWidget { - - final gridDelegate = const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - mainAxisSpacing: 10, - crossAxisSpacing: 10, - childAspectRatio: 0.8, - ); - - @override - Widget build(BuildContext context) { - return BlocBuilder(builder: (ctx, state) { - if (state is CategoryLoadedState) { - return CustomScrollView( - slivers: [ - SliverOverlapInjector( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor(ctx), - ), - _buildContent(context, state), - SliverToBoxAdapter( - child: NoMoreWidget(), - ) - ], - ); - } - if(state is CategoryLoadingState) return LoadingShower(); - return EmptyCategory(); - }); - } - - _buildContent(BuildContext context, CategoryLoadedState state) { - return SliverPadding( - padding: const EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 0), - sliver: SliverGrid( - delegate: SliverChildBuilderDelegate( - (_, index) => Container( - child: GestureDetector( - onTap: () => - _toDetailPage(context, state.categories[index]), - child: CategoryListItem( - data: state.categories[index], - onDeleteItemClick: (model) => - _deleteCollect(context, model), - onEditItemClick: (model) => - _editCollect(context, model), - )), - ), - childCount: state.categories.length), - gridDelegate: gridDelegate), - ); - } - - _deleteCollect(BuildContext context, CategoryModel model) { - showDialog( - context: context, - builder: (ctx) => Dialog( - elevation: 5, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - child: Container( - width: 50, - child: DeleteCategoryDialog( - title: '删除收藏集', - content: ' 删除【${model.name}】收藏集,你将会失去其中的所有收藏组件,是否确定继续执行?', - onSubmit: () { - BlocProvider.of(context) - .add(EventDeleteCategory(id: model.id)); - Navigator.of(context).pop(); - }, - ), - ), - )); - } - - _editCollect(BuildContext context, CategoryModel model) { - showDialog( - context: context, - builder: (ctx) => Dialog( - backgroundColor:Color(0xFFF2F2F2), - elevation: 5, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - SizedBox( - height: 5, - ), - Row( - children: [ - Padding( - padding: const EdgeInsets.only(left: 20, right: 10), - child: Circle( - color: Theme.of(context).primaryColor, - ), - ), - Text( - '修改收藏集', - style: TextStyle(fontSize: 20), - ), - Spacer(), - CloseButton() - ], - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: EditCategoryPanel(model: model,type: EditType.update,), - ), - ], - ), - )); - } - - _toDetailPage(BuildContext context, CategoryModel model) { - BlocProvider.of(context).add(EventLoadCategoryWidget(model.id)); - Navigator.pushNamed(context, UnitRouter.category_show, arguments: model); - } - - -} diff --git a/lib/views/pages/category/collect_page.dart b/lib/views/pages/category/collect_page.dart deleted file mode 100644 index 5414c3d5c..000000000 --- a/lib/views/pages/category/collect_page.dart +++ /dev/null @@ -1,138 +0,0 @@ - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/user_system/component/authentic_widget.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/pages/category/sync/upload_button.dart'; - -import 'sync/async_button.dart'; -import 'category_page.dart'; -import 'like_widget_page.dart'; - -class CollectPage extends StatefulWidget { - @override - _CollectPageState createState() => _CollectPageState(); -} - -class _CollectPageState extends State - with AutomaticKeepAliveClientMixin { - final _tabs = [ - '收藏集录', - '珍藏组件', - ]; - - @override - Widget build(BuildContext context) { - super.build(context); - BuildContext _topContext = context; - return Scaffold( - backgroundColor: Colors.white, - body: Container( - color: - BlocProvider.of(context).state.color.withAlpha(11), - child: DefaultTabController( - length: _tabs.length, // This is the number of tabs. - child: NestedScrollView( - headerSliverBuilder: (context, innerBoxIsScrolled) => [ - SliverOverlapAbsorber( - handle: NestedScrollView.sliverOverlapAbsorberHandleFor( - context), - sliver: _buildAppBar(_topContext, innerBoxIsScrolled)), - ], - body: TabBarView( - children: [ - CategoryPage(), - LikeWidgetPage(), - ], - ), - ), - ), - )); - } - - Widget _buildAppBar(BuildContext context, bool index) { - return SliverAppBar( - leading: Container( - margin: EdgeInsets.all(10), - child: FeedbackWidget( - onPressed: () { - // Navigator.of(context).pushNamed(UnitRouter.login); - }, - child: CircleImage( - image: AssetImage('assets/images/icon_head.webp'), - borderSize: 1.5, - ), - )), - backgroundColor: BlocProvider.of(context).state.color, - actions: [ - SizedBox( - width: 32, - child: AuthenticWidget.just(UploadCategoryButton())), - // SizedBox(width: 5,), - SizedBox( - width: 32, - child: AuthenticWidget.just(SyncCategoryButton())), - _buildAddAction(context) - ], - title: Text( - '收藏集 CollectUnit', - style: TextStyle( - color: Colors.white, //标题 - fontSize: 18, - shadows: [ - Shadow(color: Colors.blue, offset: Offset(1, 1), blurRadius: 2) - ]), - ), - pinned: true, - expandedHeight: 150.0, - flexibleSpace: FlexibleSpaceBar( - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - forceElevated: index, - bottom: PreferredSize( - preferredSize: Size.fromHeight(30), - child: TabBar( - indicatorColor: Colors.transparent, - unselectedLabelColor: Colors.white, - labelColor: Colors.black, - labelStyle: TextStyle(fontSize: 16, shadows: [ - Shadow( - color: Theme.of(context).primaryColor, - offset: Offset(1, 1), - blurRadius: 10) - ]), - tabs: _tabs - .map((String name) => Container( - margin: EdgeInsets.only(bottom: 5), - alignment: Alignment.center, - child: Text(name))) - .toList(), - ), - ), - ); - } - - Widget _buildAddAction(BuildContext context) => IconButton( - icon: const Icon( - Icons.add, - size: 30, - ), - onPressed: () => Scaffold.of(context).openEndDrawer()); - - - @override - bool get wantKeepAlive => true; -} - - - - diff --git a/lib/views/pages/category/edit_category_panel.dart b/lib/views/pages/category/edit_category_panel.dart deleted file mode 100644 index b5d659643..000000000 --- a/lib/views/pages/category/edit_category_panel.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/res/style/unit_color.dart'; -import 'package:flutter_unit/app/utils/color_utils.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/edit_panel.dart'; -import 'package:flutter_unit/views/components/permanent/input_button.dart'; -import 'package:flutter_unit/views/components/project/color_chooser.dart'; -import 'package:flutter_unit/model/category_model.dart'; - -/// create by 张风捷特烈 on 2020-04-23 -/// contact me by email 1981462002@qq.com -/// 说明: - -enum EditType { add, update } - -class EditCategoryPanel extends StatefulWidget { - final CategoryModel model; - final EditType type; - - EditCategoryPanel({this.model, this.type = EditType.add}); - - @override - _EditCategoryPanelState createState() => _EditCategoryPanelState(); -} - -class _EditCategoryPanelState extends State { - String name; - String color; - String info; - - int get colorIndex => widget.model == null - ? 0 - : UnitColor.collectColorSupport - .map((e) => e.value) - .toList() - .indexOf(widget.model.color.value); - - @override - void initState() { - super.initState(); - info = widget.model?.info; - color = widget.model == null - ? null - : ColorUtils.colorString(widget.model.color); - } - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), - child: InputButton( - defaultText: widget.model?.name, - config: InputButtonConfig(hint: '收藏集名称', iconData: Icons.check), - onSubmit: (str) { - name = str; - if (name.isNotEmpty) { - if (widget.type == EditType.add) { - BlocProvider.of(context).add( - EventAddCategory(name: name, info: info, color: color)); - } - if (widget.type == EditType.update) { - BlocProvider.of(context).add( - EventUpdateCategory( - id: widget.model.id, - name: name, - info: info, - color: color)); - } - } - Navigator.of(context).pop(); - }, - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), - child: EditPanel( - defaultText: widget.model?.info, - submitClear: false, - hint: '收藏集简介...', - onChange: (v) => info = v, - ), - ), - Container( - alignment: Alignment.center, - padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), - child: ColorChooser( - defaultIndex: colorIndex, - colors: UnitColor.collectColorSupport, - onChecked: (v) => color = ColorUtils.colorString(v), - ), - ), - ], - ); - } -} diff --git a/lib/views/pages/category/empty_category.dart b/lib/views/pages/category/empty_category.dart deleted file mode 100644 index 3244b9b5a..000000000 --- a/lib/views/pages/category/empty_category.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/res/color_unit.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/permanent/panel.dart'; -import 'package:sqflite/sqflite.dart'; -import 'package:path/path.dart' as path; -/// create by 张风捷特烈 on 2021/2/25 -/// contact me by email 1981462002@qq.com -/// 说明: - -class EmptyCategory extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Center( - child: Panel( - radius: 15, - color: ColorUnit.warning_color.withOpacity(0.3), - child: Wrap( - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - direction: Axis.vertical, - spacing: 5, - children: [ - const Text( - " 您还没有收藏集! ", - style: TextStyle(fontSize: 18,color: ColorUnit.head_text_color), - ), - SizedBox( - width: 10, - ), - FeedbackWidget( - onPressed: ()=>_recallDatabase(context), - child: Icon( - Icons.refresh, - textDirection: TextDirection.rtl, - color: Colors.blue, - size: 36, - ), - ), - const Text( - "恢复默认", - style: TextStyle(fontSize: 14,color: ColorUnit.input_hit_text_color), - ), - ], - ), - )); - } - - _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); - BlocProvider.of(context).add(EventLoadCategory()); - } -} diff --git a/lib/views/pages/category/home_right_drawer.dart b/lib/views/pages/category/home_right_drawer.dart deleted file mode 100644 index bc5f5a95b..000000000 --- a/lib/views/pages/category/home_right_drawer.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/circle.dart'; -import 'package:flutter_unit/views/components/project/unit_drawer_header.dart'; - -import 'edit_category_panel.dart'; - -class HomeRightDrawer extends StatefulWidget { - - @override - _HomeRightDrawerState createState() => _HomeRightDrawerState(); -} - -class _HomeRightDrawerState extends State { - String name; - String color; - String info; - - @override - Widget build(BuildContext context) { - return Drawer( - elevation: 3, - child: _buildChild(context), - ); - } - - Widget _buildChild(BuildContext context) { - final Color color = BlocProvider.of(context).state.color; - - return Container( - color: color.withAlpha(33), - child: ListView(padding: EdgeInsets.zero, children: [ - UnitDrawerHeader(color:color), - _buildTitle(context), - EditCategoryPanel() - ]), - ); - } - - Widget _buildTitle(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(top: 5.0, bottom: 8), - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Circle( - color: Theme.of(context).primaryColor, - radius: 5, - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8), - child: Text( - '添加收藏集', - style: TextStyle(fontSize: 16, shadows: [ - Shadow( - color: Colors.white, offset: Offset(.5, .5), blurRadius: 1) - ]), - ), - ), - Circle( - color: Theme.of(context).primaryColor, - radius: 5, - ), - ], - ), - ); - } -} diff --git a/lib/views/pages/category/like_widget_page.dart b/lib/views/pages/category/like_widget_page.dart deleted file mode 100644 index 6dfc0ca9f..000000000 --- a/lib/views/pages/category/like_widget_page.dart +++ /dev/null @@ -1,69 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/project/no_more_widget.dart'; -import 'package:flutter_unit/model/widget_model.dart'; -import 'package:flutter_unit/views/components/project/items/collect_widget_list_item.dart'; - -/// create by 张风捷特烈 on 2020/6/16 -/// contact me by email 1981462002@qq.com -/// 说明: - -class LikeWidgetPage extends StatelessWidget { - - final SliverGridDelegate gridDelegate = - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, - 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), - SliverToBoxAdapter( - child: NoMoreWidget(), - ) - ], - ); - }); - } - - Widget _buildContent(BuildContext context, LikeWidgetState state) { - return SliverPadding( - padding: EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 0), - sliver: SliverGrid( - delegate: SliverChildBuilderDelegate( - (_, index) => Container( - child: GestureDetector( - onTap: () => - _toDetailPage(context, state.widgets[index]), - child: CollectWidgetListItem( - data: state.widgets[index], - onDeleteItemClick: (model) => - _deleteCollect(context, model), - )), - ), - childCount: state.widgets.length), - gridDelegate: gridDelegate), - ); - } - - _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.pushNamed(context, UnitRouter.widget_detail, arguments: model); - } -} diff --git a/lib/views/pages/category/sync/async_button.dart b/lib/views/pages/category/sync/async_button.dart deleted file mode 100644 index da21f9682..000000000 --- a/lib/views/pages/category/sync/async_button.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/api/category_api.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/blocs/category/category_bloc.dart'; -import 'package:flutter_unit/model/category_model.dart'; -import 'package:flutter_unit/repositories/local_db.dart'; -import 'package:flutter_unit/repositories/rep/category_repository.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; - -/// create by 张风捷特烈 on 2021/2/24 -/// contact me by email 1981462002@qq.com -/// 说明: 同步数据按钮,点击时请求服务器,获取备份数据。 - -class SyncCategoryButton extends StatefulWidget { - @override - _SyncCategoryButtonState createState() => _SyncCategoryButtonState(); -} - -enum AsyncType { loading, error, none, success } - -class _SyncCategoryButtonState extends State { - AsyncType state = AsyncType.none; - - @override - Widget build(BuildContext context) { - Widget result; - switch (state) { - case AsyncType.loading: - result = _buildLoading(); - break; - case AsyncType.error: - result = _buildError(); - break; - case AsyncType.none: - result = _buildDefault(); - break; - case AsyncType.success: - result = _buildSuccess(); - break; - } - return result; - } - - Widget _buildLoading() { - return const CupertinoActivityIndicator(); - } - - Widget _buildError() { - return const Icon( - TolyIcon.error, - size: 25, - color: Colors.red, - ); - } - - Widget _buildDefault() { - return FeedbackWidget( - child: const Icon( - TolyIcon.download, - size: 28, - ), - onPressed: _doSync); - } - - void _doSync() async { - setState(() => state = AsyncType.loading); - ResultBean result = await CategoryApi.getCategoryData(); - - if (result.status) { - // 说明请求成功 - if (result.data != null) { - //说明有后台备份数据,进行同步操作 - CategoryRepository repository = BlocProvider.of(context).repository; - await repository.syncCategoryByData(result.data.data,result.data.likeData); - BlocProvider.of(context).add(EventLoadCategory()); - BlocProvider.of(context).add(EventLoadLikeData()); - } else { - // 说明还没有后台数据, - // 这里防止有傻孩子没点备份,就点同步,哥哥好心,给备份一下。 - CategoryRepository rep = BlocProvider.of(context).repository; - List loadCategories = await rep.loadCategoryData(); - List likeData = await LocalDb.instance.likeDao.likeWidgetIds(); - - String json = jsonEncode(loadCategories); - String likeJson = jsonEncode(likeData); - await CategoryApi.uploadCategoryData(data: json,likeData: likeJson); - } - setState(() => state = AsyncType.success); - _toDefault(); - } else { - setState(() => state = AsyncType.error); - _toDefault(); - } - } - - Widget _buildSuccess() => const Icon( - TolyIcon.upload_success, - size: 25, - color: Colors.green, - ); - - void _toDefault() async { - await Future.delayed(const Duration(milliseconds: 800)); - setState(() { - state = AsyncType.none; - }); - } -} diff --git a/lib/views/pages/category/sync/upload_button.dart b/lib/views/pages/category/sync/upload_button.dart deleted file mode 100644 index be5229868..000000000 --- a/lib/views/pages/category/sync/upload_button.dart +++ /dev/null @@ -1,106 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/api/category_api.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; -import 'package:flutter_unit/blocs/category/category_bloc.dart'; -import 'package:flutter_unit/model/category_model.dart'; -import 'package:flutter_unit/repositories/local_db.dart'; -import 'package:flutter_unit/repositories/rep/category_repository.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; - -/// create by 张风捷特烈 on 2021/2/24 -/// contact me by email 1981462002@qq.com -/// 说明: - -class UploadCategoryButton extends StatefulWidget { - @override - _UploadCategoryButtonState createState() => _UploadCategoryButtonState(); -} - -enum AsyncType { loading, error, none, success } - -class _UploadCategoryButtonState extends State { - AsyncType state = AsyncType.none; - - @override - Widget build(BuildContext context) { - Widget result; - switch (state) { - case AsyncType.loading: - result = _buildLoading(); - break; - case AsyncType.error: - result = _buildError(); - break; - case AsyncType.none: - result = _buildDefault(); - break; - case AsyncType.success: - result = _buildSuccess(); - break; - } - return result; - } - - Widget _buildLoading() { - return const CupertinoActivityIndicator(); - } - - Widget _buildError() { - return const Icon( - TolyIcon.error, - size: 28, - color: Colors.green, - ); - } - - Widget _buildDefault() { - return FeedbackWidget( - child: const Icon( - TolyIcon.upload, - size: 28, - ), - onPressed: _doUploadCategoryData); - } - - void _doUploadCategoryData() async{ - setState(() => state = AsyncType.loading); - - CategoryRepository rep = BlocProvider.of(context).repository; - List loadCategories = await rep.loadCategoryData(); - List likeData = await LocalDb.instance.likeDao.likeWidgetIds(); - - - String json = jsonEncode(loadCategories); - String likeJson = jsonEncode(likeData); - - ResultBean result = await CategoryApi.uploadCategoryData(data: json,likeData: likeJson); - - if (result.status) { - setState(() => state = AsyncType.success); - _toDefault(); - } else { - setState(() => state = AsyncType.error); - _toDefault(); - } - } - - Widget _buildSuccess() { - return const Icon( - TolyIcon.upload_success, - size: 25, - color: Colors.green, - ); - } - - void _toDefault() async { - await Future.delayed(const Duration(milliseconds: 800)); - setState(() { - state = AsyncType.none; - }); - } -} diff --git a/lib/views/pages/data_manage/data_manage_page.dart b/lib/views/pages/data_manage/data_manage_page.dart deleted file mode 100644 index 6c063ec87..000000000 --- a/lib/views/pages/data_manage/data_manage_page.dart +++ /dev/null @@ -1,145 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/api/category_api.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/app/utils/Toast.dart'; -import 'package:flutter_unit/app/utils/http_utils/result_bean.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/model/category_model.dart'; -import 'package:flutter_unit/repositories/local_db.dart'; -import 'package:flutter_unit/repositories/rep/category_repository.dart'; -import 'package:flutter_unit/user_system/component/authentic_widget.dart'; -import 'package:path/path.dart' as path; -import 'package:sqflite/sqflite.dart'; - -/// create by 张风捷特烈 on 2021/2/26 -/// contact me by email 1981462002@qq.com -/// 说明: -/// - -class DataManagePage extends StatelessWidget { - @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(Divider()), - AuthenticWidget.just(ListTile( - trailing: Icon( - TolyIcon.download, - color: Theme.of(context).primaryColor, - ), - title: const Text('同步收藏集数据'), - onTap: () => _doSync(ctx), - )), - AuthenticWidget.just(Divider()), - ListTile( - trailing: Icon( - Icons.refresh, - color: Theme.of(context).primaryColor, - ), - title: const Text('收藏集数据重置'), - // trailing: _nextIcon(context), - onTap: () => _recallDatabase(ctx), - ), - 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(EventLoadCategory()); - BlocProvider.of(context).add(EventLoadLikeData()); - Toast.toast(context, '重置成功!'); - } - - void _doUploadCategoryData(BuildContext context) async { - CategoryRepository rep = BlocProvider.of(context).repository; - List loadCategories = await rep.loadCategoryData(); - - List likeData = await LocalDb.instance.likeDao.likeWidgetIds(); - - String json = jsonEncode(loadCategories); - String likeJson = jsonEncode(likeData); - - ResultBean result = - await CategoryApi.uploadCategoryData(data: json, likeData: likeJson); - - if (result.status) { - Toast.toast(context, '数据集备份成功!'); - } else { - Toast.toast(context, '数据集备份失败!'); - } - } - - void _doSync(BuildContext context) async { - ResultBean result = await CategoryApi.getCategoryData(); - - if (result.status) { - // 说明请求成功 - if (result.data != null) { - //说明有后台备份数据,进行同步操作 - CategoryRepository repository = - BlocProvider.of(context).repository; - await repository.syncCategoryByData( - result.data.data, result.data.likeData); - BlocProvider.of(context).add(EventLoadCategory()); - BlocProvider.of(context).add(EventLoadLikeData()); - } else { - // 说明还没有后台数据, - // 这里防止有傻孩子没点备份,就点同步,哥哥好心,给备份一下。 - CategoryRepository rep = - BlocProvider.of(context).repository; - List loadCategories = await rep.loadCategoryData(); - List likeData = await LocalDb.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/views/pages/gallery/gallery_page.dart b/lib/views/pages/gallery/gallery_page.dart deleted file mode 100644 index be39d5bc8..000000000 --- a/lib/views/pages/gallery/gallery_page.dart +++ /dev/null @@ -1,195 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -import 'package:flutter_unit/painter_system/art/circle_packing.dart'; -import 'package:flutter_unit/painter_system/art/cubic_disarray.dart'; -import 'package:flutter_unit/painter_system/base/draw_picture.dart'; -import 'package:flutter_unit/painter_system/art/hypnotic_squares.dart'; -import 'package:flutter_unit/painter_system/art/joy_division.dart'; -import 'package:flutter_unit/painter_system/art/piet_mondrian.dart'; -import 'package:flutter_unit/painter_system/fun/random_portrait.dart'; -import 'package:flutter_unit/painter_system/art/tiled_lines.dart'; -import 'package:flutter_unit/painter_system/art/triangular_mesh.dart'; -import 'package:flutter_unit/painter_system/art/un_deux_trois.dart'; -import 'package:flutter_unit/painter_system/anim/draw_path.dart'; -import 'package:flutter_unit/painter_system/picture_frame.dart'; - -/// create by 张风捷特烈 on 2020/10/10 -/// contact me by email 1981462002@qq.com -/// 说明: - -class GalleryPage extends StatefulWidget { - @override - _GalleryPageState createState() => _GalleryPageState(); -} - -class _GalleryPageState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - centerTitle: true, - title: Text("Flutter 绘制集录"), - ), - body: Column(children: [ - // _buildTitle(), - Expanded( - child: Container( - // color: Colors.lightBlueAccent, - child: Center( - child: PageView( - children: [ - FrameShower( - title: "The Chaos", - author: "张风捷特烈", - info: - " 本样例介绍如何进行图片的绘制。通过加载图片并将图片资源绘制到指定的区域。在上层绘制一批45°倾角的栅格线,可以练习基本的绘制。", - content: DrawPicture()), - FrameShower( - title: "Draw Curve", - author: "张风捷特烈", - info: - " 本样例介绍如何使用路径绘制函数曲线,并使用路径测量进行动画", - content: DrawPath()), - FrameShower( - title: "Random Portrait", - author: "张风捷特烈", - info: - " 本样例介绍绘制矩形及随机数处理。通过点位集合确定矩形位置信息,将其绘制出来。其中对点的随机生成和对称处理能让你练习对数据的控制力。", - content: RandomPortrait()), - FrameShower( - title: "Tiled Line", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的tiled-lines,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: TiledLines(), - ), - FrameShower( - title: "Joy Division", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的joy-division,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: JoyDivision(), - ), - FrameShower( - title: "Cubic Disarray", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的cubic-disarray,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: CubicDisarray(), - ), - FrameShower( - title: "Triangular Mesh", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的triangular-mesh,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: TriangularMesh(), - ), - FrameShower( - title: "Un Deux Trois", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的un-deux-trois,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: UnDeuxTrois(), - ), - FrameShower( - title: "Circle Packing", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的circle-packing,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: CirclePacking(), - ), - FrameShower( - title: "Hypnotic Squares", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的hypnotic-squares,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: HypnoticSquares(), - ), - FrameShower( - title: "Piet Mondrian", - author: "generativeartistry.com", - info: - "本样例根源来自generativeartistry.com的piet-mondrian,由xrr2016使用Flutter实现。仓库地址:flutter-generative-artistry", - content: PietMondrian(), - ) - ], - ), - ), - ), - ), - ])); - } -} - -class FrameShower extends StatelessWidget { - final String title; - final String author; - final String srcUrl; - final String info; - final Widget content; - - FrameShower( - {Key key, - this.title = "", - this.author = "", - this.srcUrl = "", - this.info = "", - @required this.content}) - : super(key: key); - - @override - Widget build(BuildContext context) { - return Center( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - PictureFrame( - width: MediaQuery.of(context).size.shortestSide, - height: MediaQuery.of(context).size.shortestSide, - child: content, - ), - Text( - title, - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - SizedBox( - height: 10, - ), - Container( - padding: EdgeInsets.symmetric(horizontal: 12), - child: Row( - children: [ - Text( - "作者: $author ", - style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold), - ), - Spacer(), - Text( - "源码地址 ", - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.bold, - color: Colors.blueAccent), - ), - ], - ), - ), - SizedBox( - height: 10, - ), - Container( - padding: EdgeInsets.symmetric(horizontal: 12), - alignment: Alignment.topLeft, - child: Text( - info, - style: TextStyle( - fontSize: 12, - color: Colors.grey, - fontWeight: FontWeight.bold), - )), - ], - ), - ); - } -} diff --git a/lib/views/pages/issues_point/issue_item.dart b/lib/views/pages/issues_point/issue_item.dart deleted file mode 100644 index dd0048790..000000000 --- a/lib/views/pages/issues_point/issue_item.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/app/utils/convert_man.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/color_wrapper.dart'; -import 'package:flutter_unit/model/github/issue.dart'; - -/// create by 张风捷特烈 on 2020/9/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -class IssueItem extends StatelessWidget { - final Issue issue; - - IssueItem({this.issue}); - - @override - Widget build(BuildContext context) { - return Container( - padding: 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: Colors.white, offset: Offset(1, .5)) - ]), - ), - ), - Row( - children: [ - Spacer(), - WrapColor( - color: Colors.greenAccent, - child: Text( - issue.commentNum.toString(), - style: TextStyle(color: Colors.white), - )), - SizedBox( - width: 5, - ), - Icon( - TolyIcon.icon_common, - size: 20, - ), - ], - ) - ], - ), - ); - } - - Widget _buildTop() { - return Row( - children: [ - CircleImage( - image: NetworkImage(issue.user.avatarUrl), - size: 40, - borderSize: 2, - ), - SizedBox( - width: 10, - ), - WrapColor( - child: Text( - "#${issue.number}", - style: TextStyle(color: Colors.white), - )), - SizedBox( - width: 10, - ), - Text( - issue.user.login, - style: TextStyle(fontWeight: FontWeight.bold), - ), - Spacer(), - Text(ConvertMan.time2string(issue.createdAt)), - ], - ); - } -} \ No newline at end of file diff --git a/lib/views/pages/issues_point/issues_detail.dart b/lib/views/pages/issues_point/issues_detail.dart deleted file mode 100644 index b197cd286..000000000 --- a/lib/views/pages/issues_point/issues_detail.dart +++ /dev/null @@ -1,154 +0,0 @@ -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/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/color_wrapper.dart'; -import 'package:flutter_unit/views/components/permanent/markdown/markdown_widget.dart'; -import 'package:flutter_unit/views/components/permanent/panel.dart'; -import 'package:flutter_unit/model/github/issue_comment.dart'; -import 'package:flutter_unit/model/github/issue.dart'; - -/// create by 张风捷特烈 on 2020/9/3 -/// contact me by email 1981462002@qq.com -/// 说明: - -class IssuesDetailPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar(title: Text('Flutter Point'),), - body: BlocBuilder( - builder: _buildContent), - ); - } - - Widget _buildContent(BuildContext context, PointCommentState state) { - if (state is PointCommentLoading) { - return IssueTitle(issue: state.issue); - } - - if (state is PointCommentLoaded) { - return CustomScrollView( - slivers: [ - SliverToBoxAdapter(child: IssueTitle(issue: state.issue)), - SliverList( - delegate: SliverChildBuilderDelegate( - (ctx, int index) => IssueCommentWidget(comment:state.comments[index]), - childCount: state.comments.length)) - ], - ); - } - - return Container(); - } -} - -class IssueTitle extends StatelessWidget { - final Issue issue; - - IssueTitle({Key key, this.issue}) : super(key: key); - - String get issueDesHtml => issue.bodyHtml != null - ? issue.bodyHtml - : (issue.body != null) ? issue.body : ""; - - @override - Widget build(BuildContext context) { - return SingleChildScrollView( - child: Column( - children: [ - Stack( - children: [ - Container( - padding: EdgeInsets.symmetric(horizontal: 8), - child: Panel(child: Text('${issue.title}',style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),)), - ), - Positioned( - right: 10, - bottom: 10, - child: WrapColor( - child: Text('#${issue.number}',style: TextStyle(color: Colors.white),), - )), - ], - ), - Divider(), - - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: WrapColor( - color: Colors.blue.withAlpha(22), - child: ListTile( - dense: true, - leading: CircleImage(size:40,borderSize:1,image: NetworkImage(issue.user.avatarUrl),), - title: Text(issue.user.login), - subtitle:Row( - children: [ - Text('创建于:${ConvertMan.time2string(issue.createdAt)}'), - Spacer(), - WrapColor( - color: Colors.green, - child: Text('更新于:${ConvertMan.time2string(issue.updatedAt)}',style: TextStyle(color: Colors.white),)), - - ], - ) , - ), - ), - ), - - Padding( - padding: const EdgeInsets.all(8.0), - child: MarkdownWidget( - markdownData: issueDesHtml, style: MarkdownWidget.kWhite), - ), - Divider(thickness: 2,) - ], - ), - ); - } -} - -class IssueCommentWidget extends StatelessWidget { - final IssueComment comment; - - IssueCommentWidget({Key key, this.comment}) : super(key: key); - - String get issueDesHtml => comment.bodyHtml != null - ? comment.bodyHtml - : (comment.body != null) ? comment.body : ""; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: WrapColor( - color: Colors.blue.withAlpha(22), - child: ListTile( - dense: true, - leading: CircleImage(size:40,borderSize:1,image: NetworkImage(comment.user.avatarUrl),), - title: Text(comment.user.login), - subtitle:Row( - children: [ - Text('创建于:${ConvertMan.time2string(comment.createdAt)}'), - Spacer(), - WrapColor( - color: Colors.green, - child: Text('更新于:${ConvertMan.time2string(comment.updatedAt)}',style: TextStyle(color: Colors.white),)), - - ], - ) , - ), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: MarkdownWidget( - markdownData: issueDesHtml, style: MarkdownWidget.kWhite), - ), - Divider(thickness: 2,) - ], - ); - } -} diff --git a/lib/views/pages/issues_point/issues_point_page.dart b/lib/views/pages/issues_point/issues_point_page.dart deleted file mode 100644 index 5135fe86e..000000000 --- a/lib/views/pages/issues_point/issues_point_page.dart +++ /dev/null @@ -1,144 +0,0 @@ -import 'package:flutter/cupertino.dart'; - -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:flutter_unit/app/api/issues_api.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/model/github/issue.dart'; -import 'package:flutter_unit/model/github/repository.dart'; - -import 'issue_item.dart'; -import 'repo_widget.dart'; - -/// create by 张风捷特烈 on 2020/6/17 -/// contact me by email 1981462002@qq.com -/// 说明: - -class IssuesPointPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold(body: IssuesPointContent()); - } -} - -class IssuesPointContent extends StatefulWidget { - @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) => Container( - child: RefreshIndicator( - onRefresh: _loadIssues, - child: CustomScrollView(slivers: [ - _buildSliverAppBar(), - buildContentByState(state) - ]), - ), - )); - } - - Widget buildContentByState(PointState state) { - if (state is PointLoading) { - return 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: () { - BlocProvider.of(ctx).add(EventLoadPointComment(issues[index])); - Navigator.pushNamed(ctx, UnitRouter.point_detail); - }, - child: IssueItem(issue: issues[index])), - childCount: issues.length)); - } - - if (state is PointLoadFailure) { - return SliverPadding( - padding: EdgeInsets.only(top: 40), - sliver: SliverToBoxAdapter( - child: Center( - child: Text(state.error), - )), - ); - } - - return SliverPadding( - padding: EdgeInsets.zero, - ); - } - - Widget _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 210.0, -// leading: _buildLeading(), - title: Text('Flutter要点集录'), -// actions: _buildActions(), - elevation: 5, - pinned: true, - actions: [ - IconButton( - icon: Icon( - Icons.help_outline, - color: Colors.white, - ), - onPressed: () { - Navigator.of(context).pushNamed(UnitRouter.bug); - }) - ], - backgroundColor: Colors.blue, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: _repository == null - ? Center( - child: SpinKitFadingCube( - color: Colors.white, - ), - ) - : RepoWidget( - repository: _repository, - ), - ), - ); - } - - Future _loadIssues() async { - BlocProvider.of(context).add(EventLoadPoint()); - await Future.delayed(Duration(milliseconds: 200)); - } - - void _loadRepo() async { - final Repository result = await IssuesApi.getRepoFlutterUnit(); - setState(() { - if (result != null) { - _repository = result; - } - }); - } -} diff --git a/lib/views/pages/issues_point/repo_widget.dart b/lib/views/pages/issues_point/repo_widget.dart deleted file mode 100644 index 01865d513..000000000 --- a/lib/views/pages/issues_point/repo_widget.dart +++ /dev/null @@ -1,132 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/app/utils/convert_man.dart'; -import 'package:flutter_unit/views/components/permanent/color_wrapper.dart'; -import 'package:flutter_unit/model/github/repository.dart'; - -/// create by 张风捷特烈 on 2020/9/3 -/// contact me by email 1981462002@qq.com -/// 说明: - - -class RepoWidget extends StatelessWidget { - final Repository repository; - - RepoWidget({this.repository}); - - @override - Widget build(BuildContext context) { - return Container( - margin: EdgeInsets.only(top: 56 + 32.0, bottom: 5), - padding: EdgeInsets.only(top:20,right: 10,left: 10), - // padding: EdgeInsets.all(10), - decoration: BoxDecoration( - boxShadow: [ - BoxShadow(color: Colors.grey, offset: Offset(0, .5), blurRadius: 3) - ], - color: Colors.white, - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(10), - bottomRight: Radius.circular(10), - )), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Row( - children: [ - Text( - repository.fullName, - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), - ), - SizedBox(width: 10,), - WrapColor( - child: Text( - repository.license.spdxId, - style: TextStyle( - fontWeight: FontWeight.bold, - color: Colors.white, - fontSize: 12), - ), - ), - Spacer(), - Text( - "创建:" + - ConvertMan.time2string(repository.createdAt, just: true), - style: TextStyle(color: Colors.grey), - ), - ], - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - repository.description, - style: TextStyle(color: Colors.grey), - ), - ), - Divider(), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - Wrap( - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Icon(Icons.star_border), - Text(repository.stargazersCount.toString()), - ]), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - "|", - style: TextStyle(fontSize: 20, color: Colors.blue), - ), - ), - Wrap( - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Icon(TolyIcon.icon_show), - SizedBox( - width: 5, - ), - Text(repository.subscribersCount.toString()), - ]), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - "|", - style: TextStyle(fontSize: 20, color: Colors.blue), - ), - ), - Wrap( - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Icon(TolyIcon.icon_fork), - Text(repository.forksCount.toString()), - ]), - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text( - "|", - style: TextStyle(fontSize: 20, color: Colors.blue), - ), - ), - Wrap( - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Icon(TolyIcon.icon_issues), - SizedBox( - width: 5, - ), - Text(repository.openIssuesCount.toString()), - ]), - ], - ) - ], - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/pages/search/app_search_bar.dart b/lib/views/pages/search/app_search_bar.dart deleted file mode 100644 index 56ed07f63..000000000 --- a/lib/views/pages/search/app_search_bar.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/repositories/dao/widget_dao.dart'; - - -class AppSearchBar extends StatefulWidget { - - - @override - _AppSearchBarState createState() => _AppSearchBarState(); -} - -class _AppSearchBarState extends State { - TextEditingController _controller=TextEditingController();//文本控制器 - - @override - Widget build(BuildContext context) => Container( - height: 35, - child: - TextField( - autofocus: false, //自动聚焦,闪游标 - controller: _controller, - maxLines: 1, - decoration: InputDecoration(//输入框装饰 - - filled: true,//填满 - fillColor: Colors.white,//白色 - prefixIcon: Icon(Icons.search),//前标 - // contentPadding: EdgeInsets.only(right: 0),//调整文字边距 - border: UnderlineInputBorder( - borderSide: BorderSide.none,//去边线 - borderRadius: BorderRadius.all(Radius.circular(15)), - ), - hintText: "搜点啥...",//提示 - hintStyle: TextStyle(fontSize: 14)//提示样式 - ), - onChanged: (str) => BlocProvider.of(context) - .add(SearchWidgetEvent(args:SearchArgs(name: str,stars: [1,2,3,4,5]))), - - onSubmitted: (str) {//提交后 - FocusScope.of(context).requestFocus(FocusNode()); //收起键盘 -// _controller.clear(); - }, - )); - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } -} diff --git a/lib/views/pages/search/not_search_page.dart b/lib/views/pages/search/not_search_page.dart deleted file mode 100644 index cc4fd9e28..000000000 --- a/lib/views/pages/search/not_search_page.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; - -class NotSearchPage extends StatelessWidget { - - - @override - Widget build(BuildContext context) { - return Container( - height: 300, - alignment: Alignment.center, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Icon(Icons.search, color: Colors.blue, size: 120.0), - Container( - padding: EdgeInsets.only(top: 16.0), - child: Text( - "哥们,搜点啥...≧◔◡◔≦", - style: TextStyle( - fontSize: 20, - color: Colors.blue, - ), - ), - ) - ], - ), - ); - } -} diff --git a/lib/views/pages/search/serach_page.dart b/lib/views/pages/search/serach_page.dart deleted file mode 100644 index dd3324d46..000000000 --- a/lib/views/pages/search/serach_page.dart +++ /dev/null @@ -1,129 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/repositories/dao/widget_dao.dart'; -import 'package:flutter_unit/views/components/permanent/circle.dart'; -import 'package:flutter_unit/views/components/project/default/loading_shower.dart'; -import 'package:flutter_unit/model/widget_model.dart'; -import 'package:flutter_unit/views/components/project/items/widget/techno_widget_list_item.dart'; -import 'package:flutter_unit/views/pages/search/app_search_bar.dart'; -import 'package:flutter_unit/views/pages/search/error_page.dart'; -import 'package:flutter_unit/views/pages/search/not_search_page.dart'; -import 'package:flutter_unit/views/components/permanent/multi_chip_filter.dart'; - -import '../../components/project/default/empty_shower.dart'; - - -class SearchPage extends StatefulWidget { - @override - _SearchPageState createState() => _SearchPageState(); -} - -class _SearchPageState extends State { - @override - Widget build(BuildContext context) { - return Scaffold( - body: WillPopScope( - onWillPop: () async { - //返回时 情空搜索 - BlocProvider.of(context).add(SearchWidgetEvent(args: SearchArgs())); - return true; - }, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - SliverToBoxAdapter(child: _buildStarFilter()), - BlocBuilder(builder:_buildBodyByState) - ], - ), - ), - ); - } - - Widget _buildSliverAppBar() { - return SliverAppBar( - pinned: true, - title: AppSearchBar(), - actions: [ - Padding( - padding: const 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: [ - Circle( - radius: 5, - color: Colors.orange, - ), - Text( - '星级查询', - style: TextStyle( - color: Theme.of(context).primaryColor, - fontWeight: FontWeight.bold), - ), - ], - ), - ), - MultiChipFilter( - data: [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, - ), - Divider(), - SizedBox(height: 10,) - ], - ); - - Widget _buildBodyByState(BuildContext context,SearchState state) { - if (state is SearchStateNoSearch) return SliverToBoxAdapter(child: NotSearchPage(),); - if (state is SearchStateLoading) return SliverToBoxAdapter(child: LoadingShower()); - if (state is SearchStateError) return SliverToBoxAdapter(child: ErrorPage()); - if (state is SearchStateSuccess) return _buildSliverList(state.result); - if (state is SearchStateEmpty) return SliverToBoxAdapter(child: EmptyShower(message: "没数据,哥也没办法\n(≡ _ ≡)/~┴┴",)); - return NotSearchPage(); - } - - Widget _buildSliverList(List models) => SliverList( - delegate: SliverChildBuilderDelegate( - (_, int index) => Container( - child: InkWell( - onTap: () => _toDetailPage(models[index]), - child: TechnoWidgetListItem( - data: models[index], - ))), - childCount: models.length), - ); - - _doSelectStart(List select) { - List temp = select.map((e)=>e+1).toList(); - if (temp.length < 5) { - temp.addAll(List.generate(5 - temp.length, (e) => -1)); - } - BlocProvider.of(context) - .add(SearchWidgetEvent(args: SearchArgs(name: '', stars: temp))); - } - - _toDetailPage(WidgetModel model) { - BlocProvider.of(context).add(FetchWidgetDetail(model)); - Navigator.pushNamed(context, UnitRouter.widget_detail,arguments: model); - } -} diff --git a/lib/views/pages/setting/code_style_setting.dart b/lib/views/pages/setting/code_style_setting.dart deleted file mode 100644 index 64823fa6a..000000000 --- a/lib/views/pages/setting/code_style_setting.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/res/cons.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/code/code_widget.dart'; -import 'package:flutter_unit/views/components/permanent/code/highlighter_style.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/permanent/circle.dart'; - -/// create by 张风捷特烈 on 2020-04-10 -/// contact me by email 1981462002@qq.com -/// 说明: - -class CodeStyleSettingPage extends StatelessWidget { - final code = """ -const String _kCounty = 'China'; - -class Hello { - final String name;//言语 - final String county;//国家 - final int age;//年龄 - - Hello({ - this.name = "张风捷特烈", - this.age = 26, - this.county = _kCounty - }); -}"""; - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('代码高亮样式'), - ), - body: BlocBuilder( - builder: (_, state) => _buildFontCell(context, - Cons.codeThemeSupport.keys.toList(), state.codeStyleIndex)), - ); - } - - Widget _buildFontCell( - BuildContext context, List styles, int index) { - return ListView.builder( - itemCount: styles.length, - itemBuilder: (_ctx, i) => FeedbackWidget( - a: 0.95, - duration: Duration(milliseconds: 200), - onPressed: (){ - BlocProvider.of(context).add(EventSwitchCoderTheme(i)); - }, - child: Stack( - fit: StackFit.passthrough, - children: [ - Card( - margin: EdgeInsets.all(10), - child: CodeWidget( - code: code, - style: styles[i], - ), - ), - - Positioned( - right: 20, - bottom: 20, - child: Text(Cons.codeThemeSupport.values.toList()[i],style: TextStyle( - fontSize: 14, - color: styles[i].stringStyle.color, - shadows: [Shadow( - color: Colors.white, - offset: Offset(.5,.5), - blurRadius: 1 - ),] - ),), - ), - - if(index == i) - Positioned( - right: 20, - top: 20, - child: Circle(radius: 10, - color: Theme.of(context).primaryColor, - child: Icon(Icons.check,color:Colors.white,size: 15,),), - ) - ], - ), - )); - } -} diff --git a/lib/views/pages/setting/font_setting.dart b/lib/views/pages/setting/font_setting.dart deleted file mode 100644 index bf19fd907..000000000 --- a/lib/views/pages/setting/font_setting.dart +++ /dev/null @@ -1,81 +0,0 @@ - -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/res/cons.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/permanent/circle.dart'; - -/// create by 张风捷特烈 on 2020-04-10 -/// contact me by email 1981462002@qq.com -/// 说明: - -class FontSettingPage extends StatelessWidget { - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('字体设置 - font setting'), - ), - body: BlocBuilder( - builder: (_, state) => _buildFontCell( - context, Cons.fontFamilySupport, state.fontFamily)), - ); - } - - Widget _buildFontCell( - BuildContext context, List fontFamilySupport, String fontFamily) { - return GridView.count( - padding: EdgeInsets.only(top: 20, left: 10, right: 10), - shrinkWrap: true, - crossAxisCount: 2, - mainAxisSpacing: 10, - crossAxisSpacing: 10, - childAspectRatio: 1.5, - children: fontFamilySupport - .map((e) => FeedbackWidget( - a: 0.95, - duration: Duration(milliseconds: 200), - onPressed: () { - BlocProvider.of(context) - .add(EventSwitchFontFamily(e)); - }, - child: Card( - child: GridTile( - header: Container( - padding: EdgeInsets.only(left: 10, right: 5), - height: 30, - color: fontFamily == e - ? Colors.blue.withAlpha(88) - : Colors.grey.withAlpha(88), - child: Row( - children: [ - Text(e, - style: TextStyle( - color: Colors.black, - fontFamily: e, - )), - Spacer(), - if (fontFamily == e) Circle(color: Theme.of(context).primaryColor,) - ], - ), - ), - child: Container( - decoration: BoxDecoration( - gradient: LinearGradient(colors: [ - Colors.blueAccent.withAlpha(22), - Colors.blueAccent.withAlpha(22), - Theme.of(context).primaryColor.withAlpha(88) - ])), - alignment: Alignment(0, 0.4), - child: Text( - '张风捷特烈\n@toly1994', - style: TextStyle(fontFamily: e, fontSize: 16), - )), - ), - ))) - .toList(), - ); - } -} diff --git a/lib/views/pages/setting/item_style_setting.dart b/lib/views/pages/setting/item_style_setting.dart deleted file mode 100644 index baed8952a..000000000 --- a/lib/views/pages/setting/item_style_setting.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/permanent/circle.dart'; -import 'package:flutter_unit/views/components/project/items/widget/home_item_support.dart'; - - -/// create by 张风捷特烈 on 2020-04-10 -/// contact me by email 1981462002@qq.com -/// 说明: item样式切换支持 - -class ItemStyleSettingPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('item样式设置'), - ), - body: BlocBuilder(builder: (_, state) { - return _buildFontCell(context, state.itemStyleIndex); - }), - ); - } - - List get items=> HomeItemSupport.itemSimples(); - - Widget _buildFontCell(BuildContext context, int index) { - return ListView.builder( - itemCount: items.length, - itemBuilder: (_, i) => FeedbackWidget( - a: 0.95, - duration: Duration(milliseconds: 200), - onPressed: () { - BlocProvider.of(context) - .add(EventChangeItemStyle(i)); - }, - child: Stack( - children: [ - items[i], - if (index == i) - Positioned( - left: 25, - top: 15, - child: Circle( - color: Theme.of(context).primaryColor, - radius: 10, - child: Icon( - Icons.check, - color: Colors.white, - size: 15, - ), - ), - ) - ], - ))); - } -} diff --git a/lib/views/pages/setting/setting_page.dart b/lib/views/pages/setting/setting_page.dart deleted file mode 100644 index 062e02399..000000000 --- a/lib/views/pages/setting/setting_page.dart +++ /dev/null @@ -1,106 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; - -class SettingPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('应用设置'), - ), - body: ListView( - children: [ - ListTile( - leading: Icon( - Icons.palette, - color: Theme.of(context).primaryColor, - ), - title: Text('主题色设置'), - trailing: _nextIcon(context), - onTap: () => Navigator.of(context).pushNamed(UnitRouter.theme_color_setting), - ), - Divider(), - ListTile( - leading: Icon( - Icons.translate, - color: Theme.of(context).primaryColor, - ), - title: Text('字体设置'), - trailing: _nextIcon(context), - onTap: () => Navigator.of(context).pushNamed(UnitRouter.font_setting), - ), - Divider(), - ListTile( - leading: Icon( - TolyIcon.icon_item, - color: Theme.of(context).primaryColor, - ), - title: Text('item样式设置'), - trailing: _nextIcon(context), - onTap: () => Navigator.of(context).pushNamed(UnitRouter.item_style_setting), - ), - Divider(), - ListTile( - leading: Icon( - TolyIcon.icon_code, - color: Theme.of(context).primaryColor, - ), - title: Text('代码高亮样式'), - trailing: _nextIcon(context), - onTap: () => Navigator.of(context).pushNamed(UnitRouter.code_style_setting), - ), - Divider(), - _buildShowBg(context), - Divider(), - _buildShowOver(context), - Divider(), - ListTile( - leading: Icon( - Icons.info, - color: Theme.of(context).primaryColor, - ), - title: Text('版本信息'), - trailing: _nextIcon(context), - onTap: () => Navigator.of(context).pushNamed(UnitRouter.version_info), - ), - ], - ), - ); - } - - Widget _buildShowBg(BuildContext context) => - BlocBuilder( - builder: (_, state) => SwitchListTile( - value: state.showBackGround, - secondary: Icon( - TolyIcon.icon_background, - color: Theme.of(context).primaryColor, - ), - title: Text('显示背景'), - onChanged: (show) { - BlocProvider.of(context) - .add(EventSwitchShowBg(show)); - }, - )); - - Widget _buildShowOver(BuildContext context) => - BlocBuilder( - builder: (_, state) => SwitchListTile( - value: state.showPerformanceOverlay, - secondary: Icon( - TolyIcon.icon_show, - color: Theme.of(context).primaryColor, - ), - title: Text('显示性能浮层'), - onChanged: (show) { - BlocProvider.of(context) - .add(EventSwitchShowOver(show)); - }, - )); - - Widget _nextIcon(BuildContext context) => - Icon(Icons.chevron_right, color: Theme.of(context).primaryColor); -} diff --git a/lib/views/pages/setting/theme_color_setting.dart b/lib/views/pages/setting/theme_color_setting.dart deleted file mode 100644 index d89fefdca..000000000 --- a/lib/views/pages/setting/theme_color_setting.dart +++ /dev/null @@ -1,93 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/res/cons.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/permanent/circle.dart'; - -/// create by 张风捷特烈 on 2020-04-10 -/// contact me by email 1981462002@qq.com -/// 说明: - -class ThemeColorSettingPage extends StatelessWidget { - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text('主题色设置'), - ), - body: BlocBuilder( - builder: (_, state) => _buildFontCell( - context, Cons.themeColorSupport.keys.toList(), state.themeColor)), - ); - } - - Widget _buildFontCell( - BuildContext context, List themeColorSupport, MaterialColor color) { - return GridView.count( - padding: EdgeInsets.only(top: 20, left: 10, right: 10), - shrinkWrap: true, - crossAxisCount: 2, - mainAxisSpacing: 10, - crossAxisSpacing: 10, - childAspectRatio: 1.5, - children: themeColorSupport - .map((e) => FeedbackWidget( - a: 0.95, - duration: Duration(milliseconds: 200), - onPressed: () => BlocProvider.of(context).add(EventSwitchThemeColor(e)), - child: GridTile( - header: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.only(topLeft: Radius.circular(10),topRight: Radius.circular(10)), - color: color == e - ? Colors.blue.withAlpha(88): - Colors.grey.withAlpha(55), - ), - padding: EdgeInsets.only(left: 10, right: 5), - height: 30, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Spacer(), - Text(colorString(e), - style: TextStyle( - color: Colors.white, - )), - Spacer(), - if (color == e) Padding( - padding: const EdgeInsets.only(right:8.0), - child: Circle(color: Colors.white,radius: 7,), - ) - ], - ), - ), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - gradient: LinearGradient(colors: [ - e[50], - e[100], - e[200], - e[300], - e[400], - e[500], - e[600], - e[700], - e[800], - e[900], - ])), - alignment: Alignment(0,0.35), - child: Text( - Cons.themeColorSupport [e], - style: 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/views/pages/splash/flutter_unit_text.dart b/lib/views/pages/splash/flutter_unit_text.dart deleted file mode 100644 index 0095d9830..000000000 --- a/lib/views/pages/splash/flutter_unit_text.dart +++ /dev/null @@ -1,113 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -class FlutterUnitText extends StatefulWidget { - final String text; - final Color color; - - FlutterUnitText({this.text = "Toly", this.color = Colors.blue, Key key}) - : super(key: key); - - @override - _FlutterUnitTextState createState() => _FlutterUnitTextState(); -} - -class _FlutterUnitTextState extends State - with SingleTickerProviderStateMixin { - - AnimationController _ctrl; - - final TextPainter _textPainter = - TextPainter(textDirection: TextDirection.ltr); - - Animation animation; - - @override - void initState() { - super.initState(); - TextSpan text = TextSpan( - text: widget.text, style: TextStyle(fontSize: 40, color: Colors.blue)); - _textPainter.text = text; - _textPainter.layout(); // 进行布局 - - _ctrl = AnimationController( - vsync: this, duration: const Duration(milliseconds: 800)); - animation = CurvedAnimation(parent: _ctrl, curve: const Interpolator()); - _ctrl.forward(); - } - - @override - void dispose() { - _ctrl.dispose(); - super.dispose(); - } - - @override - void didUpdateWidget(FlutterUnitText oldWidget) { - super.didUpdateWidget(oldWidget); - _ctrl.forward(); - } - - String msg = ''; - - @override - Widget build(BuildContext context) { - return CustomPaint( - size: _textPainter.size, - painter: SpringPainter( - textPainter: _textPainter, color: widget.color, skew: animation)); - } -} - -class Interpolator extends Curve { - const Interpolator(); - - @override - double transformInternal(double t) { - t -= 1.0; - return t * t * t * t * t + 1.0; - } -} - -class SpringPainter extends CustomPainter { - final ValueListenable skew; - - String _text = ''; - Color color = Colors.blue; - - SpringPainter({this.skew, this.textPainter, this.color}) - : super(repaint: skew) { - _text = textPainter.text.toPlainText(); - } - - final TextPainter textPainter; - - @override - void paint(Canvas canvas, Size size) { - canvas.translate(size.width / 2, size.height / 2); - TextSpan text = - TextSpan(text: _text, style: TextStyle(fontSize: 40, color: color)); - textPainter.text = text; - textPainter.layout(); // 进行布局 - Size textSize = textPainter.size; // 尺寸必须在布局后获取 - - canvas.save(); - canvas.translate(-textSize.width / 2, -textSize.height / 2); - textPainter.paint(canvas, Offset.zero); - TextSpan textShadow = TextSpan( - text: _text, - style: TextStyle(fontSize: 40, color: color.withAlpha(88))); - textPainter.text = textShadow; - textPainter.layout(); // 进行布局 - Matrix4 matrix4 = Matrix4.skewX((6 / 180 * pi) * skew.value); - canvas.transform(matrix4.storage); - textPainter.paint(canvas, Offset.zero); - canvas.restore(); - } - - @override - bool shouldRepaint(covariant SpringPainter oldDelegate) => - oldDelegate.textPainter != textPainter || oldDelegate.skew != skew; -} diff --git a/lib/views/pages/splash/splash_bottom.dart b/lib/views/pages/splash/splash_bottom.dart deleted file mode 100644 index 9fee64400..000000000 --- a/lib/views/pages/splash/splash_bottom.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'dart:async'; -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/res/style/unit_text_style.dart'; - -class SplashBottom extends StatefulWidget { - @override - _SplashBottomState createState() => _SplashBottomState(); -} - -class _SplashBottomState extends State { - bool _animStart = false; - - final Duration delayTime = const Duration(milliseconds: 600); - final Duration animTime = const Duration(milliseconds: 800); - final String logoPath = 'assets/images/icon_head.webp'; - - @override - void initState() { - super.initState(); - // 延迟 600 ms 后开启动画 - Future.delayed(delayTime).then(startAnim); - } - - FutureOr startAnim(value){ - setState(() { - _animStart = true; - }); - } - - @override - Widget build(BuildContext context) { - return AnimatedOpacity( - duration: animTime, - opacity: _animStart ? 1.0 : 0.0, - child: Wrap( - direction: Axis.vertical, - alignment: WrapAlignment.center, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - const Text("Power By 张风捷特烈", style: TStyleUnit.splashShadows), - const Text("· 2021 · @编程之王 ", style: TStyleUnit.splashShadows), - ], - )); - } -} diff --git a/lib/views/pages/splash/unit_paint.dart b/lib/views/pages/splash/unit_paint.dart deleted file mode 100644 index 103e57dfd..000000000 --- a/lib/views/pages/splash/unit_paint.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020-03-07 -/// contact me by email 1981462002@qq.com -/// 说明: app 闪屏页画板 - -class UnitPainter extends CustomPainter { - Paint _paint; - final double width; - Animation _curveAnim; - final Color color; - - final Path _path1 = Path(); - final Path _path2 = Path(); - final Path _path3 = Path(); - final Path _path4 = Path(); - - UnitPainter({this.width = 200.0, this.color = Colors.blue,Animation repaint}):super(repaint: repaint) { - _paint = Paint(); - _curveAnim= CurvedAnimation(parent: repaint, curve: Curves.fastOutSlowIn); - } - - @override - void paint(Canvas canvas, Size size) { - _path1.reset(); - _path2.reset(); - _path3.reset(); - _path4.reset(); - var factor = _curveAnim.value; - canvas.translate( - size.width / 2 - width * 0.5, size.height / 2 - width * 0.5); - - canvas.save(); - canvas.translate( - -size.width / 2 * (1 - factor), -size.width / 2 * (1 - factor)); - drawColor1(canvas); - canvas.restore(); - - canvas.save(); - canvas.translate( - size.width / 2 * (1 - factor), -size.width / 2 * (1 - factor)); - drawColor2(canvas); - canvas.restore(); - - canvas.save(); - canvas.translate( - size.width / 2 * (1 - factor), size.width / 2 * (1 - factor)); - drawColor3(canvas); - canvas.restore(); - - canvas.save(); - canvas.translate( - -size.width / 2 * (1 - factor), size.width / 2 * (1 - factor)); - drawColor4(canvas); - canvas.restore(); - } - - @override - bool shouldRepaint(UnitPainter oldDelegate) { - return - oldDelegate.color!=color|| - oldDelegate.width!=width|| - oldDelegate._curveAnim!=_curveAnim; - } - - void drawColor1(Canvas canvas) { - double factor = _curveAnim.value; - _path1.moveTo(0, 0); - _path1.lineTo(width * 0.618 * factor - 1, 0); - _path1.lineTo(width * 0.5 - 1, width * 0.5 - 1); - _path1.lineTo(0, width * (1 - 0.618) * factor - 1); - - canvas.drawPath(_clipAngle(_path1), _paint..color = Colors.red); - } - - void drawColor2(Canvas canvas) { - double factor = _curveAnim.value; - _path2.moveTo(width * 0.618 * factor, 0); - _path2.lineTo(width, 0); - _path2.lineTo(width, width * 0.618 * factor); - _path2.lineTo(width * 0.5, width * 0.5); - - canvas.drawPath(_clipAngle(_path2), _paint..color = Colors.blue); - } - - void drawColor3(Canvas canvas) { - double factor = _curveAnim.value; - _path3.moveTo(width * 0.5 + 1, width * 0.5 + 1); - _path3.lineTo(width, width * 0.618 * factor + 1); - _path3.lineTo(width, width); - _path3.lineTo(width * (1 - 0.618) * factor + 1, width); - canvas.drawPath(_clipAngle(_path3), _paint..color = Colors.green); - } - - void drawColor4(Canvas canvas) { - double factor = _curveAnim.value; - _path4.moveTo(0, width * (1 - 0.618) * factor); - _path4.lineTo(width * 0.5, width * 0.5); - _path4.lineTo(width * (1 - 0.618) * factor, width); - _path4.lineTo(0, width); - canvas.drawPath(_clipAngle(_path4), _paint..color = Colors.yellow); - } - - Path _clipAngle(Path path) { - return Path.combine( - PathOperation.difference, - path, - Path() - ..addOval(Rect.fromCircle( - center: Offset(width * 0.5, width * 0.5), radius: 25.0))); - } -} diff --git a/lib/views/pages/splash/unit_splash.dart b/lib/views/pages/splash/unit_splash.dart deleted file mode 100644 index 50d6cbc23..000000000 --- a/lib/views/pages/splash/unit_splash.dart +++ /dev/null @@ -1,146 +0,0 @@ -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/res/str_unit.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/model/enums.dart'; -import 'package:flutter_unit/views/pages/splash/splash_bottom.dart'; - -import 'flutter_unit_text.dart'; -import 'unit_paint.dart'; - -/// create by 张风捷特烈 on 2020-03-07 -/// contact me by email 1981462002@qq.com -/// 说明: app 闪屏页 - -class UnitSplash extends StatefulWidget { - @override - _UnitSplashState createState() => _UnitSplashState(); -} - -class _UnitSplashState extends State with TickerProviderStateMixin { - AnimationController _controller; - - ValueNotifier _animEnd = ValueNotifier(false); - - final Duration animTime = const Duration(milliseconds: 1000); - final Duration delayTime = const Duration(milliseconds: 500); - final Duration fadeInTime = const Duration(milliseconds: 600); - - @override - void initState() { - super.initState(); - - SystemUiOverlayStyle systemUiOverlayStyle = - const SystemUiOverlayStyle(statusBarColor: Colors.transparent); - SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); - - _controller = AnimationController(duration: animTime, vsync: this) - ..addStatusListener(_listenStatus) - ..forward(); - - Future.delayed(delayTime).then((e) { - _animEnd.value = true; - }); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - void _listenStatus(AnimationStatus status) { - if (status == AnimationStatus.completed) { - Future.delayed(delayTime).then((e) { - Navigator.of(context).pushReplacementNamed(UnitRouter.nav); - }); - } - } - - @override - Widget build(BuildContext context) { - final double winH = MediaQuery.of(context).size.height; - final double winW = MediaQuery.of(context).size.width; - - return BlocListener( - listener: _listenStart, - child: Scaffold( - body: Stack( - alignment: Alignment.center, - children: [ - _buildFlutterLogo(), - Container( - width: winW, - height: winH, - child: CustomPaint( - painter: UnitPainter(repaint: _controller), - ), - ), - _buildFlutterUnitText(winH, winW), - _buildHead(), - Positioned(bottom: 15, child: SplashBottom()) - ], - ), - ), - ); - } - - Widget _buildFlutterUnitText(double winH, double winW) { - - return Positioned( - top: winH / 1.4, - child: ValueListenableBuilder( - valueListenable: _animEnd, - builder: (_, value, __) => value - ? FlutterUnitText( - text: StrUnit.appName, - color: Theme.of(context).primaryColor, - ) - : SizedBox(), - ), - ); - } - - Widget _buildFlutterLogo() { - return SlideTransition( - position: Tween( - begin: const Offset(0, 0), - end: const Offset(0, -1.5), - ).animate(_controller), - child: RotationTransition( - turns: _controller, - child: ScaleTransition( - scale: Tween(begin: 2.0, end: 1.0).animate(_controller), - child: FadeTransition( - opacity: _controller, - child: Container( - height: 120, - child: const FlutterLogo( - size: 60, - ), - )), - )), - ); - } - - Widget _buildHead() => SlideTransition( - position: Tween( - end: const Offset(0, 0), - begin: const Offset(0, -5), - ).animate(_controller), - child: Container( - height: 45, - width: 45, - child: Image.asset('assets/images/icon_head.webp'), - )); - - // 监听资源加载完毕,启动,触发事件 - void _listenStart(BuildContext context, GlobalState state) { - BlocProvider.of(context).add(EventTabTap(WidgetFamily.statelessWidget)); - BlocProvider.of(context).add(EventLoadLikeData()); - BlocProvider.of(context).add(EventLoadCategory()); - } -} diff --git a/lib/views/pages/user/page_item.dart b/lib/views/pages/user/page_item.dart deleted file mode 100644 index 57d574972..000000000 --- a/lib/views/pages/user/page_item.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/res/style/behavior/no_scroll_behavior.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: - -class MePageItem extends StatelessWidget { - final Color color; - - MePageItem({this.color = Colors.white}); - - @override - Widget build(BuildContext context) { - return _buildChild(context); - } - - Widget _buildChild(BuildContext context) { - return ScrollConfiguration( - behavior: NoScrollBehavior(), - child: ListView( - padding: EdgeInsets.zero, - children: [ - SizedBox( - height: 10, - ), - _buildItem(context, TolyIcon.icon_them, '应用设置', UnitRouter.setting), - _buildItem(context, TolyIcon.icon_layout, '数据管理', UnitRouter.data_manage), - _buildItem(context, TolyIcon.icon_collect, '我的收藏', UnitRouter.collect), - Divider( - height: 1, - ), - _buildItem(context, Icons.update, '版本信息', UnitRouter.version_info), - _buildItem(context, Icons.info, '关于应用', UnitRouter.about_app), - Divider( - height: 1, - ), - _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), - trailing: - Icon(Icons.chevron_right, color: Theme.of(context).primaryColor), - onTap: () { - if (linkTo != null && linkTo.isNotEmpty) { - Navigator.of(context).pushNamed(linkTo); - if (onTap != null) onTap(); - } - }, - ); -} - diff --git a/lib/views/pages/user/unit_drawer_header.dart b/lib/views/pages/user/unit_drawer_header.dart deleted file mode 100644 index b94538329..000000000 --- a/lib/views/pages/user/unit_drawer_header.dart +++ /dev/null @@ -1,88 +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; - - - UnitDrawerHeader({this.color}); - - @override - Widget build(BuildContext context) { - return DrawerHeader( - padding: EdgeInsets.only(top: 10, left: 15), - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/images/login_bg.png'), - fit: BoxFit.cover), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Wrap( - spacing: 10, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - FlutterLogo( - // colors: Colors.orange, - size: 35, - ), - Text( - 'Flutter Unit', - style: TextStyle(fontSize: 24, color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(1, 1), - blurRadius: 3) - ]), - ), - ], - ), - SizedBox( - height: 15, - ), - Text( - 'The Unity Of Flutter, The Unity Of Coder.', - style: TextStyle(fontSize: 15, color: Colors.white, shadows: [ - Shadow(color: color, offset: Offset(.5, .5), blurRadius: 1) - ]), - ), - SizedBox( - height: 5, - ), - Text( - 'Flutter的联合,编程者的联合。', - style: TextStyle(fontSize: 15, color: Colors.white, shadows: [ - Shadow(color: color, offset: Offset(.5, .5), blurRadius: 1) - ]), - ), - SizedBox( - height: 10, - ), - 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/views/pages/user/user_page.dart b/lib/views/pages/user/user_page.dart deleted file mode 100644 index 3ec3e381a..000000000 --- a/lib/views/pages/user/user_page.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/user_system/bloc/authentic/bloc.dart'; -import 'package:flutter_unit/user_system/bloc/authentic/state.dart'; - -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/project/wrapper/honour_wrapper.dart'; - -import 'page_item.dart'; - -/// create by 张风捷特烈 on 2020/4/26 -/// contact me by email 1981462002@qq.com -/// 说明: - -class UserPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - body: Column( - children: [ - Stack( - children: [ - Container( - height: 180, - width: MediaQuery.of(context).size.width, - margin: EdgeInsets.only(bottom: 40), - child: Image.asset( - 'assets/images/sabar_bar.webp', - fit: BoxFit.cover, - ), - ), - Positioned( - bottom: 0, - left: 40, - child: BlocBuilder( - builder: _buildAvatarByState, - ), - ), - Positioned( - bottom: 0, - right: 30, - child: BlocBuilder( - builder: _buildByState, - )) - ], - ), - Expanded(child: MePageItem()) - ], - )); - } - - Widget _buildByState(BuildContext context, AuthenticState state) { - if (state is AuthSuccess) { - if (state.user.isHonour) { - return HonourWrapper(username: state.user.username); - } - - return Text( - state.user.username, - style: TextStyle(fontSize: 18, color: Theme.of(context).primaryColor), - ); - } - return Text( - '张风捷特烈', - style: TextStyle(fontSize: 18, color: Theme.of(context).primaryColor), - ); - } - - Widget _buildAvatarByState(BuildContext context, AuthenticState state) { - if (state is AuthSuccess) { - return FeedbackWidget( - onEnd: () { - // Navigator.of(context).pushNamed(UnitRouter.login); - }, - child: CircleImage( - size: 80, - shadowColor: Theme.of(context).primaryColor.withAlpha(33), - // image: NetworkImage(state.user.userAvatar), - image: AssetImage("assets/images/icon_head.webp"), - ), - ); - } - return FeedbackWidget( - onEnd: () { - // Navigator.of(context).pushNamed(UnitRouter.login); - }, - child: CircleImage( - size: 80, - shadowColor: Theme.of(context).primaryColor.withAlpha(33), - image: AssetImage("assets/images/icon_head.webp"), - ), - ); - } -} diff --git a/lib/views/pages/widget_detail/widget_detail_page.dart b/lib/views/pages/widget_detail/widget_detail_page.dart deleted file mode 100644 index f84712cf7..000000000 --- a/lib/views/pages/widget_detail/widget_detail_page.dart +++ /dev/null @@ -1,286 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_star/flutter_star.dart'; -import 'package:flutter_unit/app/res/cons.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/app/utils/Toast.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/model/node_model.dart'; -import 'package:flutter_unit/model/widget_model.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/permanent/panel.dart'; -import 'package:flutter_unit/views/components/project/widget_node_panel.dart'; -import 'package:flutter_unit/views/pages/widget_detail/category_end_drawer.dart'; -import 'package:flutter_unit/views/widgets/widgets_map.dart'; - -class WidgetDetailPage extends StatefulWidget { - final WidgetModel model; - - WidgetDetailPage({this.model}); - - @override - _WidgetDetailPageState createState() => _WidgetDetailPageState(); -} - -class _WidgetDetailPageState extends State { - List _modelStack = []; - - @override - void initState() { - _modelStack.add(widget.model); - super.initState(); - } - - // 获取当前的 组件数据模型 - WidgetModel get currentWidgetModel => _modelStack.last; - - @override - Widget build(BuildContext context) { - return Scaffold( - endDrawer: CategoryEndDrawer(widget: _modelStack.last), - appBar: AppBar( - title: Text(_modelStack.last.name), - actions: [ - _buildToHome(), - FeedbackWidget( - onPressed: () => _toggleLikeState(context), - child: BlocConsumer( - listener: _listenLikeStateChange, builder: _buildByLikeState), - ) - ], - ), - body: Builder(builder: _buildContent), - ); - } - - Widget _buildContent(BuildContext context) => WillPopScope( - onWillPop: () => _whenPop(context), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - WidgetDetailTitle( - model: _modelStack.last, - ), - BlocBuilder(builder: _buildDetail) - ], - ), - )); - - Widget _buildToHome() => Builder( - builder: (ctx) => GestureDetector( - onLongPress: () => Scaffold.of(ctx).openEndDrawer(), - child: const Padding( - padding: EdgeInsets.all(15.0), - child: Icon(Icons.home), - ), - onTap: () => Navigator.of(ctx).pop())); - - // 监听 LikeWidgetBloc 伺机弹出 toast - void _listenLikeStateChange(BuildContext context, LikeWidgetState state) { - bool collected = state.widgets.contains(currentWidgetModel); - String msg = collected - ? "收藏【${currentWidgetModel.name}】组件成功!" - : "已取消【${currentWidgetModel.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(currentWidgetModel); - return Padding( - padding: const EdgeInsets.only(right: 20.0), - child: Icon( - liked ? TolyIcon.icon_star_ok : TolyIcon.icon_star_add, - size: 25, - ), - ); - } - - - void _toggleLikeState(BuildContext context) { - BlocProvider.of(context) - .add(ToggleLikeWidgetEvent(id: currentWidgetModel.id)); - } - - Widget _buildNodes(List nodes, String name) { - GlobalState globalState = BlocProvider.of(context).state; - return Column( - children: nodes - .asMap() - .keys - .map((i) => WidgetNodePanel( - codeStyle: Cons.codeThemeSupport.keys - .toList()[globalState.codeStyleIndex], - codeFamily: 'Inconsolata', - text: nodes[i].name, - subText: nodes[i].subtitle, - code: nodes[i].code, - show: WidgetsMap.map(name)[i], - )) - .toList()); - } - - Future _whenPop(BuildContext context) async { - if (Scaffold.of(context).isEndDrawerOpen) return true; - _modelStack.removeLast(); - if (_modelStack.length > 0) { - setState(() { - BlocProvider.of(context).add(FetchWidgetDetail(_modelStack.last)); - }); - return false; - } else { - return true; - } - } - - Widget _buildDetail(BuildContext context, DetailState state) { - print('build---${state.runtimeType}---'); - if (state is DetailWithData) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - const Padding( - padding: EdgeInsets.only(left: 15, right: 5), - child: Icon( - Icons.link, - color: Colors.blue, - ), - ), - const Text( - '相关组件', - style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), - ), - ], - ), - _buildLinkTo( - context, - state.links, - ), - const Divider(), - _buildNodes(state.nodes, state.widgetModel.name) - ], - ); - } - return Container(); - } - - _buildLinkTo(BuildContext context, List links) { - if (links == null || links.isEmpty) { - return Padding( - padding: EdgeInsets.only(left: 10), - child: Chip( - backgroundColor: Colors.grey.withAlpha(120), - labelStyle: TextStyle(fontSize: 12, color: Colors.white), - label: Text('暂无链接组件'), - )); - } else { - return Padding( - padding: const EdgeInsets.only(left: 10.0, top: 10), - child: Wrap( - spacing: 5, - children: links - .map((e) => ActionChip( - onPressed: () { - BlocProvider.of(context).add(FetchWidgetDetail(e)); - setState(() { - _modelStack.add(e); - }); - }, - elevation: 2, - shadowColor: Colors.orange, - backgroundColor: Theme.of(context).primaryColor, - labelStyle: TextStyle(fontSize: 12, color: Colors.white, - decoration: (e.deprecated)?TextDecoration.lineThrough:TextDecoration.none, - decorationThickness: 2,), - label: Text('${e.name}'), - )) - .toList(), - ), - ); - } - } -} - -class WidgetDetailTitle extends StatelessWidget { - final WidgetModel model; - - WidgetDetailTitle({this.model}); - - @override - Widget build(BuildContext context) { - return Container( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - _buildLeft(model), - _buildRight(model), - ], - ), - const Divider(), - ], - )); - } - - Widget _buildLeft(WidgetModel model) => Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(top: 20.0, left: 20), - child: Text( - model.nameCN, - style: TextStyle( - fontSize: 20, - decoration: (model.deprecated)?TextDecoration.lineThrough:TextDecoration.none, - decorationThickness: 2, - color: Color(0xff1EBBFD), - fontWeight: FontWeight.bold), - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Panel(child: Text(model.info)), - ) - ], - ), - ); - - Widget _buildRight(WidgetModel model) => Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - height: 100, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Hero( - tag: "hero_widget_image_${model.id}", - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(8)), - child: model.image == null - ? Image.asset('assets/images/caver.webp') - : Image(image: model.image))), - ), - ), - StarScore( - score: model.lever, - star: Star(size: 15, fillColor: Colors.blue), - ) - ], - ); -} diff --git a/lib/views/pages/widget_home/background.dart b/lib/views/pages/widget_home/background.dart deleted file mode 100644 index 10385399e..000000000 --- a/lib/views/pages/widget_home/background.dart +++ /dev/null @@ -1,19 +0,0 @@ -import 'package:flutter/material.dart'; - -class BackgroundShower extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Opacity( - opacity: 0.05, - child: Container( - decoration: const BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/images/sabar.webp'), - fit: BoxFit.cover), - borderRadius: BorderRadius.only( - bottomRight: Radius.circular(400), - topLeft: Radius.circular(400))), - ), - ); - } -} diff --git a/lib/views/pages/widget_home/home_drawer.dart b/lib/views/pages/widget_home/home_drawer.dart deleted file mode 100644 index 34b53adab..000000000 --- a/lib/views/pages/widget_home/home_drawer.dart +++ /dev/null @@ -1,82 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/app/res/toly_icon.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/blocs/point/point_bloc.dart'; -import 'package:flutter_unit/blocs/point/point_event.dart'; -import 'package:flutter_unit/views/components/flutter/no_div_expansion_tile.dart'; -import 'package:flutter_unit/views/components/project/unit_drawer_header.dart'; - -/// create by 张风捷特烈 on 2020-03-26 -/// contact me by email 1981462002@qq.com -/// 说明: - -class HomeDrawer extends StatelessWidget { - - @override - Widget build(BuildContext context) { - return Drawer( - elevation: 3, - child: _buildChild(context), - ); - } - - Widget _buildChild(BuildContext context) { - - final Color color = BlocProvider.of(context).state.color; - - return Container( - color: color.withAlpha(33), - child: ListView( - padding: EdgeInsets.zero, - children: [ - UnitDrawerHeader(color: color), - _buildItem(context, TolyIcon.icon_them, '应用设置', UnitRouter.setting), - _buildItem(context, TolyIcon.icon_layout, '数据管理', UnitRouter.data_manage), - Divider(height: 1), - _buildFlutterUnit(context), - _buildItem(context, TolyIcon.icon_code, 'Dart 手册', null), - Divider(height: 1), - _buildItem(context, Icons.info, '关于应用', UnitRouter.about_app), - _buildItem(context, TolyIcon.icon_kafei, '联系本王', UnitRouter.about_me), - ], - ), - ); - } - - Widget _buildFlutterUnit(BuildContext context) => NoBorderExpansionTile( - backgroundColor: Colors.white70, - leading: Icon( - Icons.extension, - color: Theme.of(context).primaryColor, - ), - title: const Text('Flutter 集录'), - children: [ - _buildItem(context, TolyIcon.icon_tag, '属性集录', UnitRouter.attr), - _buildItem(context, Icons.palette, '绘画集录', UnitRouter.galley), - _buildItem(context, Icons.widgets, '布局集录', UnitRouter.layout), - _buildItem(context, TolyIcon.icon_bug, '要点集录', UnitRouter.issues_point,onTap: (){ - BlocProvider.of(context).add(EventLoadPoint()); - }), - ], - ); - - Widget _buildItem( - BuildContext context, IconData icon, String title, String linkTo,{VoidCallback onTap}) => - ListTile( - leading: Icon( - icon, - color: Theme.of(context).primaryColor, - ), - title: Text(title), - trailing: - Icon(Icons.chevron_right, color: Theme.of(context).primaryColor), - onTap: () { - if (linkTo != null && linkTo.isNotEmpty) { - Navigator.of(context).pushNamed(linkTo); - if(onTap!=null) onTap(); - } - }, - ); -} diff --git a/lib/views/pages/widget_home/home_page.dart b/lib/views/pages/widget_home/home_page.dart deleted file mode 100644 index 13b7e6a63..000000000 --- a/lib/views/pages/widget_home/home_page.dart +++ /dev/null @@ -1,169 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/app/utils/convert.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/model/widget_model.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/project/default/empty_shower.dart'; -import 'package:flutter_unit/views/components/project/default/error_shower.dart'; -import 'package:flutter_unit/views/components/project/default/loading_shower.dart'; -import 'package:flutter_unit/views/components/project/items/widget/home_item_support.dart'; -import 'package:flutter_unit/views/components/project/no_more_widget.dart'; -import 'package:flutter_unit/views/components/project/overlay_tool_wrapper.dart'; -import 'package:flutter_unit/views/pages/widget_home/toly_app_bar.dart'; - -import 'background.dart'; - -class HomePage extends StatefulWidget { - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State - with AutomaticKeepAliveClientMixin { - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback(_onFrameCallBack); - } - - void _onFrameCallBack(Duration timeStamp) { - OverlayToolWrapper.of(context).showFloating(); - } - - @override - Widget build(BuildContext context) { - super.build(context); - - return Scaffold( - body: Stack( - children: [ - BlocBuilder(builder: _buildBackground), - BlocBuilder( - builder: (_, state) => CustomScrollView( - slivers: [ - _buildPersistentHeader(), - _buildContent(state), - const SliverToBoxAdapter( - child: NoMoreWidget(), - ) - ], - )) - ], - )); - } - - Widget _buildPersistentHeader() => SliverPersistentHeader( - pinned: true, - delegate: FlexHeaderDelegate( - minHeight: 35 + 56.0, - maxHeight: 120.0, - childBuilder: (offset, max, min) { - double dy = max - 25 - offset; - if (dy < min - 25) { - dy = min - 25; - } - return TolyAppBar( - maxHeight: dy, - onItemClick: _switchTab, - ); - })); - - Widget _buildBackground(BuildContext context, GlobalState state) { - if (state.showBackGround) { - return BackgroundShower(); - } - return Container(); - } - - Widget _buildContent(WidgetsState state) { - if (state is WidgetsLoading) { - return const SliverFillRemaining( - child: LoadingShower(), - ); - } - - if (state is WidgetsLoaded) { - List items = state.widgets; - if (items.isEmpty) - return const SliverFillRemaining( - child: EmptyShower( - message: "没数据,哥也没办法\n(≡ _ ≡)/~┴┴", - ), - ); - return SliverList( - delegate: SliverChildBuilderDelegate( - (_, int index) => _buildHomeItem(items[index]), - childCount: items.length), - ); - } - - if (state is WidgetsLoadFailed) { - return SliverFillRemaining( - child: ErrorShower( - error: "数据加载异常:\n${state.error}", - )); - } - return Container(); - } - - Widget _buildHomeItem(WidgetModel model) => - BlocBuilder( - buildWhen: (p, c) => (p.itemStyleIndex != c.itemStyleIndex), - builder: (_, state) { - return FeedbackWidget( - a: 0.95, - duration: const Duration(milliseconds: 200), - onEnd: () => _toDetailPage(model), - child: HomeItemSupport.get(model, state.itemStyleIndex)); - }, - ); - - _switchTab(int index) { - BlocProvider.of(context) - .add(EventTabTap(Convert.toFamily(index))); - } - - _toDetailPage(WidgetModel model) { - BlocProvider.of(context).add(FetchWidgetDetail(model)); - Navigator.pushNamed(context, UnitRouter.widget_detail, arguments: model); - } - - @override - bool get wantKeepAlive => true; -} - -class FlexHeaderDelegate extends SliverPersistentHeaderDelegate { - FlexHeaderDelegate({ - @required this.minHeight, - @required this.maxHeight, - @required this.childBuilder, - }); - - final double minHeight; //最小高度 - final double maxHeight; //最大高度 - final Widget Function(double offset, double max, double min) - childBuilder; //最大高度 - - @override - double get minExtent => minHeight; - - @override - double get maxExtent => max(maxHeight, minHeight); - - @override - Widget build( - BuildContext context, double shrinkOffset, bool overlapsContent) { - return childBuilder(shrinkOffset, maxHeight, minHeight); - } - - @override //是否需要重建 - bool shouldRebuild(FlexHeaderDelegate oldDelegate) { - return maxHeight != oldDelegate.maxHeight || - minHeight != oldDelegate.minHeight; - } -} diff --git a/lib/views/pages/widget_home/toly_app_bar.dart b/lib/views/pages/widget_home/toly_app_bar.dart deleted file mode 100644 index 0c5f8ed74..000000000 --- a/lib/views/pages/widget_home/toly_app_bar.dart +++ /dev/null @@ -1,196 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/permanent/circle.dart'; - - -class TolyAppBar extends StatefulWidget { - final double maxHeight; - final Function(int) onItemClick; - - @override - _TolyAppBarState createState() => _TolyAppBarState(); - - final int defaultIndex; - - TolyAppBar({this.maxHeight, this.onItemClick, this.defaultIndex = 0}); -} - -const BorderRadius _kBorderRadius = BorderRadius.only( - bottomLeft: Radius.circular(15), - bottomRight: Radius.circular(15), -); - -const TextStyle _kTabTextStyle = TextStyle(color: Colors.white, shadows: [ - Shadow(color: Colors.black, offset: Offset(0.5, 0.5), blurRadius: 0.5) -]); - -class _TolyAppBarState extends State - with SingleTickerProviderStateMixin { - double _width; - int _selectIndex = 0; - int _prevSelectIndex = 0; - - static const List colors = [ - 0xff44D1FD, - 0xffFD4F43, - 0xffB375FF, - 0xFF4CAF50, - 0xFFFF9800, - 0xFF00F1F1, - 0xFFDBD83F - ]; - - static const List info = [ - 'Stles', - 'Stful', - 'Scrow', - 'Mcrow', - 'Sliver', - 'Proxy', - 'Other' - ]; - - AnimationController _controller; - Animation circleAnim; - Animation heightAnim; - Animation backCircleAnim; - - @override - void initState() { - super.initState(); - _controller = AnimationController( - value: 1, - duration: const Duration(milliseconds: 300), - vsync: this, - ); - circleAnim = circleTween.animate(_controller); - heightAnim = CurveTween(curve: Curves.ease).animate(_controller); - backCircleAnim = ReverseAnimation(circleAnim); - - _selectIndex = widget.defaultIndex; - } - - int get nextIndex => (_selectIndex + 1) % colors.length; - - Tween circleTween = Tween(begin: 1, end: 0); - - @override - Widget build(BuildContext context) { - _width = _width ?? MediaQuery.of(context).size.width / colors.length; - return Center( - child:Flow( - delegate: TolyAppBarDelegate( - _selectIndex, - _prevSelectIndex, - widget.maxHeight, - repaint: heightAnim, - ), - children: [ - ...colors - .map((e) => GestureDetector( - onTap: () => _onTap(e), - child: _buildChild(e), - )) - .toList(), - ...colors.map((e) { - Widget child = Circle( - color: Color(e), - radius: 6, - ); - if (e == colors[_selectIndex]) { - return ScaleTransition(scale: circleAnim, child: child); - } - if (e == colors[_prevSelectIndex]) { - return ScaleTransition(scale: backCircleAnim, child: child); - } - return child; - }) - ]), - ); - } - - Widget _buildChild(int color) => Container( - alignment: const Alignment(0, 0.4), - decoration: BoxDecoration(boxShadow: [ - BoxShadow( - color: _selectIndex == colors.indexOf(color) - ? Colors.transparent - : Color(colors[_selectIndex]), - offset: const Offset(1, 1), - blurRadius: 2) - ], color: Color(color), borderRadius: _kBorderRadius), - height: widget.maxHeight + 20, - width: _width, - child: Text( - info[colors.indexOf(color)], - style: _kTabTextStyle, - ), - ); - - void _onTap(int color) { - if (_selectIndex == colors.indexOf(color)) return; - setState(() { - _controller.reset(); - _controller.forward(); - _prevSelectIndex = _selectIndex; - _selectIndex = colors.indexOf(color); - if (widget.onItemClick != null) widget.onItemClick(_selectIndex); - }); - } - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } -} - -class TolyAppBarDelegate extends FlowDelegate { - final int selectIndex; - final int prevSelectIndex; - final double height; - final Animation repaint; - - TolyAppBarDelegate(this.selectIndex, this.prevSelectIndex, this.height, - {this.repaint}) - : super(repaint: repaint); - - double get factor => repaint.value; - - @override - void paintChildren(FlowPaintingContext context) { - double ox = 0; - double obx = 0; - - for (int i = 0; i < context.childCount / 2; i++) { - Size cSize = context.getChildSize(i); - if (i == selectIndex) { - context.paintChild(i, - transform: Matrix4.translationValues(ox, 20.0 * factor - 20, 0.0)); - ox += cSize.width; - } else if (i == prevSelectIndex) { - context.paintChild(i, - transform: - Matrix4.translationValues(ox, 20.0 * (1 - factor) - 20, 0.0)); - ox += cSize.width; - } else { - context.paintChild(i, - transform: Matrix4.translationValues(ox, -20, 0.0)); - ox += cSize.width; - } - } - - // 绘制小点 - for (int i = (context.childCount / 2).floor(); i < context.childCount; i++) { - context.paintChild(i, - transform: Matrix4.translationValues( - obx + context.getChildSize(0).width / 2 - 5, height + 5, 0)); - obx += context.getChildSize(0).width; - } - } - - @override - bool shouldRepaint(TolyAppBarDelegate oldDelegate) => - oldDelegate.selectIndex != selectIndex || - oldDelegate.height != height || - oldDelegate.repaint != repaint; -} diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/Column/node1_base.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Column/node1_base.dart deleted file mode 100644 index 8af2ac7c2..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Column/node1_base.dart +++ /dev/null @@ -1,67 +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 { - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildTitle(), - _buildContent(context), - ], - ); - } - - Widget _buildTitle() { - return Container( - height: 70, - color: Color(0x4484FFFF), - child: Row( - children: [ - 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: Icon( - Icons.android, - size: 50, - color: Colors.white, - ), - ); -} diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/CustomMultiChildLayout/node1_base.dart b/lib/views/widgets/MultiChildRenderObjectWidget/CustomMultiChildLayout/node1_base.dart deleted file mode 100644 index 95d83f3d3..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/CustomMultiChildLayout/node1_base.dart +++ /dev/null @@ -1,92 +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 { - @override - Widget build(BuildContext context) { - return Container( - width: 300, - height: 150, - color: Colors.grey.withAlpha(33), - child: CustomMultiChildLayout( - delegate: CornerCustomMultiChildLayout( - padding:EdgeInsets.only(left: 10,top: 5,right: 10,bottom: 5), - ), - children: [ - LayoutId(id: CornerType.topLeft, child: Box50(Colors.red)), - LayoutId(id: CornerType.topRight, child: Box50(Colors.yellow)), - LayoutId(id: CornerType.bottomLeft, child: Box50(Colors.blue)), - LayoutId(id: CornerType.bottomRight, child: Box50(Colors.green)), - ], - ), - ); - } -} - -// 50 颜射盒 -class Box50 extends StatelessWidget { - final Color color; - Box50(this.color); - - @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/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node1_direction.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node1_direction.dart deleted file mode 100644 index 6745f7d17..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node1_direction.dart +++ /dev/null @@ -1,57 +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 { - - final redBox= Container( - color: Colors.red, - height: 30, - width: 40, - ); - - final blueBox= Container( - color: Colors.blue, - height: 20, - width: 30, - ); - - final greenBox= Container( - color: Colors.green, - height: 20, - width: 20, - ); - - @override - Widget build(BuildContext context) { - return Wrap( - children: Axis.values - .map((mode) => Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 160, - height: 80, - color: Colors.grey.withAlpha(33), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - _buildItem(mode) => Flex( - direction: mode, - children: [ - blueBox, redBox, greenBox - ], - ); -} \ No newline at end of file diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node2_mainAxisAlignment.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node2_mainAxisAlignment.dart deleted file mode 100644 index 94e1f1307..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node2_mainAxisAlignment.dart +++ /dev/null @@ -1,58 +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 { - - final redBox= Container( - color: Colors.red, - height: 30, - width: 40, - ); - - final blueBox= Container( - color: Colors.blue, - height: 20, - width: 30, - ); - - final greenBox= Container( - color: Colors.green, - height: 20, - width: 20, - ); - - @override - Widget build(BuildContext context) { - return Wrap( - runSpacing: 5, - children: MainAxisAlignment.values - .map((mode) => Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 160, - height: 80, - color: Colors.grey.withAlpha(33), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - _buildItem(mode) => Flex( - direction: Axis.horizontal, - mainAxisAlignment: mode, - children: [ - blueBox, redBox, greenBox - ], - ); -} \ No newline at end of file diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node3_crossAxisAlignment.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node3_crossAxisAlignment.dart deleted file mode 100644 index e5570db83..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node3_crossAxisAlignment.dart +++ /dev/null @@ -1,59 +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 { - - final redBox= Container( - color: Colors.red, - height: 30, - width: 40, - ); - - final blueBox= Container( - color: Colors.blue, - height: 20, - width: 30, - ); - - final greenBox= Container( - color: Colors.green, - height: 20, - width: 20, - ); - - @override - Widget build(BuildContext context) { - return Wrap( - runSpacing: 5, - children: CrossAxisAlignment.values - .map((mode) => Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 160, - height: 80, - color: Colors.grey.withAlpha(33), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - _buildItem(mode) => Flex( - direction: Axis.horizontal, - crossAxisAlignment: mode, - textBaseline: TextBaseline.alphabetic, - children: [ - blueBox, redBox, greenBox - ], - ); -} \ No newline at end of file diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node4_verticalDirection.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node4_verticalDirection.dart deleted file mode 100644 index 2a6f4f68c..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node4_verticalDirection.dart +++ /dev/null @@ -1,58 +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 { - - final redBox= Container( - color: Colors.red, - height: 30, - width: 40, - ); - - final blueBox= Container( - color: Colors.blue, - height: 20, - width: 30, - ); - - final greenBox= Container( - color: Colors.green, - height: 20, - width: 20, - ); - - @override - Widget build(BuildContext context) { - return Wrap( - runSpacing: 5, - children: VerticalDirection.values - .map((mode) => Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 160, - height: 80, - color: Colors.grey.withAlpha(33), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - _buildItem(mode) => Flex( - direction: Axis.vertical, - verticalDirection: mode, - children: [ - blueBox, redBox, greenBox - ], - ); -} diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node5_textDirection.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node5_textDirection.dart deleted file mode 100644 index d5c1a77b0..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Flex/node5_textDirection.dart +++ /dev/null @@ -1,58 +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 { - - final redBox= Container( - color: Colors.red, - height: 30, - width: 40, - ); - - final blueBox= Container( - color: Colors.blue, - height: 20, - width: 30, - ); - - final greenBox= Container( - color: Colors.green, - height: 20, - width: 20, - ); - - @override - Widget build(BuildContext context) { - return Wrap( - runSpacing: 5, - children: TextDirection.values - .map((mode) => Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 160, - height: 80, - color: Colors.grey.withAlpha(33), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - _buildItem(mode) => Flex( - direction: Axis.horizontal, - textDirection: mode, - children: [ - blueBox, redBox, greenBox - ], - ); -} \ No newline at end of file diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/Flex/zz_node_play.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Flex/zz_node_play.dart deleted file mode 100644 index 3f6e49a96..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Flex/zz_node_play.dart +++ /dev/null @@ -1,186 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -class PlayFlex extends StatefulWidget { - @override - _PlayFlexState createState() => _PlayFlexState(); -} - -class _PlayFlexState extends State { - final redBox = Container( - color: Colors.red, - height: 50, - width: 50, - ); - final blueBox = Container( - color: Colors.blue, - width: 60, - height: 60, - ); - final yellowBox = Container( - color: Colors.yellow, - height: 10, - width: 10, - ); - final greenBox = Container( - color: Colors.green, - height: 30, - width: 20, - ); - var _direction = Axis.horizontal; - var _mainAxisAlignment = MainAxisAlignment.start; - var _crossAxisAlignment = CrossAxisAlignment.center; - var _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: [ - 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; - }); - }), - ], - ), - ); - } - - Widget _buildMainAxisAlignmentSelector() { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - 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; - }); - }), - ], - ), - ); - } - - Widget _buildCrossAxisAlignmentSelector() { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - 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; - }); - }), - ], - ), - ); - } - - Widget _buildVerticalDirectionSelector() { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - 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; - }); - }), - ], - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/Flow/node1_base.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Flow/node1_base.dart deleted file mode 100644 index 3b41103c6..000000000 --- a/lib/views/widgets/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 { - final 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) { - double radius = context.size.shortestSide / 2; - print(context.getChildSize(0)); - var count = context.childCount; - var perRad = 2 * pi / count; - for (int i = 0; i < count; i++) { - var cSizeX = context.getChildSize(i).width / 2; - var cSizeY = context.getChildSize(i).height / 2; - - var offsetX = (radius - cSizeX) * cos(i * perRad) + radius; - var offsetY = (radius - cSizeY) * sin(i * perRad) + radius; - context.paintChild(i, - transform: Matrix4.translationValues( - offsetX - cSizeX, offsetY - cSizeY, 0.0)); - } - } - - @override - bool shouldRepaint(FlowDelegate oldDelegate) { - return true; - } -} diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/Flow/node2_anim.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Flow/node2_anim.dart deleted file mode 100644 index 46bd52632..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Flow/node2_anim.dart +++ /dev/null @@ -1,116 +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: CircleAvatar( - backgroundImage: AssetImage('assets/images/icon_head.webp'), - ))); - - final List children; - final Widget menu; - - BurstFlow({@required this.children, @required this.menu}); - - @override - _BurstFlowState createState() => _BurstFlowState(); -} - -class _BurstFlowState extends State - with SingleTickerProviderStateMixin { - AnimationController _controller; - double _rad = 0.0; - bool _closed = true; - - @override - void initState() { - _controller = AnimationController( - duration: 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; - var count = context.childCount - 1; - var perRad = 2 * pi / count; - for (int i = 0; i < count; i++) { - print(i); - var cSizeX = context.getChildSize(i).width / 2; - var cSizeY = context.getChildSize(i).height / 2; - var offsetX = rad * (radius - cSizeX) * cos(i * perRad) + radius; - var offsetY = rad * (radius - cSizeY) * sin(i * perRad) + radius; - context.paintChild(i, - transform: Matrix4.translationValues( - offsetX - cSizeX, offsetY - cSizeY, 0.0)); - } - context.paintChild(context.childCount - 1, - transform: Matrix4.translationValues( - radius - context.getChildSize(context.childCount - 1).width / 2, - radius - context.getChildSize(context.childCount - 1).height / 2, - 0.0)); - } - - @override - bool shouldRepaint(FlowDelegate oldDelegate) { - return true; - } -} diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/IndexedStack/node1_base.dart b/lib/views/widgets/MultiChildRenderObjectWidget/IndexedStack/node1_base.dart deleted file mode 100644 index 28a0600b7..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/IndexedStack/node1_base.dart +++ /dev/null @@ -1,61 +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 { - @override - _CustomIndexedStackState createState() => _CustomIndexedStackState(); -} - -class _CustomIndexedStackState extends State { - var _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/lib/views/widgets/MultiChildRenderObjectWidget/ListBody/node1_base.dart b/lib/views/widgets/MultiChildRenderObjectWidget/ListBody/node1_base.dart deleted file mode 100644 index 65086b213..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/ListBody/node1_base.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.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 { - - @override - Widget build(BuildContext context) { - return Container( - 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/lib/views/widgets/MultiChildRenderObjectWidget/NestedScrollViewViewport/node1_base.dart b/lib/views/widgets/MultiChildRenderObjectWidget/NestedScrollViewViewport/node1_base.dart deleted file mode 100644 index c71b92700..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/NestedScrollViewViewport/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 -/// 说明: 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 { - 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: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/RichText/node1_base.dart b/lib/views/widgets/MultiChildRenderObjectWidget/RichText/node1_base.dart deleted file mode 100644 index 5aa4a7832..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/RichText/node1_base.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../../app/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 { - 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/lib/views/widgets/MultiChildRenderObjectWidget/RichText/node2_widget.dart b/lib/views/widgets/MultiChildRenderObjectWidget/RichText/node2_widget.dart deleted file mode 100644 index 9f1b11096..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/RichText/node2_widget.dart +++ /dev/null @@ -1,56 +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 { - @override - Widget build(BuildContext context) { - return RichText( - text: TextSpan( - text: 'hello ', - style: TextStyle(color: Colors.black, fontSize: 18), - children: [ - WidgetSpan( - child: Image.asset( - 'assets/images/icon_head.webp', - width: 30, - ), - alignment: PlaceholderAlignment.baseline, - baseline: TextBaseline.ideographic), - TextSpan( - text: ' , welcome to ', - style: TextStyle(color: Colors.blue, fontSize: 18), - ), - WidgetSpan( - child: FlutterLogo(), - alignment: PlaceholderAlignment.baseline, - baseline: TextBaseline.ideographic), - TextSpan( - text: ' .\n', - ), - TextSpan( - text: 'focus me on ', - style: TextStyle(color: Colors.orange, fontSize: 16), - ), - TextSpan( - text: 'https://github.com/toly1994328', - style: TextStyle( - color: Colors.blue, - fontSize: 18, - decoration: TextDecoration.underline), - ), - TextSpan( - text: ' .\n', - ), - ], - ), - ); - } -} diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/Row/node1_base.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Row/node1_base.dart deleted file mode 100644 index 2732a51a5..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Row/node1_base.dart +++ /dev/null @@ -1,47 +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 { - @override - Widget build(BuildContext context) { - return Container( - height: 70, - color: Color(0x4484FFFF), - child: Row( - children: [ - 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/lib/views/widgets/MultiChildRenderObjectWidget/ShrinkWrappingViewport/node1_base.dart b/lib/views/widgets/MultiChildRenderObjectWidget/ShrinkWrappingViewport/node1_base.dart deleted file mode 100644 index 74cb02370..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/ShrinkWrappingViewport/node1_base.dart +++ /dev/null @@ -1,36 +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 { - 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: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/Stack/node1_base.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Stack/node1_base.dart deleted file mode 100644 index ce16eaf56..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Stack/node1_base.dart +++ /dev/null @@ -1,56 +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 { - @override - Widget build(BuildContext context) { - var yellowBox = Container( - color: Colors.yellow, - height: 100, - width: 100, - ); - - var redBox = Container( - color: Colors.red, - height: 90, - width: 90, - ); - - var greenBox = Container( - color: Colors.green, - height: 80, - width: 80, - ); - - var 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/lib/views/widgets/MultiChildRenderObjectWidget/Stack/node2_positioned.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Stack/node2_positioned.dart deleted file mode 100644 index b1d6feb45..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Stack/node2_positioned.dart +++ /dev/null @@ -1,53 +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 { - @override - Widget build(BuildContext context) { - var yellowBox = Container( - color: Colors.yellow, - height: 100, - width: 100, - ); - - var redBox = Container( - color: Colors.red, - height: 90, - width: 90, - ); - - var greenBox = Container( - color: Colors.green, - height: 80, - width: 80, - ); - - var 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/lib/views/widgets/MultiChildRenderObjectWidget/Viewport/node1_base.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Viewport/node1_base.dart deleted file mode 100644 index 3da7d8d1c..000000000 --- a/lib/views/widgets/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 { - final data = List.generate(128, (i) => Color(0xFF6600FF - 2 * i)); - - @override - Widget build(BuildContext context) { - return Container( - height: 250, - child: Scrollable( - axisDirection: AxisDirection.down, - physics: 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; - - ColorItem({Key key,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: EdgeInsets.only(top: 1), - alignment: Alignment.center, - width: 100, - height: 60, - color: widget.color, - child: Text( - colorString(widget.color), - style: 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/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node1_base.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node1_base.dart deleted file mode 100644 index 0640e841e..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node1_base.dart +++ /dev/null @@ -1,77 +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 { - @override - Widget build(BuildContext context) { - return Wrap( - children: Axis.values - .map((mode) => Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 160, - height: 100, - color: Colors.grey.withAlpha(33), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - final yellowBox = Container( - color: Colors.yellow, - height: 30, - width: 50, - ); - - final redBox = Container( - color: Colors.red, - height: 40, - width: 40, - ); - final greenBox = Container( - color: Colors.green, - height: 40, - width: 20, - ); - final blackBox = Container( - color: Colors.black, - height: 10, - width: 10, - ); - final purpleBox = Container( - color: Colors.purple, - height: 20, - width: 20, - ); - final orangeBox = Container( - color: Colors.orange, - height: 80, - width: 20, - ); - final cyanBox = Container( - color: Colors.cyanAccent, - height: 10, - width: 20, - ); - - _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/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node2_alignment.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node2_alignment.dart deleted file mode 100644 index e30a11e2a..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node2_alignment.dart +++ /dev/null @@ -1,78 +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 { - @override - Widget build(BuildContext context) { - return Wrap( - children: WrapAlignment.values - .map((mode) => Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 160, - height: 100, - color: Colors.grey.withAlpha(88), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - final yellowBox = Container( - color: Colors.yellow, - height: 30, - width: 50, - ); - - final redBox = Container( - color: Colors.red, - height: 40, - width: 40, - ); - final greenBox = Container( - color: Colors.green, - height: 40, - width: 20, - ); - final blackBox = Container( - color: Colors.black, - height: 10, - width: 10, - ); - final purpleBox = Container( - color: Colors.purple, - height: 20, - width: 20, - ); - final orangeBox = Container( - color: Colors.orange, - height: 80, - width: 20, - ); - final cyanBox = Container( - color: Colors.cyanAccent, - height: 10, - width: 20, - ); - - _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/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node3_crossAxisAlignment.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node3_crossAxisAlignment.dart deleted file mode 100644 index 00ddee910..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/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": 98, -// "name": 'Wrap的crossAxisAlignment属性', -// "priority": 3, -// "subtitle": -// "【crossAxisAlignment】 : 交叉轴对齐 【CrossAxisAlignment】", -// } -class CrossAxisAlignmentWrap extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Wrap( - children: WrapCrossAlignment.values - .map((mode) => Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 160, - height: 100, - color: Colors.grey.withAlpha(88), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - final yellowBox = Container( - color: Colors.yellow, - height: 30, - width: 50, - ); - - final redBox = Container( - color: Colors.red, - height: 40, - width: 40, - ); - final greenBox = Container( - color: Colors.green, - height: 40, - width: 20, - ); - final blackBox = Container( - color: Colors.black, - height: 10, - width: 10, - ); - final purpleBox = Container( - color: Colors.purple, - height: 20, - width: 20, - ); - final orangeBox = Container( - color: Colors.orange, - height: 80, - width: 20, - ); - final cyanBox = Container( - color: Colors.cyanAccent, - height: 10, - width: 20, - ); - - _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/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node4_textDirection.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node4_textDirection.dart deleted file mode 100644 index a3bfd3c96..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node4_textDirection.dart +++ /dev/null @@ -1,77 +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 { - @override - Widget build(BuildContext context) { - return Wrap( - children: TextDirection.values - .map((mode) => Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 160, - height: 100, - color: Colors.grey.withAlpha(88), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - final yellowBox = Container( - color: Colors.yellow, - height: 30, - width: 50, - ); - - final redBox = Container( - color: Colors.red, - height: 40, - width: 40, - ); - final greenBox = Container( - color: Colors.green, - height: 40, - width: 20, - ); - final blackBox = Container( - color: Colors.black, - height: 10, - width: 10, - ); - final purpleBox = Container( - color: Colors.purple, - height: 20, - width: 20, - ); - final orangeBox = Container( - color: Colors.orange, - height: 80, - width: 20, - ); - final cyanBox = Container( - color: Colors.cyanAccent, - height: 10, - width: 20, - ); - - _buildItem(mode) => Wrap( - textDirection: mode, - runSpacing: 10, - spacing: 10, - children: [ - yellowBox, redBox, greenBox, cyanBox, - blackBox, purpleBox, orangeBox, - ], - ); -} diff --git a/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node5_verticalDirection.dart b/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node5_verticalDirection.dart deleted file mode 100644 index 9aa9556e8..000000000 --- a/lib/views/widgets/MultiChildRenderObjectWidget/Wrap/node5_verticalDirection.dart +++ /dev/null @@ -1,78 +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 { - @override - Widget build(BuildContext context) { - return Wrap( - children: VerticalDirection.values - .map((mode) => Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 160, - height: 100, - color: Colors.grey.withAlpha(88), - child: _buildItem(mode)), - Text(mode.toString().split('.')[1]) - ])) - .toList()); - } - - final yellowBox = Container( - color: Colors.yellow, - height: 30, - width: 50, - ); - - final redBox = Container( - color: Colors.red, - height: 40, - width: 40, - ); - final greenBox = Container( - color: Colors.green, - height: 40, - width: 20, - ); - final blackBox = Container( - color: Colors.black, - height: 10, - width: 10, - ); - final purpleBox = Container( - color: Colors.purple, - height: 20, - width: 20, - ); - final orangeBox = Container( - color: Colors.orange, - height: 80, - width: 20, - ); - final cyanBox = Container( - color: Colors.cyanAccent, - height: 10, - width: 20, - ); - - _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/lib/views/widgets/Other/ErrorWidget/node1_base.dart b/lib/views/widgets/Other/ErrorWidget/node1_base.dart deleted file mode 100644 index 7dce9acc7..000000000 --- a/lib/views/widgets/Other/ErrorWidget/node1_base.dart +++ /dev/null @@ -1,25 +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 { - @override - Widget build(BuildContext context) { - return Container( - height: 200, - child: ErrorWidget( - 'I am Error ErrorWidget\n' - 'But now, there has no error.' - ), - ); - } -} diff --git a/lib/views/widgets/Other/ListWheelViewport/node1_base.dart b/lib/views/widgets/Other/ListWheelViewport/node1_base.dart deleted file mode 100644 index a5a73901d..000000000 --- a/lib/views/widgets/Other/ListWheelViewport/node1_base.dart +++ /dev/null @@ -1,58 +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 { - 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 Container( - height: 250, - width: 320, - child: Scrollable( - axisDirection: AxisDirection.down, - physics: 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: 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/lib/views/widgets/Other/ListWheelViewport/node2_perspective.dart b/lib/views/widgets/Other/ListWheelViewport/node2_perspective.dart deleted file mode 100644 index fc9f26012..000000000 --- a/lib/views/widgets/Other/ListWheelViewport/node2_perspective.dart +++ /dev/null @@ -1,59 +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 { - 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 Container( - height: 250, - width: 320, - child: Scrollable( - axisDirection: AxisDirection.down, - physics: 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: 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/lib/views/widgets/Other/ListWheelViewport/node3_magnifier.dart b/lib/views/widgets/Other/ListWheelViewport/node3_magnifier.dart deleted file mode 100644 index 1248f0f4b..000000000 --- a/lib/views/widgets/Other/ListWheelViewport/node3_magnifier.dart +++ /dev/null @@ -1,66 +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 { - 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 Container( - height: 250, - width: 320, - // color: Colors.red, - child: Scrollable( - axisDirection: AxisDirection.down, - physics: 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: 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/lib/views/widgets/Other/ListWheelViewport/node4_opacity.dart b/lib/views/widgets/Other/ListWheelViewport/node4_opacity.dart deleted file mode 100644 index a467b1d3c..000000000 --- a/lib/views/widgets/Other/ListWheelViewport/node4_opacity.dart +++ /dev/null @@ -1,62 +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 { - 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 Container( - height: 250, - width: 320, - // color: Colors.red, - child: Scrollable( - axisDirection: AxisDirection.down, - physics: 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: 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/lib/views/widgets/Other/PerformanceOverlay/node1_base.dart b/lib/views/widgets/Other/PerformanceOverlay/node1_base.dart deleted file mode 100644 index f420a5728..000000000 --- a/lib/views/widgets/Other/PerformanceOverlay/node1_base.dart +++ /dev/null @@ -1,22 +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 { - @override - Widget build(BuildContext context) { - return PerformanceOverlay.allEnabled( - - ); - } -} - diff --git a/lib/views/widgets/Other/RawImage/node1_base.dart b/lib/views/widgets/Other/RawImage/node1_base.dart deleted file mode 100644 index dd3118907..000000000 --- a/lib/views/widgets/Other/RawImage/node1_base.dart +++ /dev/null @@ -1,100 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import 'dart:ui' as ui; - -/// 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 { - @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 Container( - 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, - ), - Text('isAntiAlias: true'), - Text('FilterQuality.high') - ], - ), - Column( - mainAxisSize: MainAxisSize.min, - children: [ - RawImage( - image: _image, - width: 150, - height: 150, - isAntiAlias: false, - ), - Text('isAntiAlias: false'), - Text('FilterQuality.low') - ], - ), - ], - ); - } - - void _loadImageFromAssets(String name) async { - _image = await loadImageByProvider(AssetImage(name)); - - setState(() {}); - } - - //通过ImageProvider读取Image - Future loadImageByProvider( - ImageProvider provider, { - ImageConfiguration config = ImageConfiguration.empty, - }) async { - Completer completer = Completer(); //完成的回调 - ImageStreamListener listener; - ImageStream stream = provider.resolve(config); //获取图片流 - listener = ImageStreamListener((ImageInfo frame, bool sync) { - //监听 - final ui.Image image = frame.image; - completer.complete(image); //完成 - stream.removeListener(listener); //移除监听 - }); - stream.addListener(listener); //添加监听 - return completer.future; //返回 - } -} diff --git a/lib/views/widgets/Other/RenderObjectToWidgetAdapter/node1_base.dart b/lib/views/widgets/Other/RenderObjectToWidgetAdapter/node1_base.dart deleted file mode 100644 index c11f1441c..000000000 --- a/lib/views/widgets/Other/RenderObjectToWidgetAdapter/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 -/// 说明: 289 RenderObjectToWidgetAdapter 根组件 RenderObject 和 Element 树的桥梁。 -/// -// { -// "widgetId": 289, -// "name": 'RenderObjectToWidgetAdapter 介绍', -// "priority": 1, -// "subtitle": -// "【container】 : 渲染对象 【RenderObjectWithChildMixin】\n" -// "【child】 : 子组件 【Widget】\n" -// "【debugShortDescription】 : 调试简介 【String】", -// } - -class RenderObjectToWidgetAdapterDemo extends StatelessWidget { - final String info = - '该组件并没有什么太大的使用价值,但却非常有纪念意义。它是 Flutter 框架中最顶层的 Widget,它的 child 是 runApp 传入的组件,在 attachRootWidget 方法中被实例化。' - '它持有根渲染对象 RenderView ,负责创建根元素 RenderObjectToWidgetElement,是一个无名英雄,一个深藏功与名的组件。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/lib/views/widgets/Other/Table/node1_base.dart b/lib/views/widgets/Other/Table/node1_base.dart deleted file mode 100644 index dab470861..000000000 --- a/lib/views/widgets/Other/Table/node1_base.dart +++ /dev/null @@ -1,82 +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 { - @override - Widget build(BuildContext context) { - var title = _ItemBean("单位称", "量纲", "单位", "单位名称", "单位符号"); - var m = _ItemBean("长度", "L", "1m", "米", "m"); - var kg = _ItemBean("质量", "M", "1Kg", "千克", "Kg"); - var s = _ItemBean("时间", "T", "1s", "秒", "s"); - var a = _ItemBean("安培", "Ι", "1A", "安培", "A"); - var k = _ItemBean("热力学温度", "θ", "1K", "开尔文", "K"); - var mol = _ItemBean("物质的量", "N", "1mol", "摩尔", "mol"); - var cd = _ItemBean("发光强度", "J", "1cd", "坎德拉", "cd"); - - var data = <_ItemBean>[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: 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/lib/views/widgets/ProxyWidget/ButtonBarTheme/node1_base.dart b/lib/views/widgets/ProxyWidget/ButtonBarTheme/node1_base.dart deleted file mode 100644 index 73d03cd63..000000000 --- a/lib/views/widgets/ProxyWidget/ButtonBarTheme/node1_base.dart +++ /dev/null @@ -1,46 +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 -/// 说明: 338 ButtonBarTheme 5 主要用于为后代的ButtonBar组件统一设置默认属性,也可以通过该组件获取默认ButtonBarTheme的属性。 -// { -// "widgetId": 338, -// "name": "ButtonBarTheme基本使用", -// "priority": 1, -// "subtitle": "可指定ButtonBarThemeData数据属性为【后代】的ButtonBar组件设置默认样式,如对齐方式、样式、边距等。也可以用ButtonBarTheme.of获取ButtonBar的主题属性。", -// } - -class ButtonBarThemeDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return ButtonBarTheme( - child: TempButtonBar(), - data: ButtonBarTheme.of(context).copyWith( - alignment: MainAxisAlignment.center, - buttonPadding: EdgeInsets.symmetric(horizontal: 6), - overflowDirection: VerticalDirection.up, - buttonMinWidth: 150, - buttonHeight: 30, - buttonTextTheme: ButtonTextTheme.primary)); - } -} - -class TempButtonBar extends StatelessWidget { - @override - Widget build(BuildContext context) { - return ButtonBar( - alignment: MainAxisAlignment.center, - children: [ - RaisedButton( - color: Colors.blue, child: Text("1.Raised"), onPressed: () {}), - OutlineButton(child: Text("2.Outline"), onPressed: () {}), - FlatButton( - color: Colors.blue, - onPressed: () {}, - child: Text("3.Flat"), - ) - ], - ); - } -} diff --git a/lib/views/widgets/ProxyWidget/ButtonTheme/node1_base.dart b/lib/views/widgets/ProxyWidget/ButtonTheme/node1_base.dart deleted file mode 100644 index 5fdda2f5c..000000000 --- a/lib/views/widgets/ProxyWidget/ButtonTheme/node1_base.dart +++ /dev/null @@ -1,38 +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 { - @override - Widget build(BuildContext context) { - return ButtonTheme( - buttonColor: Colors.orange, - splashColor: Colors.blue, - minWidth: 40, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - child: Wrap( - spacing: 10, - children: [ - RaisedButton(onPressed: (){},child: Icon(Icons.add)), - FlatButton(onPressed: (){},child: Icon(Icons.add)), - OutlineButton(onPressed: (){},child: Icon(Icons.add)), - MaterialButton(onPressed: (){},child: Icon(Icons.add)), - ], - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/ProxyWidget/ChipTheme/node1_base.dart b/lib/views/widgets/ProxyWidget/ChipTheme/node1_base.dart deleted file mode 100644 index 013cd2bc9..000000000 --- a/lib/views/widgets/ProxyWidget/ChipTheme/node1_base.dart +++ /dev/null @@ -1,76 +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 { - @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: CustomFilterChip(), - ); - } -} - -class CustomFilterChip extends StatefulWidget { - @override - _CustomFilterChipState createState() => _CustomFilterChipState(); -} - -class _CustomFilterChipState extends State { - final Map map = { - 'A': 'Ant', - 'B': 'Bug', - 'C': 'Cat', - 'D': 'Dog', - }; - List _selected = []; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Wrap( - children: map.keys.map((key) => _buildChild(key)).toList(), - ), - Container( - padding: 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/lib/views/widgets/ProxyWidget/CupertinoUserInterfaceLevel/node1_base.dart b/lib/views/widgets/ProxyWidget/CupertinoUserInterfaceLevel/node1_base.dart deleted file mode 100644 index c34a93f50..000000000 --- a/lib/views/widgets/ProxyWidget/CupertinoUserInterfaceLevel/node1_base.dart +++ /dev/null @@ -1,42 +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 { - @override - Widget build(BuildContext context) { - - return CupertinoUserInterfaceLevel( - data: CupertinoUserInterfaceLevelData.elevated, - child: LevelShower() - ); - } - -} - -class LevelShower extends StatelessWidget { - @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/lib/views/widgets/ProxyWidget/DefaultAssetBundle/node1_base.dart b/lib/views/widgets/ProxyWidget/DefaultAssetBundle/node1_base.dart deleted file mode 100644 index 942dcbc96..000000000 --- a/lib/views/widgets/ProxyWidget/DefaultAssetBundle/node1_base.dart +++ /dev/null @@ -1,51 +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 { - @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: EdgeInsets.all(10), - margin: 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/lib/views/widgets/ProxyWidget/DefaultTextStyle/node1_base.dart b/lib/views/widgets/ProxyWidget/DefaultTextStyle/node1_base.dart deleted file mode 100644 index f7d5634f9..000000000 --- a/lib/views/widgets/ProxyWidget/DefaultTextStyle/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": 324, -// "name": 'DefaultTextStyle使用', -// "priority": 1, -// "subtitle": -// "各属性同Text,详见之。\n" -// "其功能是: 设置默认的文字样式应用于【后代组件】,注意后代组件也可以指定自身的样式", -// } -class DefaultTextStyleDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return DefaultTextStyle( - style: TextStyle( - fontSize: 18, - color: Colors.blue, - decoration: TextDecoration.underline), - child: Wrap( - spacing: 5, - children: [ - Text("Hello,",), - FlutterLogo(), - Text("Flutter",style: TextStyle(color: Colors.red),), - Text("Unit."), - ], - ), - ); - } -} diff --git a/lib/views/widgets/ProxyWidget/Directionality/node1_base.dart b/lib/views/widgets/ProxyWidget/Directionality/node1_base.dart deleted file mode 100644 index cca2a0c69..000000000 --- a/lib/views/widgets/ProxyWidget/Directionality/node1_base.dart +++ /dev/null @@ -1,63 +0,0 @@ -import 'package:flutter/cupertino.dart'; -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 { - @override - _DirectionalityDemoState createState() => _DirectionalityDemoState(); -} - -class _DirectionalityDemoState extends State { - TextDirection _textDirection = TextDirection.rtl; - - @override - Widget build(BuildContext context) { - return Directionality( - textDirection: _textDirection, - child: Container( - padding: EdgeInsets.all(8), - width: 250, - color: Colors.grey.withAlpha(33), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'A widget that determines the ambient directionality of text and text direction sensitive render objects.'), - _buildSwitch(), - Text( - 'The text direction from the closest instance of this class that encloses the given context.'), - ], - ), - ), - ); - } - - Widget _buildSwitch() { - return Row( - children: [ - Switch( - value: _textDirection == TextDirection.rtl, - onChanged: (v) { - setState(() { - _textDirection = - v ? TextDirection.rtl : TextDirection.ltr; - }); - }, - ), - Text( - _textDirection.toString(), - style: TextStyle(color: Colors.blue, fontSize: 18), - ) - ], - ); - } -} diff --git a/lib/views/widgets/ProxyWidget/DividerTheme/node1_base.dart b/lib/views/widgets/ProxyWidget/DividerTheme/node1_base.dart deleted file mode 100644 index 3af636e85..000000000 --- a/lib/views/widgets/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 { - @override - Widget build(BuildContext context) { - return DividerTheme( - data: DividerThemeData( - color: Colors.orange, - thickness: 2, - space: 10, - indent: 10, - endIndent: 10, - - ), - child: Wrap( - spacing: 10, - children: [ - Divider(), - Divider(), - Divider(), - Divider(), - Divider(), - Container( - height: 100, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - VerticalDivider(), - VerticalDivider(), - VerticalDivider(), - VerticalDivider(), - VerticalDivider(), - ], - ), - ) - ], - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/ProxyWidget/DropdownButtonHideUnderline/node1_base.dart b/lib/views/widgets/ProxyWidget/DropdownButtonHideUnderline/node1_base.dart deleted file mode 100644 index cc97cf34c..000000000 --- a/lib/views/widgets/ProxyWidget/DropdownButtonHideUnderline/node1_base.dart +++ /dev/null @@ -1,59 +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 { - @override - _CustomDropDownButtonHideUnderlineState createState() => - _CustomDropDownButtonHideUnderlineState(); -} - -class _CustomDropDownButtonHideUnderlineState - extends State { - Color _color = Colors.red; - final _colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green]; - final _info = ["红色", "黄色", "蓝色", "绿色"]; - - @override - Widget build(BuildContext context) { - return Wrap( - children: [ - Container( - margin: 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: (v) => setState(() => _color = v)), - ), - ], - ); - } - - List> _buildItems() => _colors - .map((e) => DropdownMenuItem( - value: e, - child: Text( - _info[_colors.indexOf(e)], - style: TextStyle(color: e), - ))) - .toList(); -} diff --git a/lib/views/widgets/ProxyWidget/Expanded/node1_base.dart b/lib/views/widgets/ProxyWidget/Expanded/node1_base.dart deleted file mode 100644 index 9896200a1..000000000 --- a/lib/views/widgets/ProxyWidget/Expanded/node1_base.dart +++ /dev/null @@ -1,55 +0,0 @@ - import 'package:flutter/material.dart'; - import '../../../../app/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 { - @override - _CustomExpendedState createState() => _CustomExpendedState(); -} - -class _CustomExpendedState extends State { - @override - Widget build(BuildContext context) { - return Container( - child: Column( - children: [ - buildRow([0, 0, 0]), - SizedBox(height: 10,), - buildRow([0, 0, 1]), - SizedBox(height: 10,), - buildRow([1, 1, 1]), - 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: TextStyle(color: Colors.white), - ), - ), - )).toList()); - } -} diff --git a/lib/views/widgets/ProxyWidget/Flexible/node1_base.dart b/lib/views/widgets/ProxyWidget/Flexible/node1_base.dart deleted file mode 100644 index 08616f552..000000000 --- a/lib/views/widgets/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 { - @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: EdgeInsets.all(8.0), - child: Row( - children: [ - Flexible( - flex: 2, - child: Container( - alignment: Alignment.center, - height: 50, - color: Colors.red, - child: Text( - 'flex=2', - style: TextStyle(color: Colors.white), - ), - ), - ), - Flexible( - flex: 3, - child: Container( - alignment: Alignment.center, - height: 50, - color: Colors.blue, - child: Text( - 'flex=3', - style: TextStyle(color: Colors.white), - ), - ), - ), - Flexible( - flex: 4, - fit: _loose?FlexFit.loose:FlexFit.tight, - child: Container( - constraints: BoxConstraints(maxWidth: 60), - alignment: Alignment.center, - height: 50, - color: Colors.green, - child: Text( - 'flex=4 \nfit:${_loose?'loose':'tight'}', - style: TextStyle(color: Colors.white), - ), - ), - ) - ], - )), - _buildOp() - ]); - } - - Widget _buildOp() { - return 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/lib/views/widgets/ProxyWidget/IconTheme/node1_base.dart b/lib/views/widgets/ProxyWidget/IconTheme/node1_base.dart deleted file mode 100644 index 1307df56a..000000000 --- a/lib/views/widgets/ProxyWidget/IconTheme/node1_base.dart +++ /dev/null @@ -1,34 +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 { - @override - Widget build(BuildContext context) { - return IconTheme( - data: IconThemeData( - color: Colors.purple, - opacity: 1.0, - size: 30 - ), - child: Wrap( - spacing: 10, - children: [ - Icon(Icons.add), - Icon(Icons.ac_unit), - Icon(Icons.g_translate), - Icon(Icons.remove) - ], - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/ProxyWidget/InheritedTheme/node1_base.dart b/lib/views/widgets/ProxyWidget/InheritedTheme/node1_base.dart deleted file mode 100644 index da33c0897..000000000 --- a/lib/views/widgets/ProxyWidget/InheritedTheme/node1_base.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 -/// 说明: 345 InheritedTheme 传承主题 -/// 它是抽象类,有非常多的 XXXTheme 相关子类,用于定义颜色、文字样式等属性,在子树中共享这些属性。 -/// link 324,326,328,329 -/// -// { -// "widgetId": 345, -// "name": 'InheritedTheme 介绍', -// "priority": 1, -// "subtitle": -// "InheritedTheme.capture 可以抓取上层主题,获取 CapturedThemes 对象,通过该对象 wrap 方法可以跨路由使用抓到的主题。", -// } - -class InheritedThemeDemo extends StatelessWidget { - - @override - Widget build(BuildContext context) { - return DefaultTextStyle( - style: TextStyle(fontSize: 24, color: Colors.blue), - child: TestBody(), - ); - } -} - -class TestBody extends StatelessWidget { - @override - Widget build(BuildContext context) { - - return GestureDetector( - onTap: () => _toNextPage(context), - child: Container( - height: 60, - margin: EdgeInsets.only(left: 40,right: 40), - alignment: Alignment.center, - color: Theme.of(context).primaryColor.withOpacity(0.1), - child: 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/lib/views/widgets/ProxyWidget/InheritedWidget/node1_base.dart b/lib/views/widgets/ProxyWidget/InheritedWidget/node1_base.dart deleted file mode 100644 index 284569374..000000000 --- a/lib/views/widgets/ProxyWidget/InheritedWidget/node1_base.dart +++ /dev/null @@ -1,58 +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 等。'; - - @override - Widget build(BuildContext context) { - return InfoInheritedWidget( - info: info, - child: InfoWidget(), - ); - } -} - -class InfoWidget extends StatelessWidget { - @override - Widget build(BuildContext context) { - String info = InfoInheritedWidget.of(context).info; - - return Container( - color: Colors.blue.withOpacity(0.1), - padding: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info), - ); - } -} - -class InfoInheritedWidget extends InheritedWidget { - final String info; - - InfoInheritedWidget({Key key, 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/lib/views/widgets/ProxyWidget/KeepAlive/node1_base.dart b/lib/views/widgets/ProxyWidget/KeepAlive/node1_base.dart deleted file mode 100644 index 135d8963c..000000000 --- a/lib/views/widgets/ProxyWidget/KeepAlive/node1_base.dart +++ /dev/null @@ -1,119 +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 { - - 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 Container( - 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; - - ColorBox({Key key, this.color, 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: [ - SizedBox(width: 60,), - Checkbox( - value: _checked, - onChanged: (v) { - setState(() { - _checked = v; - }); - }, - ), - Text( - "index ${widget.index}: ${colorString(widget.color)}", - style: 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/lib/views/widgets/ProxyWidget/LayoutId/node1_base.dart b/lib/views/widgets/ProxyWidget/LayoutId/node1_base.dart deleted file mode 100644 index 1bd5ee7b6..000000000 --- a/lib/views/widgets/ProxyWidget/LayoutId/node1_base.dart +++ /dev/null @@ -1,92 +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 { - @override - Widget build(BuildContext context) { - return Container( - width: 300, - height: 150, - color: Colors.grey.withAlpha(33), - child: CustomMultiChildLayout( - delegate: CornerCustomMultiChildLayout( - padding:EdgeInsets.only(left: 10,top: 5,right: 10,bottom: 5), - ), - children: [ - LayoutId(id: CornerType.topLeft, child: Box50(Colors.red)), - LayoutId(id: CornerType.topRight, child: Box50(Colors.yellow)), - LayoutId(id: CornerType.bottomLeft, child: Box50(Colors.blue)), - LayoutId(id: CornerType.bottomRight, child: Box50(Colors.green)), - ], - ), - ); - } -} - -// 50 颜射盒 -class Box50 extends StatelessWidget { - final Color color; - Box50(this.color); - - @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/lib/views/widgets/ProxyWidget/ListTileTheme/node1_base.dart b/lib/views/widgets/ProxyWidget/ListTileTheme/node1_base.dart deleted file mode 100644 index a5a032be1..000000000 --- a/lib/views/widgets/ProxyWidget/ListTileTheme/node1_base.dart +++ /dev/null @@ -1,51 +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 { - @override - Widget build(BuildContext context) { - return ListTileTheme( - dense: false, - style: ListTileStyle.list, - selectedColor: Colors.blue, - contentPadding: 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: EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: ListTile( - leading: Image.asset("assets/images/icon_head.webp"), - selected: _selected, - title: Text("以梦为马"), - subtitle: Text("海子"), - trailing: Icon(Icons.more_vert), - onTap: () => setState(() => _selected = !_selected), - ), - ); - } -} diff --git a/lib/views/widgets/ProxyWidget/MaterialBannerTheme/node1_base.dart b/lib/views/widgets/ProxyWidget/MaterialBannerTheme/node1_base.dart deleted file mode 100644 index e53579140..000000000 --- a/lib/views/widgets/ProxyWidget/MaterialBannerTheme/node1_base.dart +++ /dev/null @@ -1,67 +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 { - @override - Widget build(BuildContext context) { - return MaterialBannerTheme( - data: MaterialBannerTheme.of(context).copyWith( - backgroundColor: Colors.purple, - padding: EdgeInsetsDirectional.only(start: 16.0, top: 2.0,end: 2), - leadingPadding:EdgeInsetsDirectional.only(end: 16.0) , - contentTextStyle: 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: Icon(Icons.warning, color: Colors.yellow), - actions: [ - RaisedButton( - color: Colors.white, - onPressed: () {}, - child: Text( - 'I KNOW', - style: TextStyle( - color: Colors.purple, - fontWeight: FontWeight.bold, - fontSize: 14), - ), - ), - - RaisedButton( - color: Colors.white, - onPressed: () {}, - child: Text( - 'I IGNORE', - style: TextStyle( - color: Colors.purple, - fontWeight: FontWeight.bold, - fontSize: 14), - ), - ), - ], - )], - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/ProxyWidget/MediaQuery/node1_base.dart b/lib/views/widgets/ProxyWidget/MediaQuery/node1_base.dart deleted file mode 100644 index 818b2e18a..000000000 --- a/lib/views/widgets/ProxyWidget/MediaQuery/node1_base.dart +++ /dev/null @@ -1,71 +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 { - @override - Widget build(BuildContext context) { - var queryData = MediaQuery.of(context); - var 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: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - Text( - data[e].toString(), - style: TextStyle(fontSize: 16, color: Colors.orange), - ) - ], - ), - ), - Divider( - height: 1, - ) - ], - ); -} - - diff --git a/lib/views/widgets/ProxyWidget/ParentDataWidget/node1_base.dart b/lib/views/widgets/ProxyWidget/ParentDataWidget/node1_base.dart deleted file mode 100644 index 192323a83..000000000 --- a/lib/views/widgets/ProxyWidget/ParentDataWidget/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 -/// 说明: 347 ParentDataWidget 父数据组件 -/// 抽象类,用于将 ParentData 信息挂钩到 RenderObjectWidget 子组件上。其子类有 Positioned、Flexible、Expanded等,这些组件只能用于特定的组件之下。 -/// -// { -// "widgetId": 347, -// "name": 'ParentDataWidget 介绍', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】", -// } - -class ParentDataWidgetDemo extends StatelessWidget { - 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: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/lib/views/widgets/ProxyWidget/PopupMenuTheme/node1_base.dart b/lib/views/widgets/ProxyWidget/PopupMenuTheme/node1_base.dart deleted file mode 100644 index 6a8837c36..000000000 --- a/lib/views/widgets/ProxyWidget/PopupMenuTheme/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 -/// 说明: 330 PopupMenuTheme 主要用于为后代的PopupMenuButton组件统一设置默认属性,也可以通过该组件获取默认PopupMenu的属性。 -// { -// "widgetId": 330, -// "name": "PopupMenuTheme基本使用", -// "priority": 1, -// "subtitle": "可指定PopupMenuThemeData数据属性为【后代】的PopupMenuButton组件设置默认样式,如形状、影深、颜色、文字样式等。也可以用PopupMenuTheme.of获取PopupMenu的主题数据。", -// } -class PopupMenuThemeDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return PopupMenuTheme( - data: PopupMenuTheme.of(context).copyWith( - color: Colors.orangeAccent, - elevation: 1, - textStyle: TextStyle(color: Colors.white), - shape: 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 = { - "关于": Icons.info_outline, - "帮助": Icons.help_outline, - "反馈": Icons.add_comment, - }; - - @override - Widget build(BuildContext context) { - return PopupMenuButton( - itemBuilder: (context) => buildItems(), - offset: 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/lib/views/widgets/ProxyWidget/Positioned/node1_base.dart b/lib/views/widgets/ProxyWidget/Positioned/node1_base.dart deleted file mode 100644 index 0e249eb94..000000000 --- a/lib/views/widgets/ProxyWidget/Positioned/node1_base.dart +++ /dev/null @@ -1,62 +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 { - @override - Widget build(BuildContext context) { - var yellowBox = Container( - color: Colors.yellow, - height: 100, - width: 100, - ); - - var redBox = Container( - color: Colors.red, - height: 90, - width: 90, - ); - - var greenBox = Container( - color: Colors.green, - height: 80, - width: 80, - ); - - var 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/lib/views/widgets/ProxyWidget/PrimaryScrollController/node1_base.dart b/lib/views/widgets/ProxyWidget/PrimaryScrollController/node1_base.dart deleted file mode 100644 index c59121f7d..000000000 --- a/lib/views/widgets/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 { - 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: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info+"当前其持有的滑动控制器对象: $label"), - ); - } -} diff --git a/lib/views/widgets/ProxyWidget/ScrollConfiguration/node1_base.dart b/lib/views/widgets/ProxyWidget/ScrollConfiguration/node1_base.dart deleted file mode 100644 index a8bb6e5b6..000000000 --- a/lib/views/widgets/ProxyWidget/ScrollConfiguration/node1_base.dart +++ /dev/null @@ -1,68 +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 { - final 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 Container( - height: 200, - child: ScrollConfiguration( - behavior: NoScrollBehavior(), child: _buildListView()), - ); - } - - Widget _buildListView() => ListView( - padding: EdgeInsets.symmetric(horizontal: 5), - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: color, - child: Text( - colorString(color), - style: 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/lib/views/widgets/ProxyWidget/SliderTheme/node1_base.dart b/lib/views/widgets/ProxyWidget/SliderTheme/node1_base.dart deleted file mode 100644 index b9108c93e..000000000 --- a/lib/views/widgets/ProxyWidget/SliderTheme/node1_base.dart +++ /dev/null @@ -1,42 +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 { - @override - _SliderThemeDemoState createState() => _SliderThemeDemoState(); -} - -class _SliderThemeDemoState extends State { - var _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/lib/views/widgets/ProxyWidget/SliderTheme/node2_diy.dart b/lib/views/widgets/ProxyWidget/SliderTheme/node2_diy.dart deleted file mode 100644 index 885db01df..000000000 --- a/lib/views/widgets/ProxyWidget/SliderTheme/node2_diy.dart +++ /dev/null @@ -1,178 +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 { - @override - _DIYSliderThemeState createState() => _DIYSliderThemeState(); -} - -class _DIYSliderThemeState extends State { - var _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.accentTextTheme.body2 - .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, - {Animation activationAnimation, - Animation enableAnimation, - bool isDiscrete, - TextPainter labelPainter, - RenderBox parentBox, - SliderThemeData sliderTheme, - TextDirection textDirection, - double value, - double textScaleFactor, - 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)); - } -} - -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, - {Animation activationAnimation, - Animation enableAnimation, - bool isDiscrete, - TextPainter labelPainter, - RenderBox parentBox, - SliderThemeData sliderTheme, - TextDirection textDirection, - double value, - double textScaleFactor, - 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()); - 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/lib/views/widgets/ProxyWidget/TableCell/node1_base.dart b/lib/views/widgets/ProxyWidget/TableCell/node1_base.dart deleted file mode 100644 index fb313d169..000000000 --- a/lib/views/widgets/ProxyWidget/TableCell/node1_base.dart +++ /dev/null @@ -1,79 +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 -/// 说明: 317 TableCell 表室 必须在 Table 组件的后代中使用,用于控制表孩子的竖直方向对齐方式,并没是什么太大的作用。 -// { -// "widgetId": 317, -// "name": 'TableCell基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 组件 【Widget】\n" -// "【verticalAlignment】 : 竖直对齐方式 【TableCellVerticalAlignment】", -// } - -class TableCellDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - var title = _ItemBean("单位称", "量纲", "单位", "单位名称", "单位符号"); - var m = _ItemBean("长度", "L", "1m", "米", "m"); - var kg = _ItemBean("质量", "M", "1Kg", "千克", "Kg"); - var s = _ItemBean("时间", "T", "1s", "秒", "s"); - var a = _ItemBean("安培", "Ι", "1A", "安培", "A"); - var k = _ItemBean("热力学温度", "θ", "1K", "开尔文", "K"); - var mol = _ItemBean("物质的量", "N", "1mol", "摩尔", "mol"); - var cd = _ItemBean("发光强度", "J", "1cd", "坎德拉", "cd"); - - var data = <_ItemBean>[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: 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: Container(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/lib/views/widgets/ProxyWidget/ToggleButtonsTheme/node1_base.dart b/lib/views/widgets/ProxyWidget/ToggleButtonsTheme/node1_base.dart deleted file mode 100644 index 3d0d16fbf..000000000 --- a/lib/views/widgets/ProxyWidget/ToggleButtonsTheme/node1_base.dart +++ /dev/null @@ -1,55 +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 { - @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: [ - 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/lib/views/widgets/ProxyWidget/TooltipTheme/node1_base.dart b/lib/views/widgets/ProxyWidget/TooltipTheme/node1_base.dart deleted file mode 100644 index d9dff80c3..000000000 --- a/lib/views/widgets/ProxyWidget/TooltipTheme/node1_base.dart +++ /dev/null @@ -1,51 +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 -/// 说明: 333 TooltipTheme 5 主要用于为后代的Tooltip组件统一设置默认属性,也可以通过该组件获取默认TooltipTheme的属性。 -// { -// "widgetId": 333, -// "name": "TooltipTheme基本使用", -// "priority": 1, -// "subtitle": "可指定TooltipThemeData数据属性为【后代】的Tooltip组件设置默认样式,如装饰、文字样式、显示时长、边距等。也可以用TooltipTheme.of获取Tooltip的主题属性。", -// } - -class TooltipThemeDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return TooltipTheme( - child: TempTooltip(), - data: TooltipTheme.of(context).copyWith( - preferBelow: false, - padding: EdgeInsets.all(5), - verticalOffset: 20, - margin: EdgeInsets.all(2), - 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) - ]))); - } -} - -class TempTooltip extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Tooltip( - message: "天王盖地虎", - child: Icon(Icons.info_outline)), - Tooltip( - message: "宝塔镇河妖", - child: Icon(Icons.info_outline)), - ], - ); - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/AbsorbPointer/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/AbsorbPointer/node1_base.dart deleted file mode 100644 index ec83929fa..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/AbsorbPointer/node1_base.dart +++ /dev/null @@ -1,62 +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 { - @override - _CustomAbsorbPointerState createState() => _CustomAbsorbPointerState(); -} - -class _CustomAbsorbPointerState extends State { - bool _absorbing = false; - - @override - Widget build(BuildContext context) { - return Container( - child: Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - GestureDetector( - onTap: (){ - print('AbsorbPointer'); - }, - child: AbsorbPointer( - absorbing: _absorbing, - child: _buildButton(), - ), - ), - _buildSwitch(), - Text(!_absorbing ? '允许点击' : '事件已被吸收') - ], - ), - ); - } - - Widget _buildButton() => RaisedButton( - color: Theme.of(context).primaryColor, - child: Text( - 'To About', - style: TextStyle(color: Colors.white), - ), - onPressed: () => Navigator.of(context).pushNamed('AboutMePage')); - - _buildSwitch() => Switch( - value: _absorbing, - onChanged: (v) { - setState(() { - _absorbing = v; - }); - }); -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/Align/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Align/node1_base.dart deleted file mode 100644 index d396cd4d8..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Align/node1_base.dart +++ /dev/null @@ -1,61 +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 { - final alignments = [ - Alignment.topLeft, - Alignment.topCenter, - Alignment.topRight, - Alignment.centerLeft, - Alignment.center, - Alignment.centerRight, - Alignment.bottomLeft, - Alignment.bottomCenter, - Alignment.bottomRight, - ]; - - final alignmentsInfo = [ - "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: 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/lib/views/widgets/SingleChildRenderObjectWidget/Align/node2_other.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Align/node2_other.dart deleted file mode 100644 index d16e6e20e..000000000 --- a/lib/views/widgets/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 { - 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 { - SinLayout({ - Key key, - }) : super(key: key); - - @override - _SinLayoutState createState() => _SinLayoutState(); -} - -class _SinLayoutState extends State { - var _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: 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/lib/views/widgets/SingleChildRenderObjectWidget/AnimatedSize/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/AnimatedSize/node1_base.dart deleted file mode 100644 index df49a4bb5..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/AnimatedSize/node1_base.dart +++ /dev/null @@ -1,75 +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 { - @override - _CustomAnimatedSizeState createState() => _CustomAnimatedSizeState(); -} - -class _CustomAnimatedSizeState extends State - with SingleTickerProviderStateMixin { - final double start = 100; - final double end = 200; - - 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( - vsync: this, - duration: Duration(seconds: 1), - curve: Curves.fastOutSlowIn, - alignment: Alignment(0, 0), - child: Container( - height: 40, - width: _width, - alignment: Alignment.center, - color: Colors.blue, - child: Text( - '张风捷特烈', - style: TextStyle(color: Colors.white), - ), - ), - ), - ), - ], - ); - } - - Widget _buildSwitch() => Switch( - value: _width == end, - onChanged: (v) { - setState(() { - _width = v ? end : start; - }); - }); -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/AnnotatedRegion/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/AnnotatedRegion/node1_base.dart deleted file mode 100644 index 2220857ff..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/AnnotatedRegion/node1_base.dart +++ /dev/null @@ -1,85 +0,0 @@ - -import 'package:flutter/cupertino.dart'; -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 { - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.all(10), - child: ElevatedButton( - onPressed: (){ - Navigator.push(context, - MaterialPageRoute(builder: (context) => AnnotatedRegionTestPage()), - ); - }, - child: Text("进入 AnnotatedRegion 测试页"), - ), - ); - } -} - - -class AnnotatedRegionTestPage extends StatelessWidget{ - @override - Widget build(BuildContext context) { - final 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: Container( - child: Column( - children: [ - Container(height: 56+30.0,color: Colors.blue, - alignment: Alignment(0,0.55), - child: Row( - children: [ - BackButton(color: Colors.white,), - Text("AnnotatedRegion测试",style: TextStyle(color: Colors.white,fontSize: 18),) - ], - ), - ), - SizedBox(height: 30,), - Text( - "上面标题栏背景颜色为蓝色\n" - "上面标题栏图标为亮调", - - style: TextStyle(color: Colors.black,fontSize: 18),), - Spacer(), - Text( - "下面导航栏背景颜色为绿色\n" - "下面导航栏图标为亮调", - - style: TextStyle(color: Colors.black,fontSize: 18),), - SizedBox(height: 30,), - ], - ), - ), - ), - ); - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/AspectRatio/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/AspectRatio/node1_base.dart deleted file mode 100644 index 137128cd0..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/AspectRatio/node1_base.dart +++ /dev/null @@ -1,63 +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 { - @override - _CustomAspectRatioState createState() => _CustomAspectRatioState(); -} - -class _CustomAspectRatioState extends State { - var _ratio = 0.75; - - @override - Widget build(BuildContext context) { - var child = Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 50, - height: 50, - child: Text("Static"), - ); - - var box = AspectRatio( - aspectRatio: _ratio, - child: Container( - color: Colors.orange, - child: 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/lib/views/widgets/SingleChildRenderObjectWidget/BackdropFilter/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/BackdropFilter/node1_base.dart deleted file mode 100644 index 98b7cce9b..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/BackdropFilter/node1_base.dart +++ /dev/null @@ -1,103 +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 { - @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: [ - Container( - height: 150, - width: 150, - child: Image.asset( - 'assets/images/sabar.webp', - fit: BoxFit.cover, - ), - ), - Container( - 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/lib/views/widgets/SingleChildRenderObjectWidget/Baseline/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Baseline/node1_base.dart deleted file mode 100644 index a720790bf..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Baseline/node1_base.dart +++ /dev/null @@ -1,58 +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 { - @override - _CustomBaselineState createState() => _CustomBaselineState(); -} - -class _CustomBaselineState extends State { - double _baseline=20; - - @override - Widget build(BuildContext context) { - - var childBox = Text( - '你好,Flutter', - style: TextStyle(fontSize: 20,fontFamily: "Menlo"), - ); - - - var 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/lib/views/widgets/SingleChildRenderObjectWidget/Center/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Center/node1_base.dart deleted file mode 100644 index a81c643e8..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Center/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": 86, -// "name": 'Center基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】", -// } -class CustomCenter extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - margin: 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/lib/views/widgets/SingleChildRenderObjectWidget/ClipOval/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/ClipOval/node1_base.dart deleted file mode 100644 index 64e5a95df..000000000 --- a/lib/views/widgets/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 { - @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/lib/views/widgets/SingleChildRenderObjectWidget/ClipPath/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/ClipPath/node1_base.dart deleted file mode 100644 index 1fdb0220b..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/ClipPath/node1_base.dart +++ /dev/null @@ -1,70 +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 { - @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 => null; - - @override - Path getInnerPath(Rect rect, {TextDirection textDirection}) { - return null; - } - - @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 null; - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/ClipRRect/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/ClipRRect/node1_base.dart deleted file mode 100644 index d84b81b47..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/ClipRRect/node1_base.dart +++ /dev/null @@ -1,28 +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 { - @override - Widget build(BuildContext context) { - return ClipRRect( - borderRadius: BorderRadius.all(Radius.elliptical(35, 30)), - child: Image.asset( - "assets/images/wy_300x200.webp", - width: 150, - height: 100, - ), - ); - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/ClipRect/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/ClipRect/node1_base.dart deleted file mode 100644 index 20ca84e82..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/ClipRect/node1_base.dart +++ /dev/null @@ -1,28 +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 { - @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/lib/views/widgets/SingleChildRenderObjectWidget/ColorFiltered/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/ColorFiltered/node1_base.dart deleted file mode 100644 index 496ba3e03..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/ColorFiltered/node1_base.dart +++ /dev/null @@ -1,67 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../../app/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 { - @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), - SizedBox( - height: 10, - ), - Text( - mode.toString().split('.')[1], - style: TextStyle(fontSize: 10), - ) - ], - )) - .toList() - ]), - ], - ); - } - - Widget _buildChild(m) => Container( - width: 58, - height: 58, - child: ColorFiltered( - child: 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: Text('点我'), - ), - ); -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/ColoredBox/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/ColoredBox/node1_base.dart deleted file mode 100644 index 527af6f92..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/ColoredBox/node1_base.dart +++ /dev/null @@ -1,39 +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 -/// 说明: 267 ColoredBox 在子组件的布局区域上绘制颜色,然后子组件绘制在背景色上。 -// { -// "widgetId": 267, -// "name": 'ColoredBox基本使用', -// "priority": 1, -// "subtitle": -// "【color】 : 组件 【Color】\n" -// "【child】 : 组件 【Widget】", -// } - -class ColoredBoxDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return ColoredBox( - color: Colors.red, - child: Container( - margin: EdgeInsets.all(20), - padding: EdgeInsets.all(20), - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - color: Colors.blue - ), - alignment: Alignment.center, - width: 250, - height: 100, - // color: Theme.of(context).primaryColor, - child: Text( - "蓝色是加了 margin 和圆角的 Container,外层包裹红色的 ColoredBox,注意作用范围。", - style: TextStyle(color: Colors.white), - ), - ), - ); - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/CompositedTransformFollower/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/CompositedTransformFollower/node1_base.dart deleted file mode 100644 index c54530e67..000000000 --- a/lib/views/widgets/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(); - - 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: this._layerLink, - showWhenUnlinked: false, - offset: Offset(0,-10), - targetAnchor: Alignment.topRight, - child: Card( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text('我是一个 Overlay,目标组件为图标,当它变换时,我会伴随变换。'), - ), - ), - ), - )); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: _toggleOverlay, - child: CompositedTransformTarget( - link: this._layerLink, - child: - 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/lib/views/widgets/SingleChildRenderObjectWidget/CompositedTransformTarget/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/CompositedTransformTarget/node1_base.dart deleted file mode 100644 index 7ae48ca5f..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/CompositedTransformTarget/node1_base.dart +++ /dev/null @@ -1,112 +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(); - - 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: this._layerLink, - showWhenUnlinked: false, - targetAnchor: Alignment.topRight, - child: Card( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Text('我是一个 Overlay,目标组件为图标,当它变换时,我会伴随变换。'), - ), - ), - ), - )); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: _toggleOverlay, - child: CompositedTransformTarget( - link: this._layerLink, - child: - 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/lib/views/widgets/SingleChildRenderObjectWidget/ConstrainedBox/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/ConstrainedBox/node1_base.dart deleted file mode 100644 index 65171c1f4..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/ConstrainedBox/node1_base.dart +++ /dev/null @@ -1,75 +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 { - @override - _CustomConstrainedBoxState createState() => _CustomConstrainedBoxState(); -} - -class _CustomConstrainedBoxState extends State { - var _text = ''; - - @override - Widget build(BuildContext context) { - var child = Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 40, - height: 40, - child: Text("Static"), - ); - - var box = ConstrainedBox( - constraints: 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: InputDecoration( - border: OutlineInputBorder(), - hintText: '请输入', - ), - onChanged: (v) { - setState(() { - _text = v; - }); - }, - ), - ); - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/CupertinoTextSelectionToolbar/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/CupertinoTextSelectionToolbar/node1_base.dart deleted file mode 100644 index 489623194..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/CupertinoTextSelectionToolbar/node1_base.dart +++ /dev/null @@ -1,29 +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 -/// 说明: 299 CupertinoTextSelectionToolbar 对文本选择做出响应的 ios 风格的工具栏。 -// { -// "widgetId": 299, -// "name": '该组件无法使用', -// "priority": 1, -// "subtitle": -// "【-】 : - 【-】", -// } - -class CupertinoTextSelectionToolbarDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - padding: EdgeInsets.all(10), - width: 300, - // color: Theme.of(context).primaryColor, - child: Text( - "注:此组件私有构造器,外部无法使用,并没有使用价值。", - style: TextStyle(color: Colors.red, fontSize: 18), - ), - ); - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/CustomPaint/node1_clock.dart b/lib/views/widgets/SingleChildRenderObjectWidget/CustomPaint/node1_clock.dart deleted file mode 100644 index c86f46cd9..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/CustomPaint/node1_clock.dart +++ /dev/null @@ -1,203 +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 { - @override - Widget build(BuildContext context) { - return Container( - width: MediaQuery.of(context).size.width, - height: 100, - child:RepaintBoundary( - child: CustomPaint(//使用CustomPaint盛放画布 - painter: ClockPainter(), - ), - ), - ) - ; - } -} - -class ClockPainter extends CustomPainter { - Paint _paint; - var _radius = 3.0; //小球半径 - Path _path = Path(); //画笔对象 - ClockPainter () { - _paint = Paint()..color= Color(0xff45d0fd)..isAntiAlias=true; - _path.addOval(Rect.fromCircle(radius: _radius, center: 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/lib/views/widgets/SingleChildRenderObjectWidget/CustomPaint/node2_bezier.dart b/lib/views/widgets/SingleChildRenderObjectWidget/CustomPaint/node2_bezier.dart deleted file mode 100644 index fdc124bf8..000000000 --- a/lib/views/widgets/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 { - @override - _PlayBezier3PageState createState() => _PlayBezier3PageState(); -} - -class _PlayBezier3PageState extends State { - List _pos = []; - int selectPos; - - @override - void initState() { - _initPoints(); - super.initState(); - } - - void _initPoints() { - _pos = []; - _pos.add(Offset(0, 0)); - _pos.add(Offset(60, -60)); - _pos.add(Offset(-90, -90)); - _pos.add(Offset(-120, -40)); - } - - - @override - Widget build(BuildContext context) { - return Container( - height: 200, - width: MediaQuery.of(context).size.width, - child: RepaintBoundary( - child: CustomPaint( - painter: BezierPainter(pos: _pos, selectPos: selectPos), - ), - ), - - ); - } -} - -class BezierPainter extends CustomPainter { - Paint _gridPaint; - Path _gridPath; - - Paint _mainPaint; - Path _mainPath; - int selectPos; - Paint _helpPaint; - - List pos; - - BezierPainter({this.pos, 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/lib/views/widgets/SingleChildRenderObjectWidget/CustomSingleChildLayout/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/CustomSingleChildLayout/node1_base.dart deleted file mode 100644 index a3daf4abc..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/CustomSingleChildLayout/node1_base.dart +++ /dev/null @@ -1,61 +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 { - @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/lib/views/widgets/SingleChildRenderObjectWidget/CustomSingleChildLayout/node2_offset.dart b/lib/views/widgets/SingleChildRenderObjectWidget/CustomSingleChildLayout/node2_offset.dart deleted file mode 100644 index 4b5444c05..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/CustomSingleChildLayout/node2_offset.dart +++ /dev/null @@ -1,118 +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 { - final data = [ - { - '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: Icon( - Icons.android, - size: 30, - color: Colors.green, - ), - ))) - .toList()); - } -} - -class OffSetWidget extends StatelessWidget { - final Offset offset; - final Widget child; - final Direction direction; - - OffSetWidget( - {this.offset = Offset.zero, - this.child, - this.direction = Direction.topLeft}); - - @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) { - var w = size.width; - var h = size.height; - var wc = childSize.width; - var 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); - } - return offset; - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node1_base.dart deleted file mode 100644 index c4435a25a..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node1_base.dart +++ /dev/null @@ -1,49 +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 { - final rainbow = const [ - 0xffff0000, - 0xffFF7F00, - 0xffFFFF00, - 0xff00FF00, - 0xff00FFFF, - 0xff0000FF, - 0xff8B00FF - ]; - - @override - Widget build(BuildContext context) { - return DecoratedBox( - position: DecorationPosition.background, - decoration: BoxDecoration( - gradient: LinearGradient( - stops: [0.0, 1 / 6, 2 / 6, 3 / 6, 4 / 6, 5 / 6, 1.0], - colors: rainbow.map((e) => Color(e)).toList()), - borderRadius: 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/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node2_image.dart b/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node2_image.dart deleted file mode 100644 index bdd66b187..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node2_image.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": 70, -// "name": 'DecoratedBox形状和图片装饰', -// "priority": 2, -// "subtitle": -// "【shape】 : 形状 【BoxShape】\n" -// "【image】 : 背景图片 【DecorationImage】\n", -// } -class ShapeImageDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return 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/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node3_border.dart b/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node3_border.dart deleted file mode 100644 index 8b02b5e04..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node3_border.dart +++ /dev/null @@ -1,32 +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 { - @override - Widget build(BuildContext context) { - return DecoratedBox( - position: DecorationPosition.foreground, - decoration: 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/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node4_shape.dart b/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node4_shape.dart deleted file mode 100644 index 1a213d363..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node4_shape.dart +++ /dev/null @@ -1,43 +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 { - @override - Widget build(BuildContext context) { - return DecoratedBox( - decoration: ShapeDecoration( - shadows: [ - const 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/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node5_line.dart b/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node5_line.dart deleted file mode 100644 index 0128892bd..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node5_line.dart +++ /dev/null @@ -1,26 +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 { - @override - Widget build(BuildContext context) { - return 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/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node6_flutterLogo.dart b/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node6_flutterLogo.dart deleted file mode 100644 index f0799a43d..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/DecoratedBox/node6_flutterLogo.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": 'FlutterLogoDecoration装饰', -// "priority": 6, -// "subtitle": "通过FlutterLogoDecoration对象可指定Flutter图标装饰(并没有什么太大的作用)\n", -// } - -class FlutterLogoDecorationDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return 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/lib/views/widgets/SingleChildRenderObjectWidget/FadeTransition/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/FadeTransition/node1_base.dart deleted file mode 100644 index 9e69d828e..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/FadeTransition/node1_base.dart +++ /dev/null @@ -1,52 +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 { - @override - _CustomFadeTransitionState createState() => _CustomFadeTransitionState(); -} - -class _CustomFadeTransitionState extends State - with SingleTickerProviderStateMixin { - AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController(vsync: this, duration: 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: Icon(Icons.android, color: Colors.green, size: 60), - ), - ), - ); - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/FittedBox/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/FittedBox/node1_base.dart deleted file mode 100644 index 7cb27cb5a..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/FittedBox/node1_base.dart +++ /dev/null @@ -1,97 +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 { - @override - _CustomFittedBoxState createState() => _CustomFittedBoxState(); -} - -class _CustomFittedBoxState extends State { - double _childW = 20; - double _childH = 30; - - final rainbow = [ - 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), - 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: [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/lib/views/widgets/SingleChildRenderObjectWidget/FractionalTranslation/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/FractionalTranslation/node1_base.dart deleted file mode 100644 index 9a240549b..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/FractionalTranslation/node1_base.dart +++ /dev/null @@ -1,68 +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 -/// 说明: 263 FractionalTranslation 通过offset属性将子组件进行偏移,偏移量为OffSet横纵*子组件大小。 -// { -// "widgetId": 263, -// "name": "FractionalTranslation基本使用", -// "priority": 1, -// "subtitle": "【translation】 : 偏移分度值 【Offset】\n" -// "【child】: 子组件 【Widget】", -// } - -class FractionalTranslationDemo extends StatefulWidget { - @override - _FractionalTranslationDemoState createState() => - _FractionalTranslationDemoState(); -} - -class _FractionalTranslationDemoState extends State { - var dx = 0.0; - var 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: 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/lib/views/widgets/SingleChildRenderObjectWidget/FractionallySizedBox/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/FractionallySizedBox/node1_base.dart deleted file mode 100644 index 362200bee..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/FractionallySizedBox/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": 82, -// "name": 'FractionallySizedBox基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【widthFactor】 : 宽分率 【double】\n" -// "【heightFactor】 : 高分率 【double】\n" -// "【alignment】 : 对齐方式 【AlignmentGeometry】", -// } -class CustomFractionallySizedBox extends StatefulWidget { - @override - _CustomFractionallySizedBoxState createState() => - _CustomFractionallySizedBoxState(); -} - -class _CustomFractionallySizedBoxState - extends State { - var _hf = 0.5; - var _wf = 0.4; - - @override - Widget build(BuildContext context) { - var 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/lib/views/widgets/SingleChildRenderObjectWidget/IgnorePointer/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/IgnorePointer/node1_base.dart deleted file mode 100644 index f6247ffc8..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/IgnorePointer/node1_base.dart +++ /dev/null @@ -1,62 +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 { - @override - _CustomIgnorePointerState createState() => _CustomIgnorePointerState(); -} - -class _CustomIgnorePointerState extends State { - bool _ignore = false; - - @override - Widget build(BuildContext context) { - return Container( - child: Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - GestureDetector( - onTap: (){ - print('IgnorePointer'); - }, - child: IgnorePointer( - ignoring: _ignore, - child: _buildButton(), - ), - ), - _buildSwitch(), - Text(!_ignore ? '允许点击' : '点击已锁定') - ], - ), - ); - } - - Widget _buildButton() => RaisedButton( - color: Theme.of(context).primaryColor, - child: Text( - 'To About', - style: TextStyle(color: Colors.white), - ), - onPressed: () => Navigator.of(context).pushNamed('AboutMePage')); - - _buildSwitch() => Switch( - value: _ignore, - onChanged: (v) { - setState(() { - _ignore = v; - }); - }); -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/IntrinsicHeight/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/IntrinsicHeight/node1_base.dart deleted file mode 100644 index c9eaeecc2..000000000 --- a/lib/views/widgets/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 { - @override - _IntrinsicHeightDemoState createState() => _IntrinsicHeightDemoState(); -} - -class _IntrinsicHeightDemoState extends State { - var _height =120.0; - - @override - Widget build(BuildContext context) { - return Container( - child: Column( - children: [ - buildChild(_height), - 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: Text("固定高"), - ), - Container( - color: Colors.red, - width: 60, - alignment: Alignment.center, - child: Text("最高"), - ) - ], - ), - ); - } - - Widget _buildSlider() =>Slider( - value: _height, - max: 200.0, - min: 30.0, - divisions: 17, - onChanged: (v)=> setState(() => _height= v), - ); -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/IntrinsicWidth/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/IntrinsicWidth/node1_base.dart deleted file mode 100644 index e3c20aa31..000000000 --- a/lib/views/widgets/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 { - @override - _IntrinsicWidthDemoState createState() => _IntrinsicWidthDemoState(); -} - -class _IntrinsicWidthDemoState extends State { - var _height =120.0; - - @override - Widget build(BuildContext context) { - return Container( - child: Column( - children: [ - buildChild(_height), - 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: Text("固定宽"), - ), - Container( - color: Colors.red, - height: 40, - alignment: Alignment.center, - child: Text("最宽"), - ) - ], - ), - ); - } - - Widget _buildSlider() =>Slider( - value: _height, - max: 200.0, - min: 80.0, - divisions: 17, - onChanged: (v)=> setState(() => _height= v), - ); -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/LayoutBuilder/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/LayoutBuilder/node1_base.dart deleted file mode 100644 index 32bdb1f21..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/LayoutBuilder/node1_base.dart +++ /dev/null @@ -1,34 +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 { - @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: TextStyle(color: Colors.white, fontSize: 16), - ); - }, - ), - ); - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/LayoutBuilder/node2_fit.dart b/lib/views/widgets/SingleChildRenderObjectWidget/LayoutBuilder/node2_fit.dart deleted file mode 100644 index e9d97a249..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/LayoutBuilder/node2_fit.dart +++ /dev/null @@ -1,109 +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 { - @override - _FitByLayoutBuilderState createState() => _FitByLayoutBuilderState(); -} - -class _FitByLayoutBuilderState extends State { - double _width = 100; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Container( - width: _width, - child: LayoutBuilder( - builder: (_, zone) { - if (zone.maxWidth <= 150) { - return _buildType1(); - } else { - return _buildType2(zone); - } - }, - ), - ), - _buildSlider(), - ], - ); - } - - Widget _buildSlider() { - return 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: EdgeInsets.all(10), - height: 80, - width: 30, - color: Colors.grey, - ), - Expanded(child: _buildContent()) - ], - ), - ); - - Widget _buildTitle() => Container( - margin: 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/lib/views/widgets/SingleChildRenderObjectWidget/LayoutBuilder/node3_expend.dart b/lib/views/widgets/SingleChildRenderObjectWidget/LayoutBuilder/node3_expend.dart deleted file mode 100644 index 3b33aeb57..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/LayoutBuilder/node3_expend.dart +++ /dev/null @@ -1,74 +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 { - - @override - createState() => _SimpleExpandableTextState(); -} - -class _SimpleExpandableTextState extends State { - - final text = '桃树、杏树、梨树,你不让我,我不让你,都开满了花赶趟儿。' - '红的像火,粉的像霞,白的像雪。' - '花里带着甜味儿;闭了眼,树上仿佛已经满是桃儿、杏儿、梨儿。' - '花下成千成百的蜜蜂嗡嗡地闹着,大小的蝴蝶飞来飞去。' - '野花遍地是:杂样儿,有名字的,没名字的,散在草丛里,像眼睛,像星星,还眨呀眨的。'; - - bool expand = false; - int maxLines =3; - - - final style = TextStyle(fontSize: 15, color: Colors.grey, shadows: [ - Shadow( - color: Colors.white, offset: Offset(1,1) - ) - ]); - - @override - build(context) => Container( - decoration: BoxDecoration( - color: Colors.cyanAccent.withAlpha(8), - borderRadius: BorderRadiusDirectional.all(Radius.circular(20))), - padding: EdgeInsets.all(15), - child: LayoutBuilder(builder: (context, size) { - - final painter = TextPainter( - text: TextSpan(text: text, style: style), - maxLines: maxLines, - textDirection: TextDirection.ltr, - ); - 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: () => setState(() { - expand = !expand; - }), - child: Text( - expand ? '<< 收起' : '展开 >>', - style: TextStyle(color: Colors.blue), - ), - ), - ], - ); - }), - ); -} - diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/LimiteBox/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/LimiteBox/node1_base.dart deleted file mode 100644 index d1bec3c3d..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/LimiteBox/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": 79, -// "name": 'LimitedBox基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【maxHeight】 : 最大高 【double】\n" -// "【maxWidth】 : 最大宽 【double】", -// } -class CustomLimitedBox extends StatefulWidget { - @override - _CustomLimitedBoxState createState() => _CustomLimitedBoxState(); -} - -class _CustomLimitedBoxState extends State { - var _text = ''; - - @override - Widget build(BuildContext context) { - var child = Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 50, - height: 50, - child: Text("Static"), - ); - - var 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: InputDecoration( - border: OutlineInputBorder(), - hintText: '请输入', - ), - onChanged: (v) { - setState(() { - _text = v; - }); - }, - ), - ); - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/OffStage/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/OffStage/node1_base.dart deleted file mode 100644 index 7719ad4ec..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/OffStage/node1_base.dart +++ /dev/null @@ -1,54 +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 { - @override - _CustomOffstageState createState() => _CustomOffstageState(); -} - -class _CustomOffstageState extends State { - bool _off = false; - - @override - Widget build(BuildContext context) { - var radBox = Container( - height: 50, - width: 60, - color: Colors.red, - child: Switch( - value: _off, - onChanged: (v) => setState(() => _off = v)), - ); - - return Container( - 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: Text( - "Offstage", - style: TextStyle(fontSize: 20), - ), - )); -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/Opacity/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Opacity/node1_base.dart deleted file mode 100644 index aacb7eeb7..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Opacity/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": 73, -// "name": 'Opacity基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【opacity】 : 透明度0~1 【double】", -// } -class CustomOpacity extends StatefulWidget { - @override - _CustomOpacityState createState() => _CustomOpacityState(); -} - -class _CustomOpacityState extends State { - var _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/lib/views/widgets/SingleChildRenderObjectWidget/OverflowBox/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/OverflowBox/node1_base.dart deleted file mode 100644 index 569b04327..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/OverflowBox/node1_base.dart +++ /dev/null @@ -1,68 +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 { - @override - _CustomOverflowBoxState createState() => _CustomOverflowBoxState(); -} - -class _CustomOverflowBoxState extends State { - var _text = ''; - - @override - Widget build(BuildContext context) { - var box = OverflowBox( - alignment: Alignment.center, - minHeight: 50, - minWidth: 50, - maxWidth: 200, - maxHeight: 120, - child: Container( - color: Colors.orange, - child: Text(_text), - ), -// child: 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: InputDecoration( - border: OutlineInputBorder(), - hintText: '请输入', - ), - onChanged: (v) { - setState(() { - _text = v; - }); - }, - ), - ); - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/Padding/node1_all.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Padding/node1_all.dart deleted file mode 100644 index 0f3da50f4..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Padding/node1_all.dart +++ /dev/null @@ -1,40 +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 { - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.grey.withAlpha(22), - width: 200, - height: 150, - child: Padding( - padding: EdgeInsets.all(20), - child: _buildChild(), - ), - ); - } - - Widget _buildChild() { - return Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 100, - height: 100, - child: Text("孩子"), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/Padding/node2_only.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Padding/node2_only.dart deleted file mode 100644 index 81bdb009a..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Padding/node2_only.dart +++ /dev/null @@ -1,38 +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 { - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.grey.withAlpha(22), - width: 200, - height: 150, - child: Padding( - padding: EdgeInsets.only(top:10,left: 10), - child: _buildChild(), - ), - ); - } - - Widget _buildChild() { - return Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 100, - height: 100, - child: Text("孩子"), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/Padding/node3_symmetric.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Padding/node3_symmetric.dart deleted file mode 100644 index 2c7c643b5..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Padding/node3_symmetric.dart +++ /dev/null @@ -1,38 +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 { - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.grey.withAlpha(22), - width: 200, - height: 150, - child: Padding( - padding: EdgeInsets.symmetric(vertical: 30,horizontal: 10), - child: _buildChild(), - ), - ); - } - - Widget _buildChild() { - return Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 100, - height: 100, - child: Text("孩子"), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/PhysicalModel/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/PhysicalModel/node1_base.dart deleted file mode 100644 index ff28e1ef8..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/PhysicalModel/node1_base.dart +++ /dev/null @@ -1,59 +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{ - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Container( - 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), - ), - - Container( - width: 150, - height: 150, - child: PhysicalModel( - shadowColor: Colors.orange, - elevation: 3, - child: Image.asset( - 'assets/images/caver.webp', - fit: BoxFit.cover, - ), - borderRadius: BorderRadius.all(Radius.circular(20)), - clipBehavior: Clip.hardEdge, - shape: BoxShape.rectangle, - color: Colors.deepPurpleAccent), - ), - ], - ); - } - -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/PhysicalShape/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/PhysicalShape/node1_base.dart deleted file mode 100644 index 601cd7f90..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/PhysicalShape/node1_base.dart +++ /dev/null @@ -1,39 +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 -/// 说明: 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 { - @override - Widget build(BuildContext context) { - return Container( - 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: ShapeBorderClipper( - shape: CircleBorder(side: BorderSide.none), - ), - color: Colors.deepPurpleAccent), - ); - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/RepaintBoundary/main.dart b/lib/views/widgets/SingleChildRenderObjectWidget/RepaintBoundary/main.dart deleted file mode 100644 index 05ca9f7b3..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/RepaintBoundary/main.dart +++ /dev/null @@ -1,24 +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(MyApp()); - -class MyApp extends StatelessWidget { - @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/lib/views/widgets/SingleChildRenderObjectWidget/RepaintBoundary/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/RepaintBoundary/node1_base.dart deleted file mode 100644 index abd4c46c5..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/RepaintBoundary/node1_base.dart +++ /dev/null @@ -1,182 +0,0 @@ -import 'dart:ui'; - -import 'package:flutter/cupertino.dart'; -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{ - @override - Widget build(BuildContext context) { - return RepaintBoundary( - child: TempPlayBezier3Page(), - ); - } -} - -class TempPlayBezier3Page extends StatefulWidget { - @override - _TempPlayBezier3PageState createState() => _TempPlayBezier3PageState(); -} - -class _TempPlayBezier3PageState extends State { - List _pos = []; - int selectPos; - - @override - void initState() { - _initPoints(); - super.initState(); - } - - void _initPoints() { - _pos = []; - _pos.add(Offset(0, 0)); - _pos.add(Offset(60, -60)); - _pos.add(Offset(-90, -90)); - _pos.add(Offset(-120, -40)); - } - - @override - Widget build(BuildContext context) { - return Container( - height: 200, - width: MediaQuery.of(context).size.width, - child: CustomPaint( - painter: TempBezierPainter(pos: _pos, selectPos: selectPos), - ), - ); - } -} - -class TempBezierPainter extends CustomPainter { - Paint _gridPaint; - Path _gridPath; - - Paint _mainPaint; - Path _mainPath; - int selectPos; - Paint _helpPaint; - - List pos; - - TempBezierPainter({this.pos, 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/lib/views/widgets/SingleChildRenderObjectWidget/RepaintBoundary/node2_save.dart b/lib/views/widgets/SingleChildRenderObjectWidget/RepaintBoundary/node2_save.dart deleted file mode 100644 index 7d700f2c4..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/RepaintBoundary/node2_save.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'dart:io'; -import 'dart:typed_data'; -import 'dart:ui'; - -import 'package:flutter/cupertino.dart'; -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(); - - @override - Widget build(BuildContext context) { - return Stack( - children: [ - RepaintBoundary( - key: _globalKey, - child: TempPlayBezier3Page(), - ), - Positioned(right: -10, child: _buildButton3(context)) - ], - ); - } - - Widget _buildButton3(context) => MaterialButton( - child: Icon( - Icons.save_alt, - size: 15, - color: Colors.white, - ), - color: Colors.green, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onPressed: () async { - var bits = await _widget2Image(_globalKey); - var dir = await getApplicationSupportDirectory(); - var file = File(dir.path + "/save_img.png"); - var f = await file.writeAsBytes(bits); - Scaffold.of(context).showSnackBar(SnackBar( - backgroundColor: Theme.of(context).primaryColor, - content: Text('保存成功后! 路径为:${f.path}'), - )); - }); - - Future _widget2Image(GlobalKey key) async { - RenderRepaintBoundary boundary = key.currentContext.findRenderObject(); - //获得 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/lib/views/widgets/SingleChildRenderObjectWidget/RotatedBox/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/RotatedBox/node1_base.dart deleted file mode 100644 index ec9e6e68c..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/RotatedBox/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": 72, -// "name": 'RotatedBox基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【quarterTurns】 : 旋转多少个90° 【int】", -// } -class CustomRotatedBox extends StatefulWidget { - @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: Icon( - Icons.android, - size: 60, - color: Colors.blue, - )), - ); - } -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/ShaderMask/node1_radial.dart b/lib/views/widgets/SingleChildRenderObjectWidget/ShaderMask/node1_radial.dart deleted file mode 100644 index e37ec4d67..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/ShaderMask/node1_radial.dart +++ /dev/null @@ -1,59 +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 { - @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: Text( - '张风捷特烈', - style: TextStyle(fontSize: 40, color: Colors.white), - ), - ), - ShaderMask( - shaderCallback: _buildShader, - child: Container( - height: 100, - color: Colors.white, - width: 50, - ), - ), - ], - ); - } - - final colors = [Colors.red, Colors.yellow, Colors.blue]; - - 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/lib/views/widgets/SingleChildRenderObjectWidget/ShaderMask/node2_linear.dart b/lib/views/widgets/SingleChildRenderObjectWidget/ShaderMask/node2_linear.dart deleted file mode 100644 index 2004b0e17..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/ShaderMask/node2_linear.dart +++ /dev/null @@ -1,56 +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 { - @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: Text( - '张风捷特烈', - style: TextStyle(fontSize: 40, color: Colors.white), - ), - ), - ShaderMask( - shaderCallback: _buildShader, - child: Container( - height: 100, - color: Colors.white, - width: 50, - ), - ), - ], - ); - } - - final colors = [Colors.red, Colors.yellow, Colors.blue]; - - Shader _buildShader(Rect bounds) => LinearGradient( - begin: Alignment.centerLeft, - end: Alignment.centerRight, - tileMode: TileMode.mirror, - colors: colors) - .createShader(bounds); -} diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/SizeChangedLayoutNotifier/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/SizeChangedLayoutNotifier/node1_base.dart deleted file mode 100644 index 9e3b22f32..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/SizeChangedLayoutNotifier/node1_base.dart +++ /dev/null @@ -1,71 +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 -/// 说明: 294 SizeChangedLayoutNotifier 尺寸变化通告 使用 SizeChangedLayoutNotifier 可以在子组件布局区域发生变化后,发出通知。使用NotificationListener可以进行监听。 -// { -// "widgetId": 294, -// "name": '基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 组件 【Widget】", -// } - -class SizeChangedLayoutNotifierDemo extends StatefulWidget { - @override - _SizeChangedLayoutNotifierDemoState createState() => _SizeChangedLayoutNotifierDemoState(); -} - -class _SizeChangedLayoutNotifierDemoState extends State { - @override - Widget build(BuildContext context) { - return NotificationListener( - onNotification: _onNotification, - child: ChangeableBox(), - ); - } - - bool _onNotification(SizeChangedLayoutNotification notification) { - print('---------SizeChangedLayoutNotification------'); - return false; - } -} - -class ChangeableBox extends StatefulWidget { - @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/lib/views/widgets/SingleChildRenderObjectWidget/SizedBox/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/SizedBox/node1_base.dart deleted file mode 100644 index c070de058..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/SizedBox/node1_base.dart +++ /dev/null @@ -1,46 +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 { - @override - Widget build(BuildContext context) { - var child = Container( - alignment: Alignment.center, - color: Colors.cyanAccent, - width: 50, - height: 50, - child: Text("Static"), - ); - - var box = SizedBox( - width: 80, - height: 40, - child: Container( - color: Colors.orange, - child: 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/lib/views/widgets/SingleChildRenderObjectWidget/SizedOverflowBox/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/SizedOverflowBox/node1_base.dart deleted file mode 100644 index a604a534c..000000000 --- a/lib/views/widgets/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 { - - @override - _CustomSizedOverflowBoxState createState() => _CustomSizedOverflowBoxState(); -} - -class _CustomSizedOverflowBoxState extends State { - double _x = 50; - double _y = 44; - - @override - Widget build(BuildContext context) { - var 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/lib/views/widgets/SingleChildRenderObjectWidget/Transform/matrix4_shower.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Transform/matrix4_shower.dart deleted file mode 100644 index 1c14c72a5..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Transform/matrix4_shower.dart +++ /dev/null @@ -1,24 +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; - - Matrix4Shower(this.matrix4); - - @override - Widget build(BuildContext context) { - return Container( - child: 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: TextStyle(fontSize: 20, color: Colors.blue), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node1_skew.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node1_skew.dart deleted file mode 100644 index 067c9c0e6..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node1_skew.dart +++ /dev/null @@ -1,90 +0,0 @@ -import 'dart:math'; - -import 'package:flutter/cupertino.dart'; -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 { - @override - _SkewTransformState createState() => _SkewTransformState(); -} - -class _SkewTransformState extends State { - 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/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node2_translation.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node2_translation.dart deleted file mode 100644 index 4d3980eef..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node2_translation.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": '平移变换translationValues', -// "priority": 2, -// "subtitle": -// "平移x由R0C3数控制,入参为数值,表示平移长度\n" -// "平移y由R1C3数控制,入参为数值,表示平移长度\n" -// "平移z由R2C3数控制,入参为数值,表示平移长度", -// } -class TranslationTransform extends StatefulWidget { - @override - _TranslationTransformState createState() => _TranslationTransformState(); -} - -class _TranslationTransformState extends State { - 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/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node3_scale.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node3_scale.dart deleted file mode 100644 index ef27825b6..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node3_scale.dart +++ /dev/null @@ -1,99 +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 { - @override - _ScaleTransformState createState() => _ScaleTransformState(); -} - -class _ScaleTransformState extends State { - 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/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node4_rotate.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node4_rotate.dart deleted file mode 100644 index 2becac149..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node4_rotate.dart +++ /dev/null @@ -1,120 +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 { - @override - _RotateTransformState createState() => _RotateTransformState(); -} - -class _RotateTransformState extends State { - 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/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node5_perspective.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node5_perspective.dart deleted file mode 100644 index 97b37a4cd..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Transform/node5_perspective.dart +++ /dev/null @@ -1,79 +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 { - @override - _R3C2State createState() => _R3C2State(); -} - -class _R3C2State extends State { - 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/lib/views/widgets/SingleChildRenderObjectWidget/Transform/zz_node_op.dart b/lib/views/widgets/SingleChildRenderObjectWidget/Transform/zz_node_op.dart deleted file mode 100644 index 9e69fce33..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/Transform/zz_node_op.dart +++ /dev/null @@ -1,96 +0,0 @@ -import 'package:flutter/material.dart'; - -/// create by 张风捷特烈 on 2020/4/30 -/// contact me by email 1981462002@qq.com -/// 说明: - -class CustomTransform extends StatefulWidget { - @override - _CustomTransformState createState() => _CustomTransformState(); -} - -class _CustomTransformState extends State { - var angle = 0.0; - var 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: TextStyle(fontSize: 20), - ) - ], - ), - Container( - 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/lib/views/widgets/SingleChildRenderObjectWidget/UnConstrainedBox/node1_base.dart b/lib/views/widgets/SingleChildRenderObjectWidget/UnConstrainedBox/node1_base.dart deleted file mode 100644 index 36b143fad..000000000 --- a/lib/views/widgets/SingleChildRenderObjectWidget/UnConstrainedBox/node1_base.dart +++ /dev/null @@ -1,81 +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 { - @override - _CustomUnConstrainedBoxState createState() => _CustomUnConstrainedBoxState(); -} - -class _CustomUnConstrainedBoxState extends State { - var _value = false; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [_buildUnconstrainedBox(), _buildConstrainedAxis()], - ); - } - - Widget _buildUnconstrainedBox() { - var 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, - )), - ), - Text("竖直方向仍约束") - ], - ); - } -} diff --git a/lib/views/widgets/Sliver/CupertinoSliverNavigationBar/node1_base.dart b/lib/views/widgets/Sliver/CupertinoSliverNavigationBar/node1_base.dart deleted file mode 100644 index 6f78cef81..000000000 --- a/lib/views/widgets/Sliver/CupertinoSliverNavigationBar/node1_base.dart +++ /dev/null @@ -1,99 +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 { - final 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 Container( - height: 300, - child: CustomScrollView( - slivers: [ - CupertinoSliverNavigationBar( - trailing: Icon( - CupertinoIcons.share, - size: 25, - ), - leading: _buildLeading(), - backgroundColor: Colors.white, - // middle: Text('张风捷特烈'), - largeTitle: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - Icons.ac_unit, - size: 20, - ), - Padding( - padding: const 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: TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); - - Widget _buildLeading() => Container( - margin: 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/lib/views/widgets/Sliver/CupertinoSliverRefreshControl/node1_base.dart b/lib/views/widgets/Sliver/CupertinoSliverRefreshControl/node1_base.dart deleted file mode 100644 index 7e2e4ddf5..000000000 --- a/lib/views/widgets/Sliver/CupertinoSliverRefreshControl/node1_base.dart +++ /dev/null @@ -1,127 +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 { - @override - _CupertinoSliverRefreshControlDemoState createState() => - _CupertinoSliverRefreshControlDemoState(); -} - -class _CupertinoSliverRefreshControlDemoState - extends State { - final 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 r = Random(); - - @override - Widget build(BuildContext context) { - return Container( - height: 300, - child: CustomScrollView( - physics: 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: 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: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')), - title: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: 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(Duration(seconds: 2)); - setState(() { - data.insertAll(0, [randomColor()]); - }); - } -} diff --git a/lib/views/widgets/Sliver/CustomScrollView/node1_base.dart b/lib/views/widgets/Sliver/CustomScrollView/node1_base.dart deleted file mode 100644 index 2ff09ea47..000000000 --- a/lib/views/widgets/Sliver/CustomScrollView/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": 183, -// "name": 'CustomScrollView基本使用', -// "priority": 1, -// "subtitle": -// "【slivers】 : 子组件列表 【List】\n" -// "【reverse】 : 是否反向 【bool】\n" -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【controller】 : 控制器 【ScrollController】", -// } -class CustomScrollViewDemo extends StatelessWidget { - 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 Container( - height: 300, - child: CustomScrollView( - anchor: 0, - scrollDirection: Axis.vertical, - reverse: false, - slivers: [_buildSliverAppBar(), _buildSliverFixedExtentList()], - ), - ); - } - - 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(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()}"; - - _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')), - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - title: 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/lib/views/widgets/Sliver/FlexibleSpaceBar/node1_base.dart b/lib/views/widgets/Sliver/FlexibleSpaceBar/node1_base.dart deleted file mode 100644 index 39c745b2c..000000000 --- a/lib/views/widgets/Sliver/FlexibleSpaceBar/node1_base.dart +++ /dev/null @@ -1,109 +0,0 @@ -/// create by 张风捷特烈 on 2020-03-31 -/// contact me by email 1981462002@qq.com -/// 说明: - -import 'package:flutter/material.dart'; - -/// 说明: -// { -// "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 { - - final 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 - Container( - 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: Text('张风捷特烈',style: TextStyle(shadows: [ - Shadow(color: Colors.blue, offset: Offset(1, 1), blurRadius: 2) - ]),), - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - stretchModes: [StretchMode.blurBackground,StretchMode.zoomBackground], - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: 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: 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/lib/views/widgets/Sliver/SliverAnimatedList/node1_base.dart b/lib/views/widgets/Sliver/SliverAnimatedList/node1_base.dart deleted file mode 100644 index be69cccb1..000000000 --- a/lib/views/widgets/Sliver/SliverAnimatedList/node1_base.dart +++ /dev/null @@ -1,203 +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 -/// 说明: 301 SliverAnimatedList Sliver动画列表 在插入或删除项目时使其有动画效果的sliver组件。 -// { -// "widgetId": 301, -// "name": 'SliverAnimatedList基本使用', -// "priority": 1, -// "subtitle": -// "【itemBuilder】 : item构造器 【AnimatedListItemBuilder】\n" -// "【initialItemCount】 : 初始item个数 【int】", -// } -class SliverAnimatedListDemo extends StatefulWidget { - @override - _SliverAnimatedListDemoState createState() => - _SliverAnimatedListDemoState(); -} - -class _SliverAnimatedListDemoState extends State { - - final GlobalKey _listKey = GlobalKey(); - ListModel _list; - int _selectedItem; - int _nextItem; - - @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 Container( - height: 300, - child: CustomScrollView( - slivers: [ - SliverAppBar( - title: 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, - Iterable initialItems, - }) : assert(listKey != null), - 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, - }) : assert(animation != null), - assert(item != null && item >= 0), - assert(selected != null), - 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: TextStyle(color: Colors.white,fontSize: 16), - - ), - ), - ), - ), - ), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/Sliver/SliverAppBar/node1_base.dart b/lib/views/widgets/Sliver/SliverAppBar/node1_base.dart deleted file mode 100644 index d48f58731..000000000 --- a/lib/views/widgets/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 { - @override - _SliverAppBarDemoState createState() => _SliverAppBarDemoState(); -} - -class _SliverAppBarDemoState extends State { - bool _floating = false; - bool _pinned = false; - bool _snap = false; - - final 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(), - Container( - height: 300, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - _buildSliverFixedExtentList() - ], - ), - ), - ], - ); - } - - Widget _buildSliverAppBar() { - print(_floating); - return SliverAppBar( - expandedHeight: 190.0, - leading: _buildLeading(), - title: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - floating: _floating, - pinned: _pinned, - snap: _snap, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar(//伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: 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: 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: [ - Text('floating'), - Switch( - value: _floating, - onChanged: (v) { - if(_snap&&!v){ - _snap =false; - } - setState(() => _floating = v); - }), - ], - ), - Wrap( - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Text('pinned'), - Switch( - value: _pinned, - onChanged: (v) => setState(() => _pinned = v)), - ], - ) ,Wrap( - direction: Axis.vertical, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Text('snap'), - Switch( - value: _snap, - onChanged: (v) { - if(_floating){ - setState(() => _snap = v); - } - - }), - ], - ) - ], - ); - } -} diff --git a/lib/views/widgets/Sliver/SliverFillRemaining/node1_base.dart b/lib/views/widgets/Sliver/SliverFillRemaining/node1_base.dart deleted file mode 100644 index 31d43578f..000000000 --- a/lib/views/widgets/Sliver/SliverFillRemaining/node1_base.dart +++ /dev/null @@ -1,156 +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 -/// 说明: 306 SliverFillRemaining Sliver填补剩余 一个包含单个box子元素的sliver,它填充了视窗中的剩余空间。 -// { -// "widgetId": 306, -// "name": 'SliverFillRemaining基本使用', -// "priority": 1, -// "subtitle": -// "【hasScrollBody】 : 是否具有滚动主体 【bool】\n" -// "【fillOverscroll】 : 是否可填充滚动区域 【bool】\n" -// "【child】 : 子组件 【Widget】", -// } -class SliverFillRemainingDemo extends StatefulWidget { - @override - _SliverFillRemainingDemoState createState() => - _SliverFillRemainingDemoState(); -} - -class _SliverFillRemainingDemoState extends State { - final 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 r = Random(); - - bool hasScrollBody = false; - bool fillOverscroll = true; - - @override - Widget build(BuildContext context) { - return Container( - height: 300, - child: CustomScrollView( - physics: BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), - slivers: [ - _buildSliverAppBar(), - _buildSliverList(), - SliverFillRemaining( - hasScrollBody: hasScrollBody, - fillOverscroll: fillOverscroll, - child: Container( - decoration: 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: [ - RaisedButton( - color: Colors.blue, - onPressed: () { - setState(() { - hasScrollBody = !hasScrollBody; - }); - }, - child: Text('hasScrollBody:$hasScrollBody',style: TextStyle(color: Colors.white),), - ), - RaisedButton( - color: Colors.blue, - - onPressed: () { - setState(() { - fillOverscroll = !fillOverscroll; - }); - }, - child: Text('fillOverscroll:$fillOverscroll',style: 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: 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: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')), - title: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - -} diff --git a/lib/views/widgets/Sliver/SliverFillViewport/node1_base.dart b/lib/views/widgets/Sliver/SliverFillViewport/node1_base.dart deleted file mode 100644 index 01d5237e5..000000000 --- a/lib/views/widgets/Sliver/SliverFillViewport/node1_base.dart +++ /dev/null @@ -1,117 +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 { - @override - _SliverFillViewportDemoState createState() => _SliverFillViewportDemoState(); -} - -class _SliverFillViewportDemoState extends State { - final 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], - ]; - var _viewportFraction = 0.5; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildTool(), - Container( - 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: 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: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: 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/lib/views/widgets/Sliver/SliverFixedExtentList/node1_base.dart b/lib/views/widgets/Sliver/SliverFixedExtentList/node1_base.dart deleted file mode 100644 index c43fae0cd..000000000 --- a/lib/views/widgets/Sliver/SliverFixedExtentList/node1_base.dart +++ /dev/null @@ -1,101 +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 { - @override - _SliverFixedExtentListDemoState createState() => _SliverFixedExtentListDemoState(); -} - -class _SliverFixedExtentListDemoState extends State { - final 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 Container( - 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: 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: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/lib/views/widgets/Sliver/SliverGrid/node1_base.dart b/lib/views/widgets/Sliver/SliverGrid/node1_base.dart deleted file mode 100644 index 3781a8ec5..000000000 --- a/lib/views/widgets/Sliver/SliverGrid/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": 188, -// "name": 'SliverList基本使用', -// "priority": 1, -// "subtitle": -// "SliverGrid.count 指定轴向数量构造\n" -// "SliverGrid.extent 指定轴向长度构造\n" -// "属性特征同GridView,可详见之", -// } -class SliverGirdDemo extends StatelessWidget { - final data = List.generate(128, (i) => Color(0xFF6600FF - 2 * i)); - - @override - Widget build(BuildContext context) { - return Container( - 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: 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: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/lib/views/widgets/Sliver/SliverIgnorePointer/node1_base.dart b/lib/views/widgets/Sliver/SliverIgnorePointer/node1_base.dart deleted file mode 100644 index 104668327..000000000 --- a/lib/views/widgets/Sliver/SliverIgnorePointer/node1_base.dart +++ /dev/null @@ -1,158 +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 -/// 说明: 305 SliverIgnorePointer Sliver忽略事件 可以包裹一个sliver组件,通过ignoring来控制该sliver组件是否可以响应事件。 -// { -// "widgetId": 305, -// "name": 'SliverIgnorePointer基本使用', -// "priority": 1, -// "subtitle": -// "【sliver】 : sliver组件 【Widget】\n" -// "【ignoring】 : 是否忽略事件 【bool】\n", -// } -class SliverIgnorePointerDemo extends StatefulWidget { - @override - _SliverIgnorePointerDemoState createState() => - _SliverIgnorePointerDemoState(); -} - -class _SliverIgnorePointerDemoState extends State { - final 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 r = Random(); - - bool hasScrollBody = false; - bool fillOverscroll = true; - - @override - Widget build(BuildContext context) { - return Container( - height: 300, - child: CustomScrollView( - physics: BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), - slivers: [ - _buildSliverAppBar(), - _buildSliverList(), - SliverIgnorePointer( - ignoring: true, - sliver: SliverFillRemaining( - hasScrollBody: hasScrollBody, - fillOverscroll: fillOverscroll, - child: Container( - decoration: 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: [ - RaisedButton( - color: Colors.blue, - onPressed: () { - setState(() { - hasScrollBody = !hasScrollBody; - }); - }, - child: Text('hasScrollBody:$hasScrollBody',style: TextStyle(color: Colors.white),), - ), - RaisedButton( - color: Colors.blue, - - onPressed: () { - setState(() { - fillOverscroll = !fillOverscroll; - }); - }, - child: Text('fillOverscroll:$fillOverscroll',style: 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: 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: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')), - title: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; - -} diff --git a/lib/views/widgets/Sliver/SliverLayoutBuilder/node1_base.dart b/lib/views/widgets/Sliver/SliverLayoutBuilder/node1_base.dart deleted file mode 100644 index 274de28e9..000000000 --- a/lib/views/widgets/Sliver/SliverLayoutBuilder/node1_base.dart +++ /dev/null @@ -1,123 +0,0 @@ - -import 'package:flutter/cupertino.dart'; -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 { - @override - _SliverLayoutBuilderDemoState createState() => - _SliverLayoutBuilderDemoState(); -} - -class _SliverLayoutBuilderDemoState extends State { - final 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 Container( - height: 300, - child: CustomScrollView( - physics: 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: 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: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')), - title: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: 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: Text( - "SliverLayoutBuilder", - style: TextStyle(color: Colors.white, fontSize: 20), - ), - ), - ); - } -} diff --git a/lib/views/widgets/Sliver/SliverList/node1_base.dart b/lib/views/widgets/Sliver/SliverList/node1_base.dart deleted file mode 100644 index 06c62af31..000000000 --- a/lib/views/widgets/Sliver/SliverList/node1_base.dart +++ /dev/null @@ -1,94 +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 { - final 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 Container( - 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: 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: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/lib/views/widgets/Sliver/SliverOpacity/node1_base.dart b/lib/views/widgets/Sliver/SliverOpacity/node1_base.dart deleted file mode 100644 index f3e0c2665..000000000 --- a/lib/views/widgets/Sliver/SliverOpacity/node1_base.dart +++ /dev/null @@ -1,93 +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 { - final data = List.generate(128, (i) => Color(0xFF6600FF - 2 * i)); - - @override - Widget build(BuildContext context) { - return Container( - height: 300, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - SliverPadding( - padding: 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: 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: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/lib/views/widgets/Sliver/SliverOverlapAbsorber/node1_base.dart b/lib/views/widgets/Sliver/SliverOverlapAbsorber/node1_base.dart deleted file mode 100644 index 6767c4db2..000000000 --- a/lib/views/widgets/Sliver/SliverOverlapAbsorber/node1_base.dart +++ /dev/null @@ -1,97 +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 { - final _tabs = ['风神传', '封妖志', "幻将录", "永恒传说"]; - - @override - Widget build(BuildContext context) { - return Container( - 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/lib/views/widgets/Sliver/SliverOverlapInjector/node1_base.dart b/lib/views/widgets/Sliver/SliverOverlapInjector/node1_base.dart deleted file mode 100644 index f0cb84326..000000000 --- a/lib/views/widgets/Sliver/SliverOverlapInjector/node1_base.dart +++ /dev/null @@ -1,97 +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 { - final _tabs = ['风神传', '封妖志', "幻将录", "永恒传说"]; - - @override - Widget build(BuildContext context) { - return Container( - 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/lib/views/widgets/Sliver/SliverPadding/node1_base.dart b/lib/views/widgets/Sliver/SliverPadding/node1_base.dart deleted file mode 100644 index f77e6e5ee..000000000 --- a/lib/views/widgets/Sliver/SliverPadding/node1_base.dart +++ /dev/null @@ -1,91 +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 { - final data = List.generate(128, (i) => Color(0xFF6600FF - 2 * i)); - - @override - Widget build(BuildContext context) { - return Container( - height: 300, - child: CustomScrollView( - slivers: [_buildSliverAppBar(), SliverPadding( - padding: 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: 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: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/lib/views/widgets/Sliver/SliverPersistentHeader/node1_base.dart b/lib/views/widgets/Sliver/SliverPersistentHeader/node1_base.dart deleted file mode 100644 index 1e1a6073d..000000000 --- a/lib/views/widgets/Sliver/SliverPersistentHeader/node1_base.dart +++ /dev/null @@ -1,165 +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 { - final 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 Container( - height: 500, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - _buildPersistentHeader('袅缈岁月,青丝银发',Color(0xffe7fcc9)), - _buildCommonWidget(), - _buildPersistentHeader('以梦为马,不负韶华',Color(0xffcca4ff)), - _buildSliverList() - ], - ), - ); - } - - Widget _buildCommonWidget() => SliverToBoxAdapter( - child: Container( - padding: EdgeInsets.symmetric(horizontal: 10), - color: Colors.grey.withAlpha(22), - child: ListTile( - leading: Image.asset("assets/images/icon_head.webp"), - title: Text("以梦为马"), - subtitle: Text("海子"), - selected: true, - contentPadding: EdgeInsets.all(5), - trailing: 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: 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: 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: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 2, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: 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 new 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/lib/views/widgets/Sliver/SliverPrototypeExtentList/SliverPrototypeExtentList.dart b/lib/views/widgets/Sliver/SliverPrototypeExtentList/SliverPrototypeExtentList.dart deleted file mode 100644 index e68f8452b..000000000 --- a/lib/views/widgets/Sliver/SliverPrototypeExtentList/SliverPrototypeExtentList.dart +++ /dev/null @@ -1,105 +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 { - @override - _SliverPrototypeExtentListDemoState createState() => - _SliverPrototypeExtentListDemoState(); -} - -class _SliverPrototypeExtentListDemoState - extends State { - final 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 Container( - 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: 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: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/lib/views/widgets/Sliver/SliverPrototypeExtentList/node1_base.dart b/lib/views/widgets/Sliver/SliverPrototypeExtentList/node1_base.dart deleted file mode 100644 index a8f3b1217..000000000 --- a/lib/views/widgets/Sliver/SliverPrototypeExtentList/node1_base.dart +++ /dev/null @@ -1,105 +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 { - @override - _SliverPrototypeExtentListDemoState createState() => - _SliverPrototypeExtentListDemoState(); -} - -class _SliverPrototypeExtentListDemoState - extends State { - final 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 Container( - 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: 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: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 5, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/lib/views/widgets/Sliver/SliverToBoxAdapter/node1_base.dart b/lib/views/widgets/Sliver/SliverToBoxAdapter/node1_base.dart deleted file mode 100644 index 167baa32e..000000000 --- a/lib/views/widgets/Sliver/SliverToBoxAdapter/node1_base.dart +++ /dev/null @@ -1,113 +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 { - final 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 Container( - height: 300, - child: CustomScrollView( - slivers: [ - _buildSliverAppBar(), - _buildCommonWidget(), - _buildSliverList() - ], - ), - ); - } - - Widget _buildCommonWidget() => SliverToBoxAdapter( - child: Container( - padding: EdgeInsets.symmetric(horizontal: 10), - color: Colors.grey.withAlpha(22), - child: ListTile( - leading: Image.asset("assets/images/icon_head.webp"), - title: Text("以梦为马"), - subtitle: Text("海子"), - selected: true, - contentPadding: EdgeInsets.all(5), - trailing: 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: 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: Text('张风捷特烈'), - actions: _buildActions(), - elevation: 2, - pinned: true, - backgroundColor: Colors.orange, - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - background: Image.asset( - "assets/images/caver.webp", - fit: BoxFit.cover, - ), - ), - ); - } - - Widget _buildLeading() => Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')); - - List _buildActions() => [ - IconButton( - onPressed: () {}, - icon: Icon( - Icons.star_border, - color: Colors.white, - ), - ) - ]; - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/lib/views/widgets/Sliver/SliverWithKeepAliveWidget/node1_base.dart b/lib/views/widgets/Sliver/SliverWithKeepAliveWidget/node1_base.dart deleted file mode 100644 index cd3d8a775..000000000 --- a/lib/views/widgets/Sliver/SliverWithKeepAliveWidget/node1_base.dart +++ /dev/null @@ -1,35 +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 { - 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: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatefulWidget/AlignTransition/node1_base.dart b/lib/views/widgets/StatefulWidget/AlignTransition/node1_base.dart deleted file mode 100644 index 2c0cac90f..000000000 --- a/lib/views/widgets/StatefulWidget/AlignTransition/node1_base.dart +++ /dev/null @@ -1,53 +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 { - @override - _CustomAlignTransitionState createState() => _CustomAlignTransitionState(); -} - -class _CustomAlignTransitionState extends State - with SingleTickerProviderStateMixin { - AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController(vsync: this, duration: 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: Container( - child: Icon(Icons.android, color: Colors.green, size: 60)), - ), - )); - } -} diff --git a/lib/views/widgets/StatefulWidget/AnimatedAlign/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedAlign/node1_base.dart deleted file mode 100644 index e4481436e..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedAlign/node1_base.dart +++ /dev/null @@ -1,72 +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 { - @override - _CustomAnimatedAlignState createState() => _CustomAnimatedAlignState(); -} - -class _CustomAnimatedAlignState extends State { - final Alignment start = Alignment(0, 0); - final Alignment end = Alignment.bottomRight; - - 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: Duration(seconds: 1), - curve: Curves.fastOutSlowIn, - alignment: _alignment, - onEnd: () => print('End'), - child: Container( - height: 40, - width: 80, - alignment: Alignment.center, - color: Colors.blue, - child: Text( - '张风捷特烈', - style: TextStyle(color: Colors.white), - ), - ), - ), - ), - ], - ); - } - - Widget _buildSwitch() => Switch( - value: _alignment == end, - onChanged: (v) { - setState(() { - _alignment = v ? end : start; - }); - }); -} diff --git a/lib/views/widgets/StatefulWidget/AnimatedBuilder/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedBuilder/node1_base.dart deleted file mode 100644 index 01ca1c7b2..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedBuilder/node1_base.dart +++ /dev/null @@ -1,68 +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 { - @override - _AnimatedBuilderDemoState createState() => _AnimatedBuilderDemoState(); -} - -class _AnimatedBuilderDemoState extends State - with SingleTickerProviderStateMixin { - 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: BoxDecoration(color: Colors.orange, shape: BoxShape.circle), - alignment: Alignment.center, - child: Text( - 'Toly', - style: TextStyle(fontSize: 40, color: Colors.white), - ), - ); -} diff --git a/lib/views/widgets/StatefulWidget/AnimatedContainer/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedContainer/node1_base.dart deleted file mode 100644 index fcc3f7be7..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedContainer/node1_base.dart +++ /dev/null @@ -1,111 +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 { - @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 + Alignment(0.2, 0.2); - final Alignment endAlignment = Alignment.center; - - final double startHeight = 150.0; - final double endHeight = 100.0; - - Decoration _decoration; - double _height; - 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/lib/views/widgets/StatefulWidget/AnimatedCrossFade/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedCrossFade/node1_base.dart deleted file mode 100644 index cc72e64c6..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedCrossFade/node1_base.dart +++ /dev/null @@ -1,71 +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 { - @override - _CustomAnimatedCrossFadeState createState() => - _CustomAnimatedCrossFadeState(); -} - -class _CustomAnimatedCrossFadeState extends State { - var _crossFadeState = CrossFadeState.showFirst; - - bool get isFirst => _crossFadeState == CrossFadeState.showFirst; - - @override - Widget build(BuildContext context) { - return Wrap( - children: [ - Container( - child: AnimatedCrossFade( - firstChild: Container( - alignment: Alignment.center, - width: 200, - height: 150, - color: Colors.orange, - child: FlutterLogo(textColor: Colors.blue, size: 100,), - ), - secondChild: Container( - width: 200, - height: 150, - alignment: Alignment.center, - color: Colors.blue, - child: FlutterLogo( - textColor: Colors.white, -// colors: Colors.orange, - size: 100, - style: FlutterLogoStyle.stacked,), - ), - duration: 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/lib/views/widgets/StatefulWidget/AnimatedCrossFade/node2_curve.dart b/lib/views/widgets/StatefulWidget/AnimatedCrossFade/node2_curve.dart deleted file mode 100644 index e4ac90f06..000000000 --- a/lib/views/widgets/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 { - @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: [ - Container( - child: AnimatedCrossFade( - firstCurve: Curves.easeInCirc, - secondCurve: Curves.easeInToLinear, - sizeCurve: Curves.bounceOut, - firstChild: Container( - alignment: Alignment.center, - width: 200, - height: 80, - color: Colors.orange , - child: FlutterLogo(textColor: Colors.blue,size: 50,), - ), - secondChild: Container( - width: 200, - height: 150, - alignment: Alignment.center, - color: Colors.blue, - child: FlutterLogo( - textColor: Colors.white, -// colors: Colors.orange, - size: 100,style: FlutterLogoStyle.stacked,), - ), - duration: Duration(milliseconds: 1000), - crossFadeState: _crossFadeState, - ), - ), - _buildSwitch(), - ], - ); - } - - Widget _buildSwitch() => Switch(value: isFirst, onChanged: (v){ - setState(() { - _crossFadeState= v?CrossFadeState.showFirst:CrossFadeState.showSecond; - }); - }); -} diff --git a/lib/views/widgets/StatefulWidget/AnimatedDefaultTextStyle/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedDefaultTextStyle/node1_base.dart deleted file mode 100644 index 0b6f30a04..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedDefaultTextStyle/node1_base.dart +++ /dev/null @@ -1,80 +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 { - @override - _CustomAnimatedDefaultTextStyleState createState() => - _CustomAnimatedDefaultTextStyleState(); -} - -class _CustomAnimatedDefaultTextStyleState - extends State { - final TextStyle start = TextStyle(color: Colors.blue, fontSize: 50, shadows: [ - Shadow(offset: Offset(1, 1), color: Colors.black, blurRadius: 3) - ]); - final TextStyle end = TextStyle(color: Colors.white, fontSize: 20, shadows: [ - Shadow(offset: Offset(1, 1), color: Colors.purple, blurRadius: 3) - ]); - - 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: Duration(seconds: 1), - curve: Curves.fastOutSlowIn, - style: _style, - onEnd: () => print('End'), - child: Text( - '张风捷特烈', - style: TextStyle(color: Colors.white), - ), - ), - ), - ], - ); - } - - Widget _buildSwitch() => Switch( - value: _style == end, - onChanged: (v) { - setState(() { - _style = v ? end : start; - }); - }); -} diff --git a/lib/views/widgets/StatefulWidget/AnimatedList/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedList/node1_base.dart deleted file mode 100644 index d148ddfab..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedList/node1_base.dart +++ /dev/null @@ -1,191 +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 { - @override - _CustomAnimatedListState createState() => _CustomAnimatedListState(); -} - -class _CustomAnimatedListState extends State { - final GlobalKey _listKey = GlobalKey(); - ListModel _list; - int _selectedItem; - int _nextItem; - - @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(), - Container( - width: MediaQuery.of(context).size.width/2, - height: 300, - child: AnimatedList( - padding: 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, - Iterable initialItems, - }) : assert(listKey != null), - 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(animation != null), - assert(item != null && item >= 0), - assert(selected != null), - 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: TextStyle(color: Colors.white, fontSize: 18), - ), - value: selected, - onChanged: (v) => onTap()), - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/AnimatedModalBarrier/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedModalBarrier/node1_base.dart deleted file mode 100644 index 95ad73f49..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedModalBarrier/node1_base.dart +++ /dev/null @@ -1,55 +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 { - @override - _AnimatedModalBarrierDemoState createState() => _AnimatedModalBarrierDemoState(); -} - -class _AnimatedModalBarrierDemoState extends State - with SingleTickerProviderStateMixin { - AnimationController _controller; - Animation _color; - - @override - void initState() { - super.initState(); - _controller = - AnimationController(vsync: this, duration: 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 Container( - width: 200, - height: 100, - child: Stack(alignment: Alignment.center, children: [ - AnimatedModalBarrier( - dismissible: true, - color: _color, - ), - Text('点击背景返回',style: TextStyle(color: Colors.white),) - ]), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/AnimatedOpacity/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedOpacity/node1_base.dart deleted file mode 100644 index 04510c957..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedOpacity/node1_base.dart +++ /dev/null @@ -1,51 +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 { - @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: Duration(seconds: 1), - curve: Curves.fastOutSlowIn, - opacity: _opacity, - onEnd: () => print('End'), - child: Icon(Icons.android, color: Colors.green, size: 60), - ), - ), - ], - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/AnimatedPadding/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedPadding/node1_base.dart deleted file mode 100644 index f2eb951e7..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedPadding/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 -/// 说明: -// { -// "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 { - @override - _CustomAnimatedPaddingState createState() => _CustomAnimatedPaddingState(); -} - -class _CustomAnimatedPaddingState extends State { - final EdgeInsets startPadding = EdgeInsets.all(10); - final EdgeInsets endPadding = EdgeInsets.all(30); - - 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: Duration(seconds: 1), - curve: Curves.fastOutSlowIn, - padding: _padding, - onEnd: () => print('End'), - child: Container( - alignment: Alignment.center, - color: Colors.blue, - child: Text( - '张风捷特烈', - style: TextStyle(color: Colors.white), - ), - ), - ), - ), - ], - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/AnimatedPhysicalModel/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedPhysicalModel/node1_base.dart deleted file mode 100644 index 1c17adfa0..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedPhysicalModel/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 -/// 说明: 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 { - @override - _AnimatedPhysicalModelDemoState createState() => - _AnimatedPhysicalModelDemoState(); -} - -class _AnimatedPhysicalModelDemoState extends State { - bool flag = false; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSwitch(), - Container( - width: 150, - height: 150, - child: AnimatedPhysicalModel( - duration: 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() { - return Switch( - value: flag, - onChanged: (v) { - setState(() { - flag = v; - }); - }); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatefulWidget/AnimatedPositioned/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedPositioned/node1_base.dart deleted file mode 100644 index b10146ccf..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedPositioned/node1_base.dart +++ /dev/null @@ -1,85 +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 { - @override - _CustomAnimatedPositionedState createState() => - _CustomAnimatedPositionedState(); -} - -class _CustomAnimatedPositionedState extends State { - final startTop = 0.0; - final endTop = 30.0; - - var _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: Duration(seconds: 1), - top: _top, - left: _top * 4, - child: Icon( - Icons.android, - color: Colors.green, - size: 50, - ), - ), - AnimatedPositioned( - duration: Duration(seconds: 1), - top: 50 - _top, - left: 150 - _top * 4, - child: Icon( - Icons.android, - color: Colors.red, - size: 50, - ), - ) - ]; - - Widget _buildSwitch() => Switch( - value: _top == endTop, - onChanged: (v) { - setState(() { - _top = v ? endTop : startTop; - }); - }); -} diff --git a/lib/views/widgets/StatefulWidget/AnimatedPositionedDirectional/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedPositionedDirectional/node1_base.dart deleted file mode 100644 index 2622b2847..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedPositionedDirectional/node1_base.dart +++ /dev/null @@ -1,86 +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 { - @override - _CustomAnimatedPositionedDirectionalState createState() => - _CustomAnimatedPositionedDirectionalState(); -} - -class _CustomAnimatedPositionedDirectionalState - extends State { - final startTop = 0.0; - final endTop = 30.0; - - var _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: Duration(seconds: 1), - top: _top, - start: _top * 4, - child: Icon( - Icons.android, - color: Colors.green, - size: 50, - ), - ), - AnimatedPositionedDirectional( - duration: Duration(seconds: 1), - top: 50 - _top, - start: 150 - _top * 4, - child: Icon( - Icons.android, - color: Colors.red, - size: 50, - ), - ) - ]; - - Widget _buildSwitch() => Switch( - value: _top == endTop, - onChanged: (v) { - setState(() { - _top = v ? endTop : startTop; - }); - }); -} diff --git a/lib/views/widgets/StatefulWidget/AnimatedSwitcher/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedSwitcher/node1_base.dart deleted file mode 100644 index f3d87c50b..000000000 --- a/lib/views/widgets/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 { - @override - _CustomAnimatedSwitcherState createState() => _CustomAnimatedSwitcherState(); -} - -class _CustomAnimatedSwitcherState extends State { - int _count = 0; - - @override - Widget build(BuildContext context) { - return Container( - child: 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.display3, - ), - ); - - Widget _buildMinusBtn() { - return MaterialButton( - padding: EdgeInsets.all(0), - textColor: Color(0xffFfffff), - elevation: 3, - color: Colors.red, - highlightColor: Color(0xffF88B0A), - splashColor: Colors.red, - child: Icon( - Icons.remove, - color: Colors.white, - ), - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onPressed: () => setState(() => _count -= 1)); - } - - Widget _buildAddBtn() => MaterialButton( - padding: EdgeInsets.all(0), - textColor: Color(0xffFfffff), - elevation: 3, - color: Colors.blue, - highlightColor: Color(0xffF88B0A), - splashColor: Colors.red, - child: Icon( - Icons.add, - color: Colors.white, - ), - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onPressed: () => setState(() => _count += 1)); -} diff --git a/lib/views/widgets/StatefulWidget/AnimatedTheme/node1_base.dart b/lib/views/widgets/StatefulWidget/AnimatedTheme/node1_base.dart deleted file mode 100644 index 385a943bd..000000000 --- a/lib/views/widgets/StatefulWidget/AnimatedTheme/node1_base.dart +++ /dev/null @@ -1,94 +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 { - @override - _AnimatedThemeDemoState createState() => _AnimatedThemeDemoState(); -} - -class _AnimatedThemeDemoState extends State { - ThemeData startThem = ThemeData( - primaryColor: Colors.blue, - textTheme: TextTheme( - headline1: TextStyle( - color: Colors.white, fontSize: 24, fontWeight: FontWeight.bold), - )); - - ThemeData endThem = ThemeData( - primaryColor: Colors.red, - textTheme: TextTheme( - headline1: TextStyle( - color: Colors.black, - fontSize: 16, - fontWeight: FontWeight.normal))); - - ThemeData them; - - @override - void initState() { - super.initState(); - them = startThem; - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSwitch(), - AnimatedTheme( - data: them, - duration: Duration(seconds: 2), - curve: Curves.fastOutSlowIn, - onEnd: () { - print('----onEnd---'); - }, - child: ChildContent(), - ), - ], - ); - } - - Widget _buildSwitch() { - print(them == endThem); - return Switch( - value: them == endThem, - onChanged: (v) { - setState(() { - them = v ? endThem : startThem; - }); - }); - } -} - -class ChildContent extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - width: 250, - height: 60, - alignment: Alignment.center, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(5)), - color: Theme.of(context).primaryColor, - ), - padding: EdgeInsets.all(10), - child: Text( - 'Flutter Unit', - style: Theme.of(context).textTheme.headline1, - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/AppBar/node1_base.dart b/lib/views/widgets/StatefulWidget/AppBar/node1_base.dart deleted file mode 100755 index 5f2ba3346..000000000 --- a/lib/views/widgets/StatefulWidget/AppBar/node1_base.dart +++ /dev/null @@ -1,49 +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 { - @override - Widget build(BuildContext context) { - return AppBar( - title: Text('风雅六社'), - leading: BackButton(), - backgroundColor: Colors.amber[500], - elevation: 2, - centerTitle: true, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20), - bottomRight: Radius.circular(20), - topRight: Radius.circular(5), - bottomLeft: Radius.circular(5), - )), - actions: [ - IconButton( - icon: Icon(Icons.star), - tooltip: 'like', - onPressed: () { - // do nothing - }), - CustomPopupMenuButton() - ], - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/AppBar/node2_tab.dart b/lib/views/widgets/StatefulWidget/AppBar/node2_tab.dart deleted file mode 100755 index ac8f30e7d..000000000 --- a/lib/views/widgets/StatefulWidget/AppBar/node2_tab.dart +++ /dev/null @@ -1,92 +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 { - @override - _TabAppBarState createState() => _TabAppBarState(); -} - -class _TabAppBarState extends State - with SingleTickerProviderStateMixin { - final tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼']; - 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: [ - Container( - height: 180, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage( - "assets/images/sabar.webp", - ), - fit: BoxFit.cover)), - child: _buildAppBar(), - ), - Container( - height: 150, color: Color(0xff916BF0), child: _buildTableBarView()) - ], - ); - } - - Widget _buildAppBar() => AppBar( - title: Text('风雅六社'), - elevation: 1, - leading: BackButton(), - backgroundColor: Colors.amber[500].withAlpha(33), - centerTitle: true, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(20), - topRight: Radius.circular(20), - )), - actions: [ - IconButton( - icon: Icon(Icons.star), - tooltip: 'like', - onPressed: () { - // do nothing - }), - 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) => Center( - child: Text( - e, - style: TextStyle(color: Colors.white, fontSize: 20), - ))) - .toList()); -} diff --git a/lib/views/widgets/StatefulWidget/AutomaticKeepAlive/node1_base.dart b/lib/views/widgets/StatefulWidget/AutomaticKeepAlive/node1_base.dart deleted file mode 100644 index fe187be3e..000000000 --- a/lib/views/widgets/StatefulWidget/AutomaticKeepAlive/node1_base.dart +++ /dev/null @@ -1,118 +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 { - - 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 Container( - 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; - - ColorBox({Key key, this.color, 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: [ - SizedBox(width: 60,), - Checkbox( - value: _checked, - onChanged: (v) { - setState(() { - _checked = v; - }); - }, - ), - Text( - "index ${widget.index}: ${colorString(widget.color)}", - style: 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/lib/views/widgets/StatefulWidget/BottomAppBar/node1_base.dart b/lib/views/widgets/StatefulWidget/BottomAppBar/node1_base.dart deleted file mode 100755 index 1cfcb9faf..000000000 --- a/lib/views/widgets/StatefulWidget/BottomAppBar/node1_base.dart +++ /dev/null @@ -1,116 +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 { - @override - _CustomBottomAppBarState createState() => _CustomBottomAppBarState(); -} - -class _CustomBottomAppBarState extends State { - var _position = 0; - var _location = FloatingActionButtonLocation.centerDocked; - final iconsMap = { - "图鉴": Icons.home, - "动态": Icons.toys, - "喜欢": Icons.favorite, - "手册": Icons.class_, - }; - var activeColor = Colors.blue.withAlpha(240); - - @override - Widget build(BuildContext context) { - return Container( - width: MediaQuery.of(context).size.width, - height: 180, - child: Scaffold( - backgroundColor: Colors.purple.withAlpha(22), - floatingActionButton: FloatingActionButton( - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - child: Icon(Icons.add), - ), - bottomNavigationBar: _buildBottomAppBar(), - floatingActionButtonLocation: _location, - body: _buildContent(), - ), - ); - } - - Widget _buildBottomAppBar() { - return BottomAppBar( - elevation: 1, - shape: CircularNotchedRectangle(), - notchMargin: 5, - color: Colors.red, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: info.asMap().keys.map((i) => _buildChild(i)).toList() - ..insertAll(isCenter ? 2 : 4, [SizedBox(width: 30)])), - ); - } - - Container _buildContent() { - return Container( - alignment: Alignment.center, - child: Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Text( - '当前页索引:$_position', - style: 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) { - var 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/lib/views/widgets/StatefulWidget/BottomNavigationBar/node1_base.dart b/lib/views/widgets/StatefulWidget/BottomNavigationBar/node1_base.dart deleted file mode 100755 index aa9822eed..000000000 --- a/lib/views/widgets/StatefulWidget/BottomNavigationBar/node1_base.dart +++ /dev/null @@ -1,100 +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 { - @override - _CustomBottomNavigationBarState createState() => - _CustomBottomNavigationBarState(); -} - -class _CustomBottomNavigationBarState extends State { - var _position = 0; - BottomNavigationBarType _type = BottomNavigationBarType.shifting; - final iconsMap = { - //底栏图标 - "图鉴": Icons.home, "动态": Icons.toys, - "喜欢": Icons.favorite, "手册": Icons.class_, - "我的": Icons.account_circle, - }; - final _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: TextStyle(fontWeight: FontWeight.bold), - showUnselectedLabels: false, - showSelectedLabels: true, - items: iconsMap.keys - .map((key) => BottomNavigationBarItem( - title: Text( - key, - ), - icon: Icon(iconsMap[key]), - backgroundColor: _colors[_position])) - .toList(), - ); - } - - Widget _buildOp() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - Text( - _type.toString(), - style: TextStyle(fontWeight: FontWeight.bold, color: Colors.blue), - ), - Switch( - value: _type == BottomNavigationBarType.shifting, - onChanged: (b) { - setState(() => _type = b - ? BottomNavigationBarType.shifting - : BottomNavigationBarType.fixed); - }), - ], - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/BottomNavigationBar/node2_page.dart b/lib/views/widgets/StatefulWidget/BottomNavigationBar/node2_page.dart deleted file mode 100755 index 72c3f3785..000000000 --- a/lib/views/widgets/StatefulWidget/BottomNavigationBar/node2_page.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 -/// 详情: -// { -// "widgetId": 60, -// "name": '可结合PageView进行切页', -// "priority": 2, -// "subtitle": -// "在onTap时进行使用控制器进行切页", -// } -class BottomNavigationBarWithPageView extends StatefulWidget { - @override - _BottomNavigationBarWithPageViewState createState() => - _BottomNavigationBarWithPageViewState(); -} - -class _BottomNavigationBarWithPageViewState - extends State { - var _position = 0; - final iconsMap = { - //底栏图标 - "图鉴": Icons.home, "动态": Icons.toys, - "喜欢": Icons.favorite, "手册": Icons.class_, - "我的": Icons.account_circle, - }; - final _colors = [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green, - Colors.purple, - ]; - 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 Container( - child: 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: 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: TextStyle(fontWeight: FontWeight.bold), - showUnselectedLabels: false, - showSelectedLabels: true, - items: iconsMap.keys - .map((key) => BottomNavigationBarItem( - title: Text( - key, - ), - icon: Icon(iconsMap[key]), - backgroundColor: _colors[_position])) - .toList(), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/Checkbox/node1_base.dart b/lib/views/widgets/StatefulWidget/Checkbox/node1_base.dart deleted file mode 100644 index 436123b33..000000000 --- a/lib/views/widgets/StatefulWidget/Checkbox/node1_base.dart +++ /dev/null @@ -1,43 +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 { - @override - _CustomCheckboxState createState() => _CustomCheckboxState(); -} - -class _CustomCheckboxState 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, - checkColor: Colors.white, - activeColor: e, - onChanged: (v) => - setState(() => _checked = v))) - .toList(), - ); - } -} - - diff --git a/lib/views/widgets/StatefulWidget/Checkbox/node2_tristate.dart b/lib/views/widgets/StatefulWidget/Checkbox/node2_tristate.dart deleted file mode 100644 index 81e6a21c2..000000000 --- a/lib/views/widgets/StatefulWidget/Checkbox/node2_tristate.dart +++ /dev/null @@ -1,44 +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 { - @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: (v) { - print(v); - setState(() => _checked = v); - })) - .toList(), - ); - } -} - diff --git a/lib/views/widgets/StatefulWidget/CircularProgressIndicator/node1_base.dart b/lib/views/widgets/StatefulWidget/CircularProgressIndicator/node1_base.dart deleted file mode 100644 index 03b4b49b5..000000000 --- a/lib/views/widgets/StatefulWidget/CircularProgressIndicator/node1_base.dart +++ /dev/null @@ -1,43 +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 { - @override - _CustomCircularProgressIndicatorState createState() => - _CustomCircularProgressIndicatorState(); -} - -class _CustomCircularProgressIndicatorState - extends State { - - var data = [0.2,0.4,0.6,0.8,null]; - - @override - Widget build(BuildContext context) { - - return Wrap( - spacing: 10, - children:data.map((e)=>Container( - width: 50, - height: 50, - child: CircularProgressIndicator( - value: e, - backgroundColor: Colors.grey.withAlpha(33), - valueColor: AlwaysStoppedAnimation(Colors.orange), - strokeWidth: 5, - ), - )).toList(), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/CupertinoActivityIndicator/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoActivityIndicator/node1_base.dart deleted file mode 100644 index 5a2d48fc2..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoActivityIndicator/node1_base.dart +++ /dev/null @@ -1,33 +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": 48, -// "name": 'CupertinoActivityIndicator基本使用', -// "priority": 1, -// "subtitle": -// "【animating】 : 是否loading动画 【bool】\n" -// "【radius】 : 半径 【double】", -// } -class CustomCupertinoActivityIndicator extends StatelessWidget { - - @override - Widget build(BuildContext context) { - - return Wrap( - spacing: 20, - children: [ - CupertinoActivityIndicator( - animating: true, - radius: 25, - ), - CupertinoActivityIndicator( - animating: false, - radius: 25, - ) - ], - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/CupertinoApp/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoApp/node1_base.dart deleted file mode 100644 index 7b6aa51b2..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoApp/node1_base.dart +++ /dev/null @@ -1,48 +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 { - @override - Widget build(BuildContext context) { - return Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height - 200, - child: 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/lib/views/widgets/StatefulWidget/CupertinoButton/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoButton/node1_base.dart deleted file mode 100755 index ade5190fa..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoButton/node1_base.dart +++ /dev/null @@ -1,40 +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 { - @override - Widget build(BuildContext context) { - var data = { - CupertinoColors.activeBlue:4.0, - Colors.blue:6.0, - CupertinoColors.activeOrange:8.0, - }; - 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: Text("iOS"), - )).toList() - ); - } -} - diff --git a/lib/views/widgets/StatefulWidget/CupertinoContextMenu/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoContextMenu/node1_base.dart deleted file mode 100644 index 0c9f7a47d..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoContextMenu/node1_base.dart +++ /dev/null @@ -1,48 +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": 143, -// "name": 'CupertinoContextMenu基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【actions】 : 行为组件集 【List】\n" -// "【previewBuilder】 : 动画构造器 【ContextMenuPreviewBuilder】", -// } -class CustomCupertinoContextMenu extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - child: _buildCupertinoContextMenu(context), - ); - } - - final info= ['保存图片','立刻呼叫','添加到收藏夹']; - - Widget _buildCupertinoContextMenu(context) => Container( - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage('assets/images/sabar_bar.webp'), - fit: BoxFit.cover), - borderRadius: BorderRadius.all(Radius.circular(50))), - width: 100, - height: 100, - child: CupertinoContextMenu( - child: Container( - decoration: 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/lib/views/widgets/StatefulWidget/CupertinoContextMenuAction/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoContextMenuAction/node1_base.dart deleted file mode 100644 index e6770c550..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoContextMenuAction/node1_base.dart +++ /dev/null @@ -1,43 +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'; -import 'package:flutter/material.dart'; - -class CustomCupertinoContextMenuAction extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Column( - children: [ - Container( - width: 200, - margin: EdgeInsets.all(5), - child: CupertinoContextMenuAction( - trailingIcon: CupertinoIcons.settings, - isDefaultAction: true, - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - child: Text('张风捷特烈')), - ), - Container( - width: 200, - margin: EdgeInsets.all(5), - child: CupertinoContextMenuAction( - trailingIcon: CupertinoIcons.home, - isDefaultAction: false, - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - child: Text('百里·巫缨')), - ), - ], - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/CupertinoDatePicker/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoDatePicker/node1_base.dart deleted file mode 100644 index 25a87df32..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoDatePicker/node1_base.dart +++ /dev/null @@ -1,79 +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 { - @override - _CustomCupertinoDatePickerState createState() => - _CustomCupertinoDatePickerState(); -} - -class _CustomCupertinoDatePickerState extends State { - DateTime _date = DateTime.now(); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Text( - '当前日期:${_date.toIso8601String()}', - style: 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: 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: TextStyle(color: Colors.blue, fontSize: 16,fontWeight: FontWeight.bold), - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/CupertinoNavigationBar/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoNavigationBar/node1_base.dart deleted file mode 100755 index e259daaa6..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoNavigationBar/node1_base.dart +++ /dev/null @@ -1,38 +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 { - @override - Widget build(BuildContext context) { - return CupertinoNavigationBar( - leading: Icon( - CupertinoIcons.back, - size: 25, - color: Colors.blue, - ), - middle: Text("风雪雅舍"), - trailing: Image.asset( - "assets/images/icon_head.webp", - width: 25.0, - height: 25.0, - ), - backgroundColor: Color(0xfff1f1f1), - padding: EdgeInsetsDirectional.only(start: 10,end: 20), - border: Border.all(color: Colors.transparent), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/CupertinoPageScaffold/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoPageScaffold/node1_base.dart deleted file mode 100644 index d75702949..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoPageScaffold/node1_base.dart +++ /dev/null @@ -1,34 +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 { - @override - Widget build(BuildContext context) { - return Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height - 300, - child: 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/lib/views/widgets/StatefulWidget/CupertinoPicker/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoPicker/node1_base.dart deleted file mode 100644 index ac7029706..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoPicker/node1_base.dart +++ /dev/null @@ -1,50 +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 { - final names = [ - 'Java', - 'Kotlin', - 'Dart', - 'Swift', - 'C++', - 'Python', - "JavaScript", - "PHP", - "Go", - "Object-c" - ]; - - @override - Widget build(BuildContext context) { - return Container( - 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/lib/views/widgets/StatefulWidget/CupertinoScrollbar/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoScrollbar/node1_base.dart deleted file mode 100644 index bc89eedab..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoScrollbar/node1_base.dart +++ /dev/null @@ -1,60 +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 { - final 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 Container( - height: 200, - child: CupertinoScrollbar( - child: ListView( - padding: EdgeInsets.symmetric(horizontal: 5), - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: color, - child: Text( - colorString(color), - style: 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/lib/views/widgets/StatefulWidget/CupertinoSegmentedControl/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoSegmentedControl/node1_base.dart deleted file mode 100644 index 59ef2c993..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoSegmentedControl/node1_base.dart +++ /dev/null @@ -1,52 +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": 'iOS页签基本使用', -// "priority": 1, -// "subtitle": -// "【children】 : 组件Map 【Map】\n" -// "【onValueChanged】 : 值改变回调 【ValueChanged】\n" -// "【groupValue】 : 选中值 【T】\n" -// "【padding】 : 内边距 【EdgeInsetsGeometry】", -// } -class CupertinoSegmentedControlDemo extends StatefulWidget { - @override - _CupertinoSegmentedControlDemoState createState() => - _CupertinoSegmentedControlDemoState(); -} - -class _CupertinoSegmentedControlDemoState - extends State { - var _value = 1; - - @override - Widget build(BuildContext context) { - return Container( - child: CupertinoSegmentedControl( - groupValue: _value, - onValueChanged: _onValueChanged, - padding: EdgeInsets.only(top: 20), - children: { - 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/lib/views/widgets/StatefulWidget/CupertinoSegmentedControl/node2_color.dart b/lib/views/widgets/StatefulWidget/CupertinoSegmentedControl/node2_color.dart deleted file mode 100644 index cf71d0a52..000000000 --- a/lib/views/widgets/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 { - @override - _CupertinoSegmentedControlColorState createState() => - _CupertinoSegmentedControlColorState(); -} - -class _CupertinoSegmentedControlColorState - extends State { - var _value = 1; - - @override - Widget build(BuildContext context) { - return Container( - child: CupertinoSegmentedControl( - unselectedColor: Colors.yellow, - selectedColor: Colors.green, - pressedColor: Colors.blue, - borderColor: Colors.red, - groupValue: _value, - onValueChanged: _onValueChanged, - padding: EdgeInsets.only(top: 20), - children: { - 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/lib/views/widgets/StatefulWidget/CupertinoSlider/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoSlider/node1_base.dart deleted file mode 100644 index 2d6f0f682..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoSlider/node1_base.dart +++ /dev/null @@ -1,57 +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 { - @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/lib/views/widgets/StatefulWidget/CupertinoSlidingSegmentedControl/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoSlidingSegmentedControl/node1_base.dart deleted file mode 100644 index 24a542b5e..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoSlidingSegmentedControl/node1_base.dart +++ /dev/null @@ -1,55 +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 { - @override - _CupertinoSlidingSegmentedControlDemoState createState() => - _CupertinoSlidingSegmentedControlDemoState(); -} - -class _CupertinoSlidingSegmentedControlDemoState - extends State { - var _value = 1; - - @override - Widget build(BuildContext context) { - return Container( - child: CupertinoSlidingSegmentedControl( - groupValue: _value, - onValueChanged: _onValueChanged, - thumbColor: Colors.amberAccent, - backgroundColor: Colors.green.withAlpha(99), - padding: EdgeInsets.all(5), - children: { - 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/lib/views/widgets/StatefulWidget/CupertinoSwitch/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoSwitch/node1_base.dart deleted file mode 100644 index ec5d4d921..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoSwitch/node1_base.dart +++ /dev/null @@ -1,39 +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 { - @override - _CustomCupertinoSwitchState createState() => _CustomCupertinoSwitchState(); -} - -class _CustomCupertinoSwitchState extends State { - final 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/lib/views/widgets/StatefulWidget/CupertinoTabBar/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoTabBar/node1_base.dart deleted file mode 100755 index 69fe86720..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoTabBar/node1_base.dart +++ /dev/null @@ -1,75 +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 { - @override - _CustomCupertinoTabBarState createState() => _CustomCupertinoTabBarState(); -} - -class _CustomCupertinoTabBarState extends State { - var _position = 0; - final 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], - ), - title: Text(e), - )) - .toList(), - activeColor: Colors.blue, - inactiveColor: Color(0xff333333), - backgroundColor: Color(0xfff1f1f1), - iconSize: 25.0, - ); - } - - Widget _buildContent(BuildContext context) { - return Container( - alignment: Alignment.center, - width: MediaQuery.of(context).size.width, - height: 150, - color: Color(0xffE7F3FC), - child: Text( - iconsMap.keys.toList()[_position], - style: TextStyle(color: Colors.blue, fontSize: 24), - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/CupertinoTabScaffold/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoTabScaffold/node1_base.dart deleted file mode 100644 index 1ff6ba20b..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoTabScaffold/node1_base.dart +++ /dev/null @@ -1,67 +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 { - @override - _CustomCupertinoTabScaffoldState createState() => - _CustomCupertinoTabScaffoldState(); -} - -class _CustomCupertinoTabScaffoldState - extends State { - var _position = 0; - final iconsMap = { - //底栏图标 - "图鉴": Icons.home, "动态": Icons.toys, - "喜欢": Icons.favorite, "手册": Icons.class_, - "我的": Icons.account_circle, - }; - - @override - Widget build(BuildContext context) { - return Container( - 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], - ), - title: Text(e), - )) - .toList(), - activeColor: Colors.blue, - inactiveColor: Color(0xff333333), - backgroundColor: Color(0xfff1f1f1), - iconSize: 25.0, - ); - - _buildContent(int index) => Container( - alignment: Alignment.center, - child: Text(iconsMap.keys.toList()[index]), - ); -} diff --git a/lib/views/widgets/StatefulWidget/CupertinoTabView/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoTabView/node1_base.dart deleted file mode 100644 index dce35fb0a..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoTabView/node1_base.dart +++ /dev/null @@ -1,107 +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 { - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.all(10), - child: ElevatedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => CupertinoTabViewPage()), - ); - }, - child: Text("进入 CupertinoTabView 测试页"), - ), - ); - } -} - - -class CupertinoTabViewPage extends StatelessWidget { - - @override - Widget build(BuildContext context) { - return Container( - height: 300, - child: CupertinoTabView( - routes: { - '/': (context) => HomePage(), - '/test_detail': (context) => DetailPage(), - }, - ), - ); - } -} - -class DetailPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return CupertinoPageScaffold( - navigationBar: 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: CupertinoNavigationBar( - middle: Text('我是主页'), - ), - child: Center(child: Column( - - children: [ - Spacer(), - Material(child: Padding( - padding: const EdgeInsets.only(left:18.0,right: 18,bottom: 20), - child: Text(info), - )), - CupertinoButton( - padding: EdgeInsets.only(left: 10,right: 10), - color: Colors.blue, - onPressed: () { - Navigator.pushNamed( - context, "/test_detail" - ); - }, - child: Text("进入详情页"), - ), - Spacer(), - ], - )), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/CupertinoTextField/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoTextField/node1_base.dart deleted file mode 100644 index c84de5fcb..000000000 --- a/lib/views/widgets/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 { - @override - _CupertinoTextFieldDemoState createState() => _CupertinoTextFieldDemoState(); -} - -class _CupertinoTextFieldDemoState extends State { - var _value = ''; - var _color =Colors.black; - - @override - Widget build(BuildContext context) { - return Container( - child: Column( - children: [ - Text('输入了:$_value',style: TextStyle(color: _color),), - CupertinoTextField( - placeholder: 'Input Name', - showCursor: true, - minLines: 1, - maxLines: 4, - padding: 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/lib/views/widgets/StatefulWidget/CupertinoTextField/node2_style.dart b/lib/views/widgets/StatefulWidget/CupertinoTextField/node2_style.dart deleted file mode 100644 index 5e8848569..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoTextField/node2_style.dart +++ /dev/null @@ -1,41 +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 { - - @override - Widget build(BuildContext context) { - return Container( - child: - 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/lib/views/widgets/StatefulWidget/CupertinoTimerPicker/node1_base.dart b/lib/views/widgets/StatefulWidget/CupertinoTimerPicker/node1_base.dart deleted file mode 100644 index 0b6934b48..000000000 --- a/lib/views/widgets/StatefulWidget/CupertinoTimerPicker/node1_base.dart +++ /dev/null @@ -1,72 +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 { - @override - _CustomCupertinoTimerPickerState createState() => - _CustomCupertinoTimerPickerState(); -} - -class _CustomCupertinoTimerPickerState - extends State { - Duration _date = Duration(seconds: 30); - - @override - Widget build(BuildContext context) { - return Column( - children: [ - Text( - '当前时间:${_date.toString()}', - style: 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: TextStyle( - color: Colors.blue, fontSize: 16, fontWeight: FontWeight.bold), - ), - ); - } - - Widget buildPicker(CupertinoTimerPickerMode mode) { - return Container( - margin: EdgeInsets.all(10), - height: 150, - child: CupertinoTimerPicker( - mode: mode, - initialTimerDuration: Duration(seconds: 30), - onTimerDurationChanged: (date) { - print(date); - setState(() => _date = date); - }, - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/DecoratedBoxTransition/node1_base.dart b/lib/views/widgets/StatefulWidget/DecoratedBoxTransition/node1_base.dart deleted file mode 100644 index 2d987053b..000000000 --- a/lib/views/widgets/StatefulWidget/DecoratedBoxTransition/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": 113, -// "name": 'DecoratedBoxTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【position】 : 前/背景色 【DecorationPosition】\n" -// "【decoration】 : 动画 【Animation】", -// } -class CustomDecoratedBoxTransition extends StatefulWidget { - @override - _CustomDecoratedBoxTransitionState createState() => - _CustomDecoratedBoxTransitionState(); -} - -class _CustomDecoratedBoxTransitionState - extends State - with SingleTickerProviderStateMixin { - AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController(vsync: this, duration: 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: 200, - height: 100, - child: DecoratedBoxTransition( - position: DecorationPosition.background, - decoration: DecorationTween( - begin: BoxDecoration( - color: Colors.greenAccent, - borderRadius: BorderRadius.all(Radius.circular(50)), - boxShadow: [ - BoxShadow( - offset: Offset(1, 1), - color: Colors.purple, - blurRadius: 3, - spreadRadius: 1) - ]), - end: 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: Container( - child: Icon(Icons.android, color: Colors.white, size: 60)), - ), - )); - } -} diff --git a/lib/views/widgets/StatefulWidget/DefaultTabController/node1_base.dart b/lib/views/widgets/StatefulWidget/DefaultTabController/node1_base.dart deleted file mode 100644 index 34791a2a1..000000000 --- a/lib/views/widgets/StatefulWidget/DefaultTabController/node1_base.dart +++ /dev/null @@ -1,51 +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 -/// 说明: 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 = [ - Tab(text: '青眼白龙'), - Tab(text: '黑魔术师'), - Tab(text: '混沌战士'), - ]; - - @override - Widget build(BuildContext context) { - return Container( - height: 300, - child: DefaultTabController( - length: tabs.length, - child: Scaffold( - appBar: AppBar( - title: 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/lib/views/widgets/StatefulWidget/DefaultTextStyleTransition/node1_base.dart b/lib/views/widgets/StatefulWidget/DefaultTextStyleTransition/node1_base.dart deleted file mode 100644 index 334508e0a..000000000 --- a/lib/views/widgets/StatefulWidget/DefaultTextStyleTransition/node1_base.dart +++ /dev/null @@ -1,73 +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": 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 { - @override - _CustomDefaultTextStyleTransitionState createState() => - _CustomDefaultTextStyleTransitionState(); -} - -class _CustomDefaultTextStyleTransitionState - extends State - with SingleTickerProviderStateMixin { - AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController(vsync: this, duration: 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: TextStyle(color: Colors.blue, fontSize: 50, shadows: [ - Shadow( - offset: Offset(1, 1), color: Colors.black, blurRadius: 3) - ]), - end: TextStyle(color: Colors.white, fontSize: 20, shadows: [ - Shadow( - offset: Offset(1, 1), color: Colors.purple, blurRadius: 3) - ])).animate(_ctrl), - child: Text('张风捷特烈'), - ), - )); - } -} diff --git a/lib/views/widgets/StatefulWidget/Dismissible/node1_base.dart b/lib/views/widgets/StatefulWidget/Dismissible/node1_base.dart deleted file mode 100644 index dda065eed..000000000 --- a/lib/views/widgets/StatefulWidget/Dismissible/node1_base.dart +++ /dev/null @@ -1,95 +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 { - @override - _CustomDismissibleState createState() => _CustomDismissibleState(); -} - -class _CustomDismissibleState extends State { - var 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 Container( - height: 200, - child: ListView( - padding: EdgeInsets.symmetric(horizontal: 5), - children: data.map((color) => _buildItem(color)).toList(), - ), - ); - } - - Widget _buildItem(Color color) { - return Dismissible( - background: Container( - color: Colors.green, - alignment: Alignment(-0.9, 0), - child: Icon( - Icons.check, - color: Colors.white, - ), - ), - secondaryBackground: Container( - alignment: Alignment(0.9, 0), - child: 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: 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/lib/views/widgets/StatefulWidget/Dismissible/node2_direction.dart b/lib/views/widgets/StatefulWidget/Dismissible/node2_direction.dart deleted file mode 100644 index 87f2d9317..000000000 --- a/lib/views/widgets/StatefulWidget/Dismissible/node2_direction.dart +++ /dev/null @@ -1,96 +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 { - @override - _CustomDirectionDismissibleState createState() => _CustomDirectionDismissibleState(); -} - -class _CustomDirectionDismissibleState extends State { - var 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 Container( - height: 200, - child: ListView( - scrollDirection: Axis.horizontal, - padding: 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: Alignment( 0,-0.9,), - child: Icon( - Icons.check, - color: Colors.white, - ), - ), - crossAxisEndOffset: 0.5, - secondaryBackground: Container( - alignment: Alignment( 0,0.9,), - child: 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: 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/lib/views/widgets/StatefulWidget/DragTarget/node1_base.dart b/lib/views/widgets/StatefulWidget/DragTarget/node1_base.dart deleted file mode 100644 index 715f36c3d..000000000 --- a/lib/views/widgets/StatefulWidget/DragTarget/node1_base.dart +++ /dev/null @@ -1,102 +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 { - @override - _CustomDragTargetState createState() => _CustomDragTargetState(); -} - -class _CustomDragTargetState extends State { - Color _color = Colors.grey; - String _info = 'DragTarget'; - - @override - Widget build(BuildContext context) { - return Container( - child: Column( - children: [ - Wrap( - children: _buildColors(), - spacing: 10, - ), - SizedBox(height: 20,), - _buildDragTarget() - ], - ), - ); - } - - List _buildColors() { - var colors = [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green, - Colors.orange, - Colors.purple, - Colors.cyanAccent - ]; - return colors - .map( - (e) => Draggable( - child: Container( - width: 30, - height: 30, - alignment: Alignment.center, - child: Text( - colors.indexOf(e).toString(), - style: 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: TextStyle(color: Colors.white), - ), - ))); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatefulWidget/Draggable/node1_base.dart b/lib/views/widgets/StatefulWidget/Draggable/node1_base.dart deleted file mode 100644 index 0742f797b..000000000 --- a/lib/views/widgets/StatefulWidget/Draggable/node1_base.dart +++ /dev/null @@ -1,41 +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 { - @override - Widget build(BuildContext context) { - var 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: BoxDecoration( - color: Colors.blue, shape: BoxShape.circle), - ), - feedback: Container( - width: 30, - height: 30, - decoration: BoxDecoration( - color: Colors.red, shape: BoxShape.circle), - ), - )) - .toList()); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatefulWidget/Draggable/node2_data.dart b/lib/views/widgets/StatefulWidget/Draggable/node2_data.dart deleted file mode 100644 index c9400cde7..000000000 --- a/lib/views/widgets/StatefulWidget/Draggable/node2_data.dart +++ /dev/null @@ -1,108 +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 { - @override - _DraggablePageState createState() => _DraggablePageState(); -} - -class _DraggablePageState extends State { - Color _color = Colors.grey; - String _info = 'DragTarget'; - - @override - Widget build(BuildContext context) { - return Container( - child: Column( - children: [ - Wrap( - children: _buildColors(), - spacing: 10, - ), - SizedBox( - height: 20, - ), - _buildDragTarget() - ], - ), - ); - } - - List _buildColors() { - var colors = [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green, - Colors.orange, - Colors.purple, - Colors.cyanAccent - ]; - return 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: 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: TextStyle(color: Colors.white), - ), - ))); - } -} diff --git a/lib/views/widgets/StatefulWidget/Draggable/node3_use.dart b/lib/views/widgets/StatefulWidget/Draggable/node3_use.dart deleted file mode 100644 index 2608cf7a2..000000000 --- a/lib/views/widgets/StatefulWidget/Draggable/node3_use.dart +++ /dev/null @@ -1,90 +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 { - @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 Container( - child: Column( - children: [ - Wrap( - children: _buildColors(), - spacing: 10, - ), - 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: - BoxDecoration(color: Colors.red, shape: BoxShape.circle), - child: 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: - 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/lib/views/widgets/StatefulWidget/DraggableScrollableSheet/node1_base.dart b/lib/views/widgets/StatefulWidget/DraggableScrollableSheet/node1_base.dart deleted file mode 100644 index 9d0d2901b..000000000 --- a/lib/views/widgets/StatefulWidget/DraggableScrollableSheet/node1_base.dart +++ /dev/null @@ -1,100 +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 -/// 说明: 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 { - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.all(10), - child: ElevatedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DraggableScrollableSheetPage()), - ); - }, - child: Text("进入 DraggableScrollableSheet 测试页"), - ), - ); - } -} - -class DraggableScrollableSheetPage extends StatelessWidget { - final 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: 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: 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/lib/views/widgets/StatefulWidget/DrawerController/node1_base.dart b/lib/views/widgets/StatefulWidget/DrawerController/node1_base.dart deleted file mode 100644 index 06653af89..000000000 --- a/lib/views/widgets/StatefulWidget/DrawerController/node1_base.dart +++ /dev/null @@ -1,75 +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 -/// 说明: 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 { - @override - _DrawerControllerDemoState createState() => _DrawerControllerDemoState(); -} - -class _DrawerControllerDemoState extends State { - final GlobalKey _drawerKey = - GlobalKey(); - - bool _open = false; - - @override - Widget build(BuildContext context) { - return Container( - child: Column( - children: [ - ElevatedButton( - onPressed: toggleDrawer, - child: Text("显隐 Drawer"), - ), - Container( - 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: 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/lib/views/widgets/StatefulWidget/DropdownButton/node1_base.dart b/lib/views/widgets/StatefulWidget/DropdownButton/node1_base.dart deleted file mode 100644 index 6accccce9..000000000 --- a/lib/views/widgets/StatefulWidget/DropdownButton/node1_base.dart +++ /dev/null @@ -1,61 +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 { - @override - _CustomDropDownButtonState createState() => _CustomDropDownButtonState(); -} - -class _CustomDropDownButtonState extends State { - Color _color = Colors.red; - final _colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green]; - final _info = ["红色", "黄色", "蓝色", "绿色"]; - - @override - Widget build(BuildContext context) { - return Wrap( - children: [ - Container( - margin: 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)), - ], - ); - } - - 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/lib/views/widgets/StatefulWidget/DropdownButton/node2_style.dart b/lib/views/widgets/StatefulWidget/DropdownButton/node2_style.dart deleted file mode 100644 index 16894165e..000000000 --- a/lib/views/widgets/StatefulWidget/DropdownButton/node2_style.dart +++ /dev/null @@ -1,58 +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 { - @override - _StyleDropDownButtonState createState() => _StyleDropDownButtonState(); -} - -class _StyleDropDownButtonState extends State { - Color _color = Colors.red ; - final _colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green]; - final _info = ["红色", "黄色", "蓝色", "绿色"]; - - @override - Widget build(BuildContext context) { - return Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Container( - margin: EdgeInsets.symmetric(horizontal: 20), - width: 50, - height: 50, - color: _color??Colors.transparent, - ), - DropdownButton( - hint: Text('请选择'), - isDense: true, - iconSize:20, - iconEnabledColor:_color??Colors.orange, - value: _color, - items: _buildItems(), - onChanged: (v) => setState(() => _color = v)), - ], - ); - } - - List> _buildItems() => _colors - .map((e) => DropdownMenuItem( - value: e, - child: Text( - _info[_colors.indexOf(e)], - style: TextStyle(color: e), - ))) - .toList(); -} diff --git a/lib/views/widgets/StatefulWidget/DropdownButtonFormField/node1_base.dart b/lib/views/widgets/StatefulWidget/DropdownButtonFormField/node1_base.dart deleted file mode 100644 index 18b6495c7..000000000 --- a/lib/views/widgets/StatefulWidget/DropdownButtonFormField/node1_base.dart +++ /dev/null @@ -1,69 +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 { - @override - _DropdownButtonFormFieldDemoState createState() => _DropdownButtonFormFieldDemoState(); -} - -class _DropdownButtonFormFieldDemoState extends State { - Color _color; - final _colors = [Colors.red, Colors.yellow, Colors.blue, Colors.green]; - final _info = ["红色", "黄色", "蓝色", "绿色"]; - - @override - Widget build(BuildContext context) { - return Wrap( - children: [ - Container( - margin: EdgeInsets.symmetric(horizontal: 20), - width: 50, - height: 50, - color: _color??_colors[0], - ), - - SizedBox( - width: 80, - child: DropdownButtonFormField( - value: _color, - elevation: 1, - hint: Text('选择颜色',style: TextStyle(fontSize: 12),), - icon: Icon( - Icons.expand_more, - size: 20, - color: _color, - ), - items: _buildItems(), - onChanged: (v) => setState(() => _color = v) - ), - ) - - ], - ); - } - - 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/lib/views/widgets/StatefulWidget/EditableText/node1_base.dart b/lib/views/widgets/StatefulWidget/EditableText/node1_base.dart deleted file mode 100644 index d7b3b5126..000000000 --- a/lib/views/widgets/StatefulWidget/EditableText/node1_base.dart +++ /dev/null @@ -1,41 +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 -/// 说明: 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 { - @override - _EditableTextDemoState createState() => _EditableTextDemoState(); -} - -class _EditableTextDemoState extends State { - final _ctrl = TextEditingController(text:'Hello Flutter Unit!'); - final _node = FocusNode(); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: EditableText( - controller: _ctrl, - focusNode: _node, - style: TextStyle(fontSize: 16,color: Colors.blue), - cursorColor: Colors.blue, - backgroundCursorColor: Colors.orange, - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/ElevatedButton/node1_base.dart b/lib/views/widgets/StatefulWidget/ElevatedButton/node1_base.dart deleted file mode 100644 index 6a132900d..000000000 --- a/lib/views/widgets/StatefulWidget/ElevatedButton/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 -/// 说明: 354 ElevatedButton Material风格的升起按钮,表现和RaisedButton类似。可通过样式更改边框、颜色、阴影等属性。 -// { -// "widgetId": 354, -// "name": 'ElevatedButton基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 是否具有滚动主体 【Widget】\n" -// "【onPressed】 : 点击事件 【VoidCallback】\n" -// "【onLongPress】 : 长按事件 【VoidCallback】", -// } - -class ElevatedButtonDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - height: 60, - child: Wrap( - spacing: 20, - children: [ - ElevatedButton( - child: Text('ElevatedButton'), - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - ElevatedButton( - child: Text('禁用按钮'), - onPressed: null, - onLongPress: null, - ), - ], - )); - } - - _onPressed() {} - - _onLongPress() {} -} diff --git a/lib/views/widgets/StatefulWidget/ElevatedButton/node2_style.dart b/lib/views/widgets/StatefulWidget/ElevatedButton/node2_style.dart deleted file mode 100644 index d4d610008..000000000 --- a/lib/views/widgets/StatefulWidget/ElevatedButton/node2_style.dart +++ /dev/null @@ -1,58 +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 { - @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: Text('ElevatedButton样式'), - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - ElevatedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.white, - primary: Colors.black, - side: BorderSide(color: Colors.blue,width: 1), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)) - ), - // elevation: 2, - shadowColor: Colors.orangeAccent), - child: Text('ElevatedButton边线'), - autofocus: false, - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - ], - ), - ); - } - - _onPressed() {} - - _onLongPress() {} -} diff --git a/lib/views/widgets/StatefulWidget/ExpandIcon/node1_base.dart b/lib/views/widgets/StatefulWidget/ExpandIcon/node1_base.dart deleted file mode 100644 index a3fcb5b95..000000000 --- a/lib/views/widgets/StatefulWidget/ExpandIcon/node1_base.dart +++ /dev/null @@ -1,36 +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 { - @override - _CustomExpandIconState createState() => _CustomExpandIconState(); -} - -class _CustomExpandIconState extends State { - var _closed = true; - - @override - Widget build(BuildContext context) { - return ExpandIcon( - isExpanded: _closed, - padding: EdgeInsets.all(5), - size: 30, - color: Colors.blue, - expandedColor: Colors.orangeAccent, - onPressed: (value) => setState(() => _closed = !_closed), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/ExpansionPanelList/node1_base.dart b/lib/views/widgets/StatefulWidget/ExpansionPanelList/node1_base.dart deleted file mode 100644 index 4ca219401..000000000 --- a/lib/views/widgets/StatefulWidget/ExpansionPanelList/node1_base.dart +++ /dev/null @@ -1,92 +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 { - @override - _CustomExpansionPanelListState createState() => - _CustomExpansionPanelListState(); -} - -class _CustomExpansionPanelListState extends State { - var 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 Container( - width: 300, - child: ExpansionPanelList( - children: data.map((color) => _buildItem(color)).toList(), - animationDuration: 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: TextStyle(color: Colors.black), - ), - ), - ], - ), - ), - body: Container( - alignment: Alignment.center, - height: 50, - color: color, - child: Text( - colorString(color), - style: 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/lib/views/widgets/StatefulWidget/ExpansionTile/node1_base.dart b/lib/views/widgets/StatefulWidget/ExpansionTile/node1_base.dart deleted file mode 100644 index ea376b45b..000000000 --- a/lib/views/widgets/StatefulWidget/ExpansionTile/node1_base.dart +++ /dev/null @@ -1,38 +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 { - @override - _CustomExpansionTileState createState() => _CustomExpansionTileState(); -} - -class _CustomExpansionTileState extends State { - @override - Widget build(BuildContext context) { - return ExpansionTile( - leading: Icon(Icons.star), - title: Text("选择语言"), - backgroundColor: Colors.grey.withAlpha(6), - onExpansionChanged: (value) { - print('$value'); - }, - initiallyExpanded: false, - children: [CustomRadioListTile()], - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/Form/node1_base.dart b/lib/views/widgets/StatefulWidget/Form/node1_base.dart deleted file mode 100644 index 4a1cfbc2f..000000000 --- a/lib/views/widgets/StatefulWidget/Form/node1_base.dart +++ /dev/null @@ -1,111 +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 { - @override - _CustomFormState createState() => _CustomFormState(); -} - -class _CustomFormState extends State { - GlobalKey _formKey = GlobalKey(); - - @override - Widget build(BuildContext context) { - return Container( - child: Form( - onWillPop: () => _willPop(context), - key: _formKey, - onChanged: () { - print('Form---onChanged'); - }, - child: - Stack( - alignment: Alignment.centerRight, - children: [ - Container( - width: 350, - child: UnconstrainedBox( - child: Container( - width: 200, - height: 70, - child: TextFormField( - style: TextStyle(textBaseline: TextBaseline.alphabetic), - decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'username', - ), - validator: _validateUsername, - ), - ), - ), - ), - Positioned( - top: 0, right: 0, child: _buildSubmitButton(context)), - ], - ), - ), - ); - } - - String _validateUsername(value) { - if (value.isEmpty) { - return '用户名不能为空'; - } - return null; - } - - RaisedButton _buildSubmitButton(BuildContext context) { - return RaisedButton( - color: Colors.blue, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onPressed: _onSubmit, - child: Icon( - Icons.check, - color: Colors.white, - ), - ); - } - - _onSubmit(){ - 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: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - title: Text('提示'), - content: Text('你确定要离开此页吗?'), - actions: [ - FlatButton( - onPressed: () => Navigator.of(context).pop(true), - child: Text('确定'), - ), - FlatButton( - onPressed: () => Navigator.of(context).pop(false), - child: Text('取消'), - ), - ], - ), - ) ?? - false; - } -} diff --git a/lib/views/widgets/StatefulWidget/FormField/node1_base.dart b/lib/views/widgets/StatefulWidget/FormField/node1_base.dart deleted file mode 100644 index dd7fab8a3..000000000 --- a/lib/views/widgets/StatefulWidget/FormField/node1_base.dart +++ /dev/null @@ -1,36 +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 { - 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: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/FutureBuilder/node1_base.dart b/lib/views/widgets/StatefulWidget/FutureBuilder/node1_base.dart deleted file mode 100644 index fc8c5f6a9..000000000 --- a/lib/views/widgets/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 { - @override - _CustomFutureBuilderState createState() => _CustomFutureBuilderState(); -} - -class _CustomFutureBuilderState extends State { - Future _future; - - @override - void initState() { - _future = loadData(); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Container( - child: FutureBuilder( - initialData: 'Load', - future: _future, - builder: (ctx, snap) { - if (snap.connectionState == ConnectionState.done) { - return Text(snap.data); - } - if (snap.connectionState == ConnectionState.waiting) { - return CircularProgressIndicator(); - } - if (snap.hasError) { - return Text('Error'); - } - return Container(); - }), - ); - } - - Future loadData() async { - await Future.delayed(Duration(seconds: 2)); - return 'LoadeSuccess'; - } -} diff --git a/lib/views/widgets/StatefulWidget/GlowingOverscrollIndicator/node1_base.dart b/lib/views/widgets/StatefulWidget/GlowingOverscrollIndicator/node1_base.dart deleted file mode 100644 index 5fab49902..000000000 --- a/lib/views/widgets/StatefulWidget/GlowingOverscrollIndicator/node1_base.dart +++ /dev/null @@ -1,63 +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 -/// 说明: 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 { - 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 Container( - height: 300, - child: GlowingOverscrollIndicator( - color: Colors.purple, - // showLeading: false, - // showTrailing: false, - axisDirection: AxisDirection.down, - child: ListView.builder( - itemBuilder: (_, index) => Container( - margin: EdgeInsets.all(10), - height: 60, - color: data[index], - ), - itemCount: data.length, - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/Hero/node1_base.dart b/lib/views/widgets/StatefulWidget/Hero/node1_base.dart deleted file mode 100644 index 8c1aebaa8..000000000 --- a/lib/views/widgets/StatefulWidget/Hero/node1_base.dart +++ /dev/null @@ -1,122 +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": 171, -// "name": 'Hero基本使用', -// "priority": 1, -// "subtitle": -// "【tag】 : 标签 【String】\n", -// } -class CustomHero extends StatelessWidget { - @override - Widget build(BuildContext context) { - var hero = Hero( - //----定义一个Hero,并添加tag标签,此中组件共享 - tag: 'user-head', - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(30)), - child: Image.asset( - "assets/images/icon_head.webp", - width: 60, - height: 60, - fit: BoxFit.cover, - ), - ), - ); - - var container = Container( - alignment: 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: TargetPage(), duration: 1000), - ), - ); - } - -} - -class TargetPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - var hero = Hero( - //----定义一个Hero,为其添加标签,两个标签相同,则可以共享 - tag: 'user-head', - child: Padding( - padding: EdgeInsets.all(6.0), - child: CircleAvatar( - backgroundColor: Colors.transparent, - backgroundImage: AssetImage( - "assets/images/icon_head.webp", - ), - ), - ), - ); - - var 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( - {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: Offset(0.0, 1.0), - end: Offset(0.0, 0.0), - ).animate(CurvedAnimation(parent: a1, curve: curve)), - child: child)); -} diff --git a/lib/views/widgets/StatefulWidget/Image/node1_base.dart b/lib/views/widgets/StatefulWidget/Image/node1_base.dart deleted file mode 100644 index 54fa4c0c8..000000000 --- a/lib/views/widgets/StatefulWidget/Image/node1_base.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 -/// 说明: -// { -// "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 { - final assetsImagePath = "assets/images/icon_head.webp"; - final assetsGif = "assets/images/pica.gif"; - final netImageUrl = "https://user-gold-cdn.xitu.io" - "/2019/7/24/16c225e78234ec26?" - "imageView2/1/w/1304/h/734/q/85/format/webp/interlace/1"; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: [ - _loadFromAssets(), - _loadFromNet(), - ], - ); - } - - Widget _loadFromAssets() => Wrap( - spacing: 10, - children: [ - Image.asset(assetsImagePath, height: 80, width: 80), - Image.asset(assetsGif, height: 80, width: 80), - ], - ); - - Widget _loadFromNet() => Image.network(netImageUrl, height: 80); -} diff --git a/lib/views/widgets/StatefulWidget/Image/node2_fit.dart b/lib/views/widgets/StatefulWidget/Image/node2_fit.dart deleted file mode 100644 index 17573d8a6..000000000 --- a/lib/views/widgets/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 { - @override - _FitImageState createState() => _FitImageState(); -} - -class _FitImageState extends State { - bool _smallImage = false; - - @override - Widget build(BuildContext context) { - var imageLi = BoxFit.values - .toList() - .map((mode) => Column(children: [ - Container( - margin: 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: [ - Text("使用小图"), - Switch( - value: _smallImage, - onChanged: (b) => setState(() => _smallImage = b)), - ], - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/Image/node3_alignment.dart b/lib/views/widgets/StatefulWidget/Image/node3_alignment.dart deleted file mode 100644 index fc2750a95..000000000 --- a/lib/views/widgets/StatefulWidget/Image/node3_alignment.dart +++ /dev/null @@ -1,47 +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 { - @override - Widget build(BuildContext context) { - var alignment = [ - Alignment.center, - Alignment.centerLeft, - Alignment.centerRight, - Alignment.topCenter, - Alignment.topLeft, - Alignment.topRight, - Alignment.bottomCenter, - Alignment.bottomLeft, - Alignment.bottomRight - ]; //测试数组 - var imgLi = alignment - .map((alignment) => //生成子Widget列表 - Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 90, - height: 60, - color: Colors.grey.withAlpha(88), - child: Image( - image: AssetImage("assets/images/wy_30x20.webp"), - alignment: alignment, - )), - Text(alignment.toString()) - ])) - .toList(); - var imageAlignment = Wrap(children: imgLi); - return imageAlignment; - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatefulWidget/Image/node4_colorBlendMode.dart b/lib/views/widgets/StatefulWidget/Image/node4_colorBlendMode.dart deleted file mode 100644 index 647ea8c34..000000000 --- a/lib/views/widgets/StatefulWidget/Image/node4_colorBlendMode.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": 38, -// "name": '图片颜色及混合模式', -// "priority": 4, -// "subtitle": -// "【color】 : 颜色 【Color】\n" -// "【colorBlendMode】: 混合模式*29 【BlendMode】", -// }, - -class BlendModeImage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Wrap( - children: BlendMode.values - .toList() - .map((mode) => Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 60, - height: 60, - color: Colors.red, - child: Image( - image: 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/lib/views/widgets/StatefulWidget/Image/node5_repeat.dart b/lib/views/widgets/StatefulWidget/Image/node5_repeat.dart deleted file mode 100644 index 27c45f875..000000000 --- a/lib/views/widgets/StatefulWidget/Image/node5_repeat.dart +++ /dev/null @@ -1,34 +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 { - @override - Widget build(BuildContext context) { - return Wrap( - children: ImageRepeat.values - .toList() - .map((mode) => Column(children: [ - Container( - margin: EdgeInsets.all(5), - width: 150, - height: 60, - color: Colors.red, - child: Image( - image: AssetImage("assets/images/wy_30x20.webp"), - repeat: mode)), - Text(mode.toString().split(".")[1]) - ])) - .toList(), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatefulWidget/Image/node6_centerSlice.dart b/lib/views/widgets/StatefulWidget/Image/node6_centerSlice.dart deleted file mode 100644 index 4c7359d13..000000000 --- a/lib/views/widgets/StatefulWidget/Image/node6_centerSlice.dart +++ /dev/null @@ -1,30 +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 { - @override - Widget build(BuildContext context) { - return Container( - width: 300, - height: 80, - child: Image.asset( - "assets/images/right_chat.png", - centerSlice: Rect.fromLTRB(9, 27, 60, 27 + 1.0), - fit: BoxFit.fill, - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/Ink/node1_base.dart b/lib/views/widgets/StatefulWidget/Ink/node1_base.dart deleted file mode 100644 index 20adc809f..000000000 --- a/lib/views/widgets/StatefulWidget/Ink/node1_base.dart +++ /dev/null @@ -1,47 +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 { - @override - Widget build(BuildContext context) { - return Material( - color: Colors.orangeAccent, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Center( - child: Ink( - padding: EdgeInsets.all(10), - decoration: BoxDecoration( - color: Colors.yellow, - borderRadius: BorderRadius.all(Radius.circular(20)) - ), - - width: 200.0, - height: 100.0, - child: InkWell( - onTap: () { - }, - child: Center( - child: Text('Hello'), - )), - ), - ), - ), - ); - } -} - diff --git a/lib/views/widgets/StatefulWidget/Ink/node2_image.dart b/lib/views/widgets/StatefulWidget/Ink/node2_image.dart deleted file mode 100644 index 23226b834..000000000 --- a/lib/views/widgets/StatefulWidget/Ink/node2_image.dart +++ /dev/null @@ -1,40 +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 { - @override - Widget build(BuildContext context) { - return Material( - color: Colors.grey[800], - child: Center( - child: Ink.image( - image: AssetImage('assets/images/sabar.webp'), - fit: BoxFit.cover, - width: 300.0, - height: 200.0, - child: InkWell( - onTap: () {}, - child: Align( - alignment: Alignment.topLeft, - child: Padding( - padding: const EdgeInsets.all(10.0), - child: Text('Chaos', - style: TextStyle( - fontWeight: FontWeight.w900, color: Colors.black)), - ), - )), - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/InkResponse/node1_base.dart b/lib/views/widgets/StatefulWidget/InkResponse/node1_base.dart deleted file mode 100644 index 3294192c9..000000000 --- a/lib/views/widgets/StatefulWidget/InkResponse/node1_base.dart +++ /dev/null @@ -1,42 +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 { - @override - _CustomInkResponseState createState() => _CustomInkResponseState(); -} - -class _CustomInkResponseState extends State { - var _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/lib/views/widgets/StatefulWidget/InkResponse/node2_color.dart b/lib/views/widgets/StatefulWidget/InkResponse/node2_color.dart deleted file mode 100644 index 236e4fa55..000000000 --- a/lib/views/widgets/StatefulWidget/InkResponse/node2_color.dart +++ /dev/null @@ -1,44 +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 { - @override - _ColorInkResponseState createState() => _ColorInkResponseState(); -} - -class _ColorInkResponseState extends State { - var _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/lib/views/widgets/StatefulWidget/InkWell/node1_base.dart b/lib/views/widgets/StatefulWidget/InkWell/node1_base.dart deleted file mode 100644 index de2b03d04..000000000 --- a/lib/views/widgets/StatefulWidget/InkWell/node1_base.dart +++ /dev/null @@ -1,42 +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 { - @override - _CustomInkWellState createState() => _CustomInkWellState(); -} - -class _CustomInkWellState extends State { - var _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/lib/views/widgets/StatefulWidget/InkWell/node2_color.dart b/lib/views/widgets/StatefulWidget/InkWell/node2_color.dart deleted file mode 100644 index 442a992d2..000000000 --- a/lib/views/widgets/StatefulWidget/InkWell/node2_color.dart +++ /dev/null @@ -1,44 +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 { - @override - _ColorInkWellState createState() => _ColorInkWellState(); -} - -class _ColorInkWellState extends State { - var _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/lib/views/widgets/StatefulWidget/InputDecorator/node1_base.dart b/lib/views/widgets/StatefulWidget/InputDecorator/node1_base.dart deleted file mode 100644 index 26c7452ca..000000000 --- a/lib/views/widgets/StatefulWidget/InputDecorator/node1_base.dart +++ /dev/null @@ -1,33 +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 -/// 说明: 231 InputDecorator 输入装饰 在外层包裹输入的装饰,是TextField的底层核心组件之一,一般不单独使用。 -// { -// "widgetId": 231, -// "name": "InputDecorator基本使用", -// "priority": 1, -// "subtitle": "【decoration】 : 装饰对象 【InputDecoration】\n" -// "【textAlign】 : 文字对齐方式 【TextAlign】\n" -// "【child】 : 子组件 【Widget】", -// } - -class InputDecoratorDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: InputDecorator( - decoration: InputDecoration(), - child: EditableText( - controller: TextEditingController(text:'hello'), - focusNode: FocusNode(), - style: TextStyle(fontSize: 12,color: Colors.black), - cursorColor: Colors.blue, - backgroundCursorColor: Colors.orange, - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/InteractiveViewer/node1_base.dart b/lib/views/widgets/StatefulWidget/InteractiveViewer/node1_base.dart deleted file mode 100644 index 41979fdd0..000000000 --- a/lib/views/widgets/StatefulWidget/InteractiveViewer/node1_base.dart +++ /dev/null @@ -1,57 +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 -/// 说明: 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 { - - @override - Widget build(BuildContext context) { - return Container( - height: 150, - color: Colors.grey.withAlpha(33), - child: InteractiveViewer( - boundaryMargin: EdgeInsets.all(40.0), - maxScale: 2.5, - minScale: 0.3, - panEnabled: true, - scaleEnabled: true, - child: Container( - 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/lib/views/widgets/StatefulWidget/InteractiveViewer/node2_constrained.dart b/lib/views/widgets/StatefulWidget/InteractiveViewer/node2_constrained.dart deleted file mode 100644 index ecb1c76a2..000000000 --- a/lib/views/widgets/StatefulWidget/InteractiveViewer/node2_constrained.dart +++ /dev/null @@ -1,59 +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 -/// 说明: -// { -// "widgetId": 351, -// "name": "constrained属性测试", -// "priority": 2, -// "subtitle": "【constrained】 : 受约束的 【bool】", -// } - -class InteractiveViewerDemo2 extends StatelessWidget { - - Widget build(BuildContext context) { - const int _rowCount = 20; - const int _columnCount = 4; - - return Container( - 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: EdgeInsets.all(2), - height: 50, - alignment: Alignment.center, - color: _colorful(row,column), - child: Text('($row,$column)',style: TextStyle(fontSize: 20,color: Colors.white),), - ), - ], - ), - ]; - } - - final colors = [Colors.red,Colors.yellow,Colors.blue,Colors.green]; - final colors2 = [Colors.yellow,Colors.blue,Colors.green,Colors.red]; - - _colorful(int row, int column ) => row % 2==0?colors[column]:colors2[column]; -} diff --git a/lib/views/widgets/StatefulWidget/InteractiveViewer/node3_controller.dart b/lib/views/widgets/StatefulWidget/InteractiveViewer/node3_controller.dart deleted file mode 100644 index 653378980..000000000 --- a/lib/views/widgets/StatefulWidget/InteractiveViewer/node3_controller.dart +++ /dev/null @@ -1,156 +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 -/// 说明: -// { -// "widgetId": 351, -// "name": "变换控制器的使用", -// "priority": 3, -// "subtitle": "【transformationController】 : 变换控制器 【TransformationController】", -// } - -class InteractiveViewerDemo3 extends StatefulWidget { - @override - _InteractiveViewerDemo3State createState() => _InteractiveViewerDemo3State(); -} - -class _InteractiveViewerDemo3State extends State - with SingleTickerProviderStateMixin { - final TransformationController _transformationController = - TransformationController(); - Animation _animationReset; - AnimationController _controllerReset; - - void _onAnimateReset() { - _transformationController.value = _animationReset.value; - if (!_controllerReset.isAnimating) { - _animationReset?.removeListener(_onAnimateReset); - _animationReset = null; - _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); - _animationReset = null; - _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: EdgeInsets.all(40), - transformationController: _transformationController, - minScale: 0.1, - maxScale: 1.8, - onInteractionStart: _onInteractionStart, - child: Container( - child: Image.asset('assets/images/caver.webp'), - ), - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - _buildButton(), - _buildButton2(), - _buildButton3(), - ], - ) - ], - ); - } - - Widget _buildButton() { - return MaterialButton( - child: Icon( - Icons.refresh, - color: Colors.white, - ), - color: Colors.green, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onPressed: _animateResetInitialize); - } - - var _x = 0.0; - - Widget _buildButton2() { - return MaterialButton( - child: Icon( - Icons.navigate_before, - color: Colors.white, - ), - color: Colors.green, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onPressed: () { - var temp = _transformationController.value.clone(); - temp.translate(_x - 4); - _transformationController.value = temp; - }); - } - - Widget _buildButton3() { - return MaterialButton( - child: Icon( - Icons.navigate_next, - color: Colors.white, - ), - color: Colors.green, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onPressed: () { - var temp = _transformationController.value.clone(); - temp.translate(_x + 4); - _transformationController.value = temp; - }); - } -} diff --git a/lib/views/widgets/StatefulWidget/LicensePage/node1_base.dart b/lib/views/widgets/StatefulWidget/LicensePage/node1_base.dart deleted file mode 100644 index f031c9081..000000000 --- a/lib/views/widgets/StatefulWidget/LicensePage/node1_base.dart +++ /dev/null @@ -1,30 +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 { - @override - Widget build(BuildContext context) { - return Container( - width: MediaQuery.of(context).size.width, - height: 400, - child: LicensePage( - applicationIcon: FlutterLogo(), - applicationVersion: 'v0.0.1', - applicationName: 'Flutter Unit', - applicationLegalese: 'Copyright© 2018-2020 张风捷特烈', - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/LinearProgressIndicator/node1_base.dart b/lib/views/widgets/StatefulWidget/LinearProgressIndicator/node1_base.dart deleted file mode 100644 index 42cf95b66..000000000 --- a/lib/views/widgets/StatefulWidget/LinearProgressIndicator/node1_base.dart +++ /dev/null @@ -1,42 +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 { - @override - _CustomLinearProgressIndicatorState createState() => - _CustomLinearProgressIndicatorState(); -} - -class _CustomLinearProgressIndicatorState - extends State { - var data = [0.2, 0.4, 0.6, 0.8, null]; - - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: data - .map((e) => Container( - width: 50, - height: 3, - child:LinearProgressIndicator( - value: e, - backgroundColor: Colors.grey.withAlpha(33), - valueColor: AlwaysStoppedAnimation(Colors.orange), - ), - )) - .toList(), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/ListWheelScrollView/node1_base.dart b/lib/views/widgets/StatefulWidget/ListWheelScrollView/node1_base.dart deleted file mode 100644 index 3e4e274e0..000000000 --- a/lib/views/widgets/StatefulWidget/ListWheelScrollView/node1_base.dart +++ /dev/null @@ -1,86 +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 { - @override - _CustomListWheelScrollViewState createState() => _CustomListWheelScrollViewState(); -} - -class _CustomListWheelScrollViewState extends State { - var 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(), - Container( - 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: 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: 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/lib/views/widgets/StatefulWidget/LongPressDraggable/node1_base.dart b/lib/views/widgets/StatefulWidget/LongPressDraggable/node1_base.dart deleted file mode 100644 index 1d2166d56..000000000 --- a/lib/views/widgets/StatefulWidget/LongPressDraggable/node1_base.dart +++ /dev/null @@ -1,100 +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 { - @override - _CustomLongPressDraggableState createState() => _CustomLongPressDraggableState(); -} - -class _CustomLongPressDraggableState extends State { - Color _color = Colors.grey; - String _info = 'DragTarget'; - - @override - Widget build(BuildContext context) { - return Container( - child: Column( - children: [ - Wrap( - children: _buildColors(), - spacing: 10, - ), - SizedBox(height: 20,), - _buildDragTarget() - ], - ), - ); - } - - List _buildColors() { - var colors = [ - Colors.red, - Colors.yellow, - Colors.blue, - Colors.green, - Colors.orange, - Colors.purple, - Colors.cyanAccent - ]; - return 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: 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: TextStyle(color: Colors.white), - ), - ))); - } -} diff --git a/lib/views/widgets/StatefulWidget/Material/node1_base.dart b/lib/views/widgets/StatefulWidget/Material/node1_base.dart deleted file mode 100644 index 1461e8407..000000000 --- a/lib/views/widgets/StatefulWidget/Material/node1_base.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": 160, -// "name": 'Material基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "【type】 : 类型 【MaterialType】\n" -// "【elevation】 : 影深 【double】\n" -// "【shadowColor】 : 阴影颜色 【Color】\n" -// "【color】 : 颜色 【Color】", -// } -class CustomMaterial extends StatelessWidget { - @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: TextStyle(color: Colors.black), - ), - ), - ); -} diff --git a/lib/views/widgets/StatefulWidget/Material/node2_shape.dart b/lib/views/widgets/StatefulWidget/Material/node2_shape.dart deleted file mode 100644 index 67a3d479c..000000000 --- a/lib/views/widgets/StatefulWidget/Material/node2_shape.dart +++ /dev/null @@ -1,65 +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 { - - final shapeMap = { - '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(0xFFFFDFDFDF)), - left: BorderSide(width: 5.0, color: Color(0xFFFFDFDFDF)), - right: BorderSide(width: 5.0, color: Color(0xFFFF7F7F7F)), - bottom: BorderSide(width: 5.0, color: Color(0xFFFF7F7F7F)), - ), - 'Circle': CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - 'RoundedRectangleBorder': RoundedRectangleBorder( - side: BorderSide(width: 1.0, color: Colors.black), - borderRadius: BorderRadius.all(Radius.circular(15))), - 'ContinuousRectangleBorder': ContinuousRectangleBorder( - side: BorderSide.none, - borderRadius: BorderRadius.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: TextStyle(color: Colors.white), - child: Container( - alignment: Alignment.center, - width: 300, - height: 60, - child: Text( - type, - ), - ), - ); -} diff --git a/lib/views/widgets/StatefulWidget/MaterialApp/node1_base.dart b/lib/views/widgets/StatefulWidget/MaterialApp/node1_base.dart deleted file mode 100644 index a31592365..000000000 --- a/lib/views/widgets/StatefulWidget/MaterialApp/node1_base.dart +++ /dev/null @@ -1,128 +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 { - @override - _WidgetsAppDemoState createState() => _WidgetsAppDemoState(); -} - -class _WidgetsAppDemoState extends State { - var _debugShowCheckedModeBanner = false; - var _showPerformanceOverlay = false; - var _debugShowMaterialGrid = false; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildSwitchers(), - Container( - height: 250, - child: MaterialApp( - debugShowCheckedModeBanner: _debugShowCheckedModeBanner, - showPerformanceOverlay: _showPerformanceOverlay, - debugShowMaterialGrid: _debugShowMaterialGrid, - home: HomePage(), - ), - ), - ], - ); - } - - Widget _buildSwitchers() { - return DefaultTextStyle( - style: TextStyle(color: Colors.blue), - child: Wrap( - spacing: 10, - children: [ - Column( - children: [ - Switch( - value: _showPerformanceOverlay, - onChanged: (v) { - setState(() { - _showPerformanceOverlay = v; - }); - }, - ), - Text('性能浮层') - ], - ), - Column( - children: [ - Switch( - value: _debugShowCheckedModeBanner, - onChanged: (v) { - setState(() { - _debugShowCheckedModeBanner = v; - }); - }, - ), - Text('开启角标') - ], - ), - Column( - children: [ - Switch( - value: _debugShowMaterialGrid, - onChanged: (v) { - setState(() { - _debugShowMaterialGrid = v; - }); - }, - ), - Text('开启网格') - ], - ) - ], - ), - ); - } -} - -class HomePage extends StatefulWidget { - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State { - var _count = 0; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - alignment: Alignment(0, 0.7), - child: Text( - '你点击了$_count次', - style: TextStyle(fontSize: 18, color: Colors.blue), - ), - ), - floatingActionButton: FloatingActionButton( - child: Icon(Icons.add), - onPressed: () { - setState(() { - _count++; - }); - }, - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/MergeableMaterial/node1_base.dart b/lib/views/widgets/StatefulWidget/MergeableMaterial/node1_base.dart deleted file mode 100644 index c4b98430d..000000000 --- a/lib/views/widgets/StatefulWidget/MergeableMaterial/node1_base.dart +++ /dev/null @@ -1,81 +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 -/// 说明: 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 { - @override - _MergeableMaterialDemoState createState() => _MergeableMaterialDemoState(); -} - -class _MergeableMaterialDemoState extends State { - List items=[]; - - @override - void initState() { - super.initState(); - _init(20); - } - - @override - Widget build(BuildContext context) { - return Container( - 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/lib/views/widgets/StatefulWidget/MonthPicker/node1_base.dart b/lib/views/widgets/StatefulWidget/MonthPicker/node1_base.dart deleted file mode 100644 index ee72ab5fe..000000000 --- a/lib/views/widgets/StatefulWidget/MonthPicker/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": 135, -// "name": 'MonthPicker基本使用', -// "priority": 1, -// "subtitle": -// "【selectedDate】 : 选中日期 【DateTime】\n" -// "【firstDate】 : 最前日期限制 【DateTime】\n" -// "【lastDate】 : 最后日期限制 【DateTime】\n" -// "【onChanged】 : 点击回调 【Function(DateTime)】", -// } -class CustomMonthPicker extends StatefulWidget { - @override - _CustomMonthPickerState createState() => _CustomMonthPickerState(); -} - -class _CustomMonthPickerState extends State { - DateTime _date = DateTime.now(); - - @override - Widget build(BuildContext context) { - return Container( - height: 350, - child: MonthPicker( - selectedDate: _date, - onChanged: (date) => setState(() => _date = date), - firstDate: DateTime(2018), - lastDate: DateTime(2030), - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/MouseRegion/node1_base.dart b/lib/views/widgets/StatefulWidget/MouseRegion/node1_base.dart deleted file mode 100644 index 66ebac1d0..000000000 --- a/lib/views/widgets/StatefulWidget/MouseRegion/node1_base.dart +++ /dev/null @@ -1,69 +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 -/// 说明: 293 MouseRegion 用于鼠标事件监听的组件,通常用于桌面和Web平台,可监听鼠标的移入、移除、移动事件。 -// { -// "widgetId": 293, -// "name": "MouseRegion基本使用", -// "priority": 1, -// "subtitle": "【onEnter】 : 移入事件 【PointerEnterEventListener】\n" -// "【onHover】: 移动事件 【PointerHoverEventListener】\n" -// "【onExit】: 移出事件 【PointerExitEventListener】", -// } - -class MouseRegionDemo extends StatefulWidget { - @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(Size(300.0, 200.0)), - child: MouseRegion( - onEnter: _incrementEnter, - onHover: _updateLocation, - onExit: _incrementExit, - child: Container( - color: Colors.lightBlueAccent, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - 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/lib/views/widgets/StatefulWidget/Navigator/node1_base.dart b/lib/views/widgets/StatefulWidget/Navigator/node1_base.dart deleted file mode 100644 index 448e07232..000000000 --- a/lib/views/widgets/StatefulWidget/Navigator/node1_base.dart +++ /dev/null @@ -1,170 +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 { - @override - Widget build(BuildContext context) { - return Container( - 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: (_) => HomeContent(), settings: settings); - case "/red": - return MaterialPageRoute(builder: (_) => RedPage(), settings: settings); - case "/yellow": - return MaterialPageRoute( - builder: (_) => YellowPage(), settings: settings); - case "/green": - return MaterialPageRoute( - builder: (_) => GreenPage(), settings: settings); - default: - return MaterialPageRoute( - builder: (_) => 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 { - @override - Widget build(BuildContext context) { - return Container( - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - RaisedButton( - color: Colors.red, - onPressed: () { - Navigator.pushNamed(context, '/red'); - }, - ), - RaisedButton( - color: Colors.yellow, - onPressed: () { - Navigator.pushNamed(context, '/yellow'); - }, - ), - RaisedButton( - color: Colors.green, - onPressed: () { - Navigator.pushNamed(context, '/green'); - }, - ) - ], - ), - ], - ), - ); - } -} - -class RedPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text("RedPage"), - ), - body: Container( - color: Colors.red, - ), - ); - } -} - -class YellowPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text("YellowPage"), - ), - body: Container( - color: Colors.yellow, - ), - ); - } -} - -class GreenPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: Text("GreenPage"), - ), - body: Container( - color: Colors.green, - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/NestedScrollView/node1_base.dart b/lib/views/widgets/StatefulWidget/NestedScrollView/node1_base.dart deleted file mode 100644 index 51b2cc64b..000000000 --- a/lib/views/widgets/StatefulWidget/NestedScrollView/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": 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 _tabs = ['风神传', '封妖志', "幻将录", "永恒传说"]; - - @override - Widget build(BuildContext context) { - return Container( - 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/lib/views/widgets/StatefulWidget/OutlinedButton/node1_base.dart b/lib/views/widgets/StatefulWidget/OutlinedButton/node1_base.dart deleted file mode 100644 index 27498b82a..000000000 --- a/lib/views/widgets/StatefulWidget/OutlinedButton/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 -/// 说明: 355 OutlinedButton Material风格的边线按钮,表现和OutlineButton类似。可通过样式更改边框、颜色、阴影等属性。 -// { -// "widgetId": 355, -// "name": 'OutlinedButton基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 是否具有滚动主体 【Widget】\n" -// "【onPressed】 : 点击事件 【VoidCallback】\n" -// "【onLongPress】 : 长按事件 【VoidCallback】", -// } - -class OutlinedButtonDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - height: 60, - child: Wrap( - spacing: 20, - children: [ - OutlinedButton( - child: Text('OutlinedButton'), - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - OutlinedButton( - child: Text('禁用按钮'), - onPressed: null, - onLongPress: null, - ), - ], - )); - } - - _onPressed() {} - - _onLongPress() {} -} diff --git a/lib/views/widgets/StatefulWidget/OutlinedButton/node2_style.dart b/lib/views/widgets/StatefulWidget/OutlinedButton/node2_style.dart deleted file mode 100644 index 48a97a91f..000000000 --- a/lib/views/widgets/StatefulWidget/OutlinedButton/node2_style.dart +++ /dev/null @@ -1,58 +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 { - @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: Text('ElevatedButton样式'), - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - OutlinedButton( - style: TextButton.styleFrom( - backgroundColor: Colors.white, - primary: Colors.black, - side: BorderSide(color: Colors.blue,width: 1), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)) - ), - // elevation: 2, - shadowColor: Colors.orangeAccent), - child: Text('ElevatedButton边线'), - autofocus: false, - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - ], - ), - ); - } - - _onPressed() {} - - _onLongPress() {} -} diff --git a/lib/views/widgets/StatefulWidget/Overlay/node1_base.dart b/lib/views/widgets/StatefulWidget/Overlay/node1_base.dart deleted file mode 100644 index 4589626ba..000000000 --- a/lib/views/widgets/StatefulWidget/Overlay/node1_base.dart +++ /dev/null @@ -1,104 +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 { - @override - Widget build(BuildContext context) { - return Wrap( - children: [ - Container( - height: 50, - child: RawMaterialButton( - elevation: 2, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - fillColor: Colors.blue, - splashColor: Colors.orange, - textStyle: TextStyle(color: Colors.white), - child: Icon(Icons.add), - onPressed: ()=>showFloating(context), - ), - ), - Container( - height: 50, - child: RawMaterialButton( - elevation: 2, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - fillColor: Colors.red, - splashColor: Colors.orange, - textStyle: TextStyle(color: Colors.white), - child: Icon(Icons.remove), - onPressed: hideFloating, - ), - ), - ], - ); - } -} - -bool show = false; -Offset offset = Offset(200, 200); - -final 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 - 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: 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/lib/views/widgets/StatefulWidget/PageView/node1_base.dart b/lib/views/widgets/StatefulWidget/PageView/node1_base.dart deleted file mode 100644 index 5927b1dc5..000000000 --- a/lib/views/widgets/StatefulWidget/PageView/node1_base.dart +++ /dev/null @@ -1,59 +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 { - final 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], - ]; - - @override - Widget build(BuildContext context) { - return Container( - 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(color: Colors.white, - fontSize:24,shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - )) - .toList(), - ), - ); - } - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/lib/views/widgets/StatefulWidget/PageView/node2_direction.dart b/lib/views/widgets/StatefulWidget/PageView/node2_direction.dart deleted file mode 100644 index 63ccf6819..000000000 --- a/lib/views/widgets/StatefulWidget/PageView/node2_direction.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": 2, -// "subtitle": -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【reverse】 : 是否反向 【bool】", -// } -class DirectionPageView extends StatelessWidget { - final 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 Container( - 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(color: Colors.white, - fontSize: 24, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - )) - .toList(), - ), - ); - } - - String colorString(Color color) => - "#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}"; -} diff --git a/lib/views/widgets/StatefulWidget/PageView/node3_controller.dart b/lib/views/widgets/StatefulWidget/PageView/node3_controller.dart deleted file mode 100644 index e98c0ba5e..000000000 --- a/lib/views/widgets/StatefulWidget/PageView/node3_controller.dart +++ /dev/null @@ -1,84 +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 { - @override - _CtrlPageViewState createState() => _CtrlPageViewState(); -} - -class _CtrlPageViewState extends State { - final 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], - ]; - - PageController _controller; - - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - @override - void initState() { - super.initState(); - _controller=PageController( - viewportFraction: 0.8, - initialPage: (data.length/2).round() - ); - - } - - @override - Widget build(BuildContext context) { - return Container( - 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(color: Colors.white, - fontSize: 24, 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/lib/views/widgets/StatefulWidget/PaginatedDataTable/node1_base.dart b/lib/views/widgets/StatefulWidget/PaginatedDataTable/node1_base.dart deleted file mode 100644 index 2d56de2cc..000000000 --- a/lib/views/widgets/StatefulWidget/PaginatedDataTable/node1_base.dart +++ /dev/null @@ -1,214 +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 { - @override - State createState() => _PaginatedDataTableDemoState(); -} - -class _PaginatedDataTableDemoState extends State { - int _rowsPerPage = 5; - - int _sortColumnIndex; - bool _sortAscending = true; - - final DessertDataSource _dessertsDataSource = DessertDataSource(); - - void sort( - Comparable getField(HeroInfo d), int columnIndex, bool ascending) { - _dessertsDataSource.sort(getField, ascending); - setState(() { - _sortColumnIndex = columnIndex; - _sortAscending = ascending; - }); - } - - @override - Widget build(BuildContext context) { - return Container( - height: 300, - width: 350, - child: SingleChildScrollView( - child: PaginatedDataTable( - actions: [ - IconButton(icon: Icon(Icons.add), onPressed: null), - ], - header: const Text( - '《旷古奇书》-角色预设', - style: TextStyle(color: Colors.blue), - ), - rowsPerPage: _rowsPerPage, - availableRowsPerPage: [5, 8, 10, 15], - onRowsPerPageChanged: (int value) { - setState(() { - _rowsPerPage = value; - }); - }, - 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 getField(HeroInfo d), 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) { - _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) { - for (HeroInfo dessert in _desserts) dessert.selected = checked; - _selectedCount = checked ? _desserts.length : 0; - notifyListeners(); - } -} diff --git a/lib/views/widgets/StatefulWidget/PopupMenuButton/node1_base.dart b/lib/views/widgets/StatefulWidget/PopupMenuButton/node1_base.dart deleted file mode 100644 index 2e2330be0..000000000 --- a/lib/views/widgets/StatefulWidget/PopupMenuButton/node1_base.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../components/project/dialogs/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 { - @override - _CustomPopupMenuButtonState createState() => _CustomPopupMenuButtonState(); -} - -class _CustomPopupMenuButtonState extends State { - final map = { - "关于": Icons.info_outline, - "帮助": Icons.help_outline, - "问题反馈": Icons.add_comment, - }; - - @override - Widget build(BuildContext context) { - return PopupMenuButton( - itemBuilder: (context) => buildItems(), - offset: Offset(0, 50), - color: Color(0xffF4FFFA), - elevation: 1, - shape: 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/lib/views/widgets/StatefulWidget/PopupMenuDivider/node1_base.dart b/lib/views/widgets/StatefulWidget/PopupMenuDivider/node1_base.dart deleted file mode 100644 index 3dc594ddb..000000000 --- a/lib/views/widgets/StatefulWidget/PopupMenuDivider/node1_base.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../components/project/dialogs/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 { - final map = { - "关于": Icons.info_outline, - "帮助": Icons.help_outline, - "问题反馈": Icons.add_comment, - }; - - @override - Widget build(BuildContext context) { - return Container( - child: Column( - children: [ - _buildPopupMenuButton(context), - PopupMenuDivider(), - ], - ), - ); - } - - PopupMenuButton _buildPopupMenuButton(BuildContext context) { - return PopupMenuButton( - itemBuilder: (context) => [ - ...buildItems().sublist(0, 2), - PopupMenuDivider(), - ...buildItems().sublist(2, 3) - ], - offset: Offset(0, 50), - color: Color(0xffF4FFFA), - elevation: 1, - shape: 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/lib/views/widgets/StatefulWidget/PositionedTransition/node1_base.dart b/lib/views/widgets/StatefulWidget/PositionedTransition/node1_base.dart deleted file mode 100644 index 0413296d8..000000000 --- a/lib/views/widgets/StatefulWidget/PositionedTransition/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": 93, -// "name": 'PositionedTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【rect】 : 动画 【Animation】\n" -// " PositionedTransition组件只能在Stack内起作用", -// } -class CustomPositionedTransition extends StatefulWidget { - @override - _CustomPositionedTransitionState createState() => - _CustomPositionedTransitionState(); -} - -class _CustomPositionedTransitionState extends State - with SingleTickerProviderStateMixin { - AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController(vsync: this, duration: 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: RelativeRect.fromLTRB(0, 50, 150, 100), - end: RelativeRect.fromLTRB(60, 0, 150, -50), - ).animate(_ctrl), - child: Icon( - Icons.android, - color: Colors.green, - size: 50, - ), - ) - ], - ), - )); - } -} diff --git a/lib/views/widgets/StatefulWidget/Radio/node1_base.dart b/lib/views/widgets/StatefulWidget/Radio/node1_base.dart deleted file mode 100644 index f36599140..000000000 --- a/lib/views/widgets/StatefulWidget/Radio/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": 45, -// "name": 'Radio基本使用', -// "priority": 1, -// "subtitle": -// "【value】 : 选钮值 【T】\n" -// "【groupValue】 : 当前匹配值 【T】\n" -// "【activeColor】 : 激活颜色 【Color】\n" -// "【onChanged】 : 改变时回调 【Function(T)】", -// } -class CustomRadio extends StatefulWidget { - @override - _CustomRadioState createState() => _CustomRadioState(); -} - -class _CustomRadioState extends State { - var 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))) - .toList(), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/RangeSlider/node1_base.dart b/lib/views/widgets/StatefulWidget/RangeSlider/node1_base.dart deleted file mode 100644 index 9dacc4096..000000000 --- a/lib/views/widgets/StatefulWidget/RangeSlider/node1_base.dart +++ /dev/null @@ -1,52 +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 { - @override - _CustomRangeSliderState createState() => _CustomRangeSliderState(); -} - -class _CustomRangeSliderState extends State { - RangeValues _rangeValues = 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/lib/views/widgets/StatefulWidget/RawChip/node1_press.dart b/lib/views/widgets/StatefulWidget/RawChip/node1_press.dart deleted file mode 100644 index 9b3938ce1..000000000 --- a/lib/views/widgets/StatefulWidget/RawChip/node1_press.dart +++ /dev/null @@ -1,38 +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": 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 { - @override - Widget build(BuildContext context) { - return Container( - child: RawChip( - padding: EdgeInsets.all(5), - labelPadding: EdgeInsets.all(3), - label: 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/lib/views/widgets/StatefulWidget/RawChip/node2_select.dart b/lib/views/widgets/StatefulWidget/RawChip/node2_select.dart deleted file mode 100644 index e4fc3b1ac..000000000 --- a/lib/views/widgets/StatefulWidget/RawChip/node2_select.dart +++ /dev/null @@ -1,46 +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": 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 { - @override - _SelectRawChipState createState() => _SelectRawChipState(); -} - -class _SelectRawChipState extends State { - bool _selected = false; - @override - Widget build(BuildContext context) { - return Container( - child: RawChip( - selected: _selected, - padding: EdgeInsets.all(5), - labelPadding: EdgeInsets.all(3), - deleteIconColor: Colors.red, - selectedColor: Colors.orangeAccent.withAlpha(44), - label: 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/lib/views/widgets/StatefulWidget/RawGestureDetector/node1_base.dart b/lib/views/widgets/StatefulWidget/RawGestureDetector/node1_base.dart deleted file mode 100644 index f493088da..000000000 --- a/lib/views/widgets/StatefulWidget/RawGestureDetector/node1_base.dart +++ /dev/null @@ -1,68 +0,0 @@ -import 'package:flutter/cupertino.dart'; -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 { - @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/lib/views/widgets/StatefulWidget/RawKeyboardListener/node1_base.dart b/lib/views/widgets/StatefulWidget/RawKeyboardListener/node1_base.dart deleted file mode 100644 index 5dc14955d..000000000 --- a/lib/views/widgets/StatefulWidget/RawKeyboardListener/node1_base.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'package:flutter/cupertino.dart'; -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 { - @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: Container( - width: 300, - child: Row( - children: [ - Expanded( - child: TextField( - decoration: InputDecoration( - border: OutlineInputBorder() - ), - ), - ), - 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/lib/views/widgets/StatefulWidget/RawMaterialButton/node1_base.dart b/lib/views/widgets/StatefulWidget/RawMaterialButton/node1_base.dart deleted file mode 100644 index f213fe80d..000000000 --- a/lib/views/widgets/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 { - @override - Widget build(BuildContext context) { - return Container( - child: Wrap( - spacing: 20, - children: [ - RawMaterialButton( - elevation: 2, - fillColor: Colors.green, - splashColor: Colors.orange, - textStyle: TextStyle(color: Colors.white), - onLongPress: ()=>print('onLongPress'), - child: Icon(Icons.remove), - onPressed: ()=>print('onPressed'), - ), - RawMaterialButton( - elevation: 2, - fillColor: Colors.blue, - splashColor: Colors.orange, - textStyle: TextStyle(color: Colors.white), - onLongPress: ()=>print('onLongPress'), - child: Text('Push'), - onPressed: ()=>print('onPressed'), - ), - RawMaterialButton( - elevation: 2, - fillColor: Colors.red, - splashColor: Colors.orange, - textStyle: TextStyle(color: Colors.white), - onLongPress: ()=>print('onLongPress'), - child: Icon(Icons.add), - onPressed: ()=>print('onPressed'), - ), - - ], - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatefulWidget/RawMaterialButton/node2_shape.dart b/lib/views/widgets/StatefulWidget/RawMaterialButton/node2_shape.dart deleted file mode 100644 index 41a678a72..000000000 --- a/lib/views/widgets/StatefulWidget/RawMaterialButton/node2_shape.dart +++ /dev/null @@ -1,63 +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 { - @override - Widget build(BuildContext context) { - return Container( - child: Wrap( - spacing: 20, - children: [ - RawMaterialButton( - elevation: 2, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - fillColor: Colors.green, - splashColor: Colors.orange, - textStyle: TextStyle(color: Colors.white), - onLongPress: ()=>print('onLongPress'), - child: Icon(Icons.remove), - onPressed: ()=>print('onPressed'), - ), - RawMaterialButton( - shape:RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(15))), - elevation: 0, - highlightElevation: 0, - fillColor: Colors.blue, - splashColor: Colors.orange, - textStyle: TextStyle(color: Colors.white), - onLongPress: ()=>print('onLongPress'), - child: Text('Push'), - onPressed: ()=>print('onPressed'), - ), - RawMaterialButton( - elevation: 2, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - fillColor: Colors.red, - splashColor: Colors.orange, - textStyle: TextStyle(color: Colors.white), - onLongPress: ()=>print('onLongPress'), - child: Icon(Icons.add), - onPressed: ()=>print('onPressed'), - ), - - ], - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatefulWidget/RefreshIndicator/node1_base.dart b/lib/views/widgets/StatefulWidget/RefreshIndicator/node1_base.dart deleted file mode 100644 index a3c25c823..000000000 --- a/lib/views/widgets/StatefulWidget/RefreshIndicator/node1_base.dart +++ /dev/null @@ -1,54 +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 { - @override - _CustomRefreshIndicatorState createState() => _CustomRefreshIndicatorState(); -} - -class _CustomRefreshIndicatorState extends State { - int _count = 0; - - @override - Widget build(BuildContext context) { - - return Container( - 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: TextStyle(color: Colors.white,fontSize: 40)), - ), - ), - ), - ); - } - - Future _increment() async { - await Future.delayed(Duration(seconds: 2)); - setState(() { - _count++; - }); - } -} diff --git a/lib/views/widgets/StatefulWidget/RelativePositionedTransition/node1_base.dart b/lib/views/widgets/StatefulWidget/RelativePositionedTransition/node1_base.dart deleted file mode 100644 index 8792e6eb4..000000000 --- a/lib/views/widgets/StatefulWidget/RelativePositionedTransition/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": 115, -// "name": 'RelativePositionedTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【size】 : 左和上的偏移量 【Size】\n" -// "【rect】 : 动画 【Animation】\n" -// " PositionedTransition组件只能在Stack内起作用", -// } -class CustomRelativePositionedTransition extends StatefulWidget { - @override - _CustomRelativePositionedTransitionState createState() => - _CustomRelativePositionedTransitionState(); -} - -class _CustomRelativePositionedTransitionState - extends State - with SingleTickerProviderStateMixin { - AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController(vsync: this, duration: 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: [ - RelativePositionedTransition( - size: Size(200, 100), - rect: RectTween( - begin: Rect.fromLTRB(0, 0, 50, 50), - end: Rect.fromLTRB(0, 0, 50, 50).translate(100, 50), - ).animate(_ctrl), - child: Icon( - Icons.android, - color: Colors.green, - size: 50, - ), - ) - ], - ), - )); - } -} diff --git a/lib/views/widgets/StatefulWidget/ReorderableListView/node1_base.dart b/lib/views/widgets/StatefulWidget/ReorderableListView/node1_base.dart deleted file mode 100644 index 09c2bfa8f..000000000 --- a/lib/views/widgets/StatefulWidget/ReorderableListView/node1_base.dart +++ /dev/null @@ -1,81 +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 { - @override - _CustomReorderableListViewState createState() => _CustomReorderableListViewState(); -} - -class _CustomReorderableListViewState extends State { - var 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 Container( - height: 250, - child: ReorderableListView( - padding: EdgeInsets.all(10), - header: Container( - color: Colors.blue, - alignment: Alignment.center, - height: 50, - child: 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: 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/lib/views/widgets/StatefulWidget/ReorderableListView/node2_direction.dart b/lib/views/widgets/StatefulWidget/ReorderableListView/node2_direction.dart deleted file mode 100644 index a0736123d..000000000 --- a/lib/views/widgets/StatefulWidget/ReorderableListView/node2_direction.dart +++ /dev/null @@ -1,76 +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 { - @override - _DirectionReorderableListViewState createState() => _DirectionReorderableListViewState(); -} - -class _DirectionReorderableListViewState extends State { - var 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 Container( - 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: 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/lib/views/widgets/StatefulWidget/RotationTransition/node1_base.dart b/lib/views/widgets/StatefulWidget/RotationTransition/node1_base.dart deleted file mode 100644 index cacf18b2f..000000000 --- a/lib/views/widgets/StatefulWidget/RotationTransition/node1_base.dart +++ /dev/null @@ -1,52 +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 { - @override - _CustomRotationTransitionState createState() => _CustomRotationTransitionState(); -} - -class _CustomRotationTransitionState extends State with SingleTickerProviderStateMixin{ - - 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: RotationTransition( - turns: CurvedAnimation(parent: _ctrl, curve: Curves.linear), - child: const Icon(Icons.camera_outlined,color: Colors.green,size: 60), - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/Scaffold/node1_base.dart b/lib/views/widgets/StatefulWidget/Scaffold/node1_base.dart deleted file mode 100755 index d18c17899..000000000 --- a/lib/views/widgets/StatefulWidget/Scaffold/node1_base.dart +++ /dev/null @@ -1,129 +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 { - CustomScaffold({Key key}) : super(key: key); - - @override - State createState() => _CustomScaffoldState(); -} - -// AppBar 默认的实例,有状态 -class _CustomScaffoldState extends State with SingleTickerProviderStateMixin { - final tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼']; - var _position = 0; - final iconsMap = { - "图鉴": Icons.home, - "动态": Icons.toys, - "喜欢": Icons.favorite, - "手册": Icons.class_, - "我的": Icons.account_circle, - }; - final _colors = [ - Colors.blue, - Colors.red, - Colors.yellow, - Colors.green, - Colors.purple, - ]; - - 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 Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height - 300, - child: Scaffold( - floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, - floatingActionButton: FloatingActionButton( - child: Icon(Icons.add), - onPressed: () {}, - ), - drawer: _buildLeftDrawer(), - endDrawer: _buildLeftDrawer(), - appBar: AppBar( - title: Text('风雅六社'), - backgroundColor: Colors.blue, - centerTitle: true, - actions: [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, - ), - ); - - Widget _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: TextStyle(fontWeight: FontWeight.bold), - showUnselectedLabels: false, - showSelectedLabels: true, - items: iconsMap.keys - .map((key) => BottomNavigationBarItem( - title: Text( - key, - ), - icon: Icon(iconsMap[key]), - backgroundColor: _colors[_position])) - .toList(), - ); - - Widget _buildTableBarView() => TabBarView( - controller: _tabController, - children: tabs - .map((e) => Center( - child: Text( - e, - style: TextStyle(color: Colors.blue, fontSize: 20), - ))) - .toList()); -} diff --git a/lib/views/widgets/StatefulWidget/ScaleTransition/node1_base.dart b/lib/views/widgets/StatefulWidget/ScaleTransition/node1_base.dart deleted file mode 100644 index 167ba93fa..000000000 --- a/lib/views/widgets/StatefulWidget/ScaleTransition/node1_base.dart +++ /dev/null @@ -1,49 +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 { - @override - _CustomScaleTransitionState createState() => _CustomScaleTransitionState(); -} - -class _CustomScaleTransitionState extends State - with SingleTickerProviderStateMixin { - AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController(vsync: this, duration: 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: Icon(Icons.android, color: Colors.green, size: 60), - ), - )); - } -} diff --git a/lib/views/widgets/StatefulWidget/Scrollable/node1_base.dart b/lib/views/widgets/StatefulWidget/Scrollable/node1_base.dart deleted file mode 100644 index 229d1deff..000000000 --- a/lib/views/widgets/StatefulWidget/Scrollable/node1_base.dart +++ /dev/null @@ -1,65 +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" -// "【color】: 子组件 【Color】", -// } - -class ScrollableDemo extends StatelessWidget { - final data = List.generate(32, (i) => Color(0xFF6600FF - 2 * i)); - - @override - Widget build(BuildContext context) { - return Container( - height: 250, - child: Scrollable( - axisDirection: AxisDirection.down, - physics: 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: EdgeInsets.only(top: 1), - alignment: Alignment.center, - width: 100, - height: 60, - color: data[index], - child: Text( - colorString(data[index]), - style: 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/lib/views/widgets/StatefulWidget/Scrollbar/node1_base.dart b/lib/views/widgets/StatefulWidget/Scrollbar/node1_base.dart deleted file mode 100644 index af04fac61..000000000 --- a/lib/views/widgets/StatefulWidget/Scrollbar/node1_base.dart +++ /dev/null @@ -1,59 +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 { - final 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 Container( - height: 200, - child: Scrollbar( - child: ListView( - padding: EdgeInsets.symmetric(horizontal: 5), - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: color, - child: Text( - colorString(color), - style: 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/lib/views/widgets/StatefulWidget/SelectableText/node1_base.dart b/lib/views/widgets/StatefulWidget/SelectableText/node1_base.dart deleted file mode 100644 index 354dca808..000000000 --- a/lib/views/widgets/StatefulWidget/SelectableText/node1_base.dart +++ /dev/null @@ -1,39 +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 { - final text = " 始臣之解牛之时,所见无非牛者。三年之后,未尝见全牛也。方今之时," - "臣以神遇而不以目视,官知止而神欲行。依乎天理,批大郤,导大窾,因其固然," - "技经肯綮之未尝,而况大軱乎!良庖岁更刀,割也;族庖月更刀,折也。" - "今臣之刀十九年矣,所解数千牛矣,而刀刃若新发于硎。彼节者有间,而刀刃者无厚;" - "以无厚入有间,恢恢乎其于游刃必有余地矣,是以十九年而刀刃若新发于硎。" - "虽然,每至于族,吾见其难为,怵然为戒,视为止,行为迟。动刀甚微,謋然已解,如土委地。" - "提刀而立,为之四顾,为之踌躇满志,善刀而藏之."; - - @override - Widget build(BuildContext context) { - return SelectableText( - text, - style: TextStyle(fontSize: 18, color: Colors.orange), - cursorColor: Colors.green, - cursorRadius: Radius.circular(3), - cursorWidth: 5, - showCursor: true, - autofocus: false, - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/SelectableText/node2_align.dart b/lib/views/widgets/StatefulWidget/SelectableText/node2_align.dart deleted file mode 100644 index 41667e82b..000000000 --- a/lib/views/widgets/StatefulWidget/SelectableText/node2_align.dart +++ /dev/null @@ -1,73 +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 { - @override - _AlignSelectableTextState createState() => _AlignSelectableTextState(); -} - -class _AlignSelectableTextState extends State { - final 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."; - var _textAlign = TextAlign.left; - - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildSelector(), - SelectableText( - text, - style: TextStyle(fontSize: 18, color: Colors.red), - cursorColor: Colors.green, - cursorRadius: Radius.circular(3), - cursorWidth: 5, - showCursor: true, - textAlign: _textAlign, - textDirection: TextDirection.ltr, - - autofocus: false, - ), - ], - ); - } - - Widget _buildSelector() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - 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; - }); - }), - ], - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/SizeTransition/node1_base.dart b/lib/views/widgets/StatefulWidget/SizeTransition/node1_base.dart deleted file mode 100644 index 31c4d731a..000000000 --- a/lib/views/widgets/StatefulWidget/SizeTransition/node1_base.dart +++ /dev/null @@ -1,64 +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": 92, -// "name": 'SizeTransition基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 孩子组件 【Widget】\n" -// "【axis】 : 轴向*2 【Axis】\n" -// "【sizeFactor】 : 动画 【Animation】", -// } -class CustomSizeTransition extends StatefulWidget { - @override - _CustomSizeTransitionState createState() => _CustomSizeTransitionState(); -} - -class _CustomSizeTransitionState extends State - with SingleTickerProviderStateMixin { - AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController(vsync: this, duration: 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: 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: Icon(Icons.android, color: Colors.green, size: 80)), - ), - ], - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/SlideTransition/node1_base.dart b/lib/views/widgets/StatefulWidget/SlideTransition/node1_base.dart deleted file mode 100644 index 18289878c..000000000 --- a/lib/views/widgets/StatefulWidget/SlideTransition/node1_base.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/cupertino.dart'; -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 { - @override - _CustomSlideTransitionState createState() => _CustomSlideTransitionState(); -} - -class _CustomSlideTransitionState extends State - with SingleTickerProviderStateMixin { - AnimationController _ctrl; - Animation animation; - - @override - void initState() { - super.initState(); - _ctrl = AnimationController( - vsync: this, - duration: const Duration(seconds: 2), - )..forward(); - - animation = Tween( - begin: Offset.zero, - end: 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/lib/views/widgets/StatefulWidget/Slider/node1_base.dart b/lib/views/widgets/StatefulWidget/Slider/node1_base.dart deleted file mode 100644 index 720d7810d..000000000 --- a/lib/views/widgets/StatefulWidget/Slider/node1_base.dart +++ /dev/null @@ -1,44 +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 { - @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: (value) { - setState(() { - _value = value; - }); - }), - ], - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/Slider/node2_lable.dart b/lib/views/widgets/StatefulWidget/Slider/node2_lable.dart deleted file mode 100644 index 86f890119..000000000 --- a/lib/views/widgets/StatefulWidget/Slider/node2_lable.dart +++ /dev/null @@ -1,46 +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 { - @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/lib/views/widgets/StatefulWidget/StatefulBuilder/node1_base.dart b/lib/views/widgets/StatefulWidget/StatefulBuilder/node1_base.dart deleted file mode 100644 index 9725373c0..000000000 --- a/lib/views/widgets/StatefulWidget/StatefulBuilder/node1_base.dart +++ /dev/null @@ -1,33 +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 -/// 说明: 242 StatefulBuilder 需要传入 builder 属性进行构造组件,在 builder 中可以使用 StateSetter 改变构造子组件的状态,即可以不用创建类而实现一个局部刷新的组件。 -// { -// "widgetId": 242, -// "name": 'StatefulBuilder基本使用', -// "priority": 1, -// "subtitle": -// "【builder】 : 组件构造器 【StatefulWidgetBuilder】", -// } - -class StatefulBuilderDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - int count = 0; - - return Container( - child: StatefulBuilder( - builder: (ctx, setState) => ElevatedButton( - child: Text("当前数字: $count"), - onPressed: () { - setState(() { - count++; - }); - }, - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/StatusTransitionWidget/node1_base.dart b/lib/views/widgets/StatefulWidget/StatusTransitionWidget/node1_base.dart deleted file mode 100644 index abfbd8d7d..000000000 --- a/lib/views/widgets/StatefulWidget/StatusTransitionWidget/node1_base.dart +++ /dev/null @@ -1,78 +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 { - @override - _StatusTransitionWidgetDemoState createState() => _StatusTransitionWidgetDemoState(); -} - -class _StatusTransitionWidgetDemoState extends State - with SingleTickerProviderStateMixin { - AnimationController _ctrl; - - @override - void initState() { - super.initState(); - _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 1))..forward(); - } - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: (){ - _ctrl.forward(from: 0); - }, - child: ColorStatusTransitionWidget( - animation: _ctrl, - ), - ); - } -} - -class ColorStatusTransitionWidget extends StatusTransitionWidget { - final Animation animation; - - ColorStatusTransitionWidget({Key key, this.animation}) - : super(key: key, animation: animation); - - @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: TextStyle(color: Colors.white),), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/Stepper/node1_base.dart b/lib/views/widgets/StatefulWidget/Stepper/node1_base.dart deleted file mode 100644 index d77d9a964..000000000 --- a/lib/views/widgets/StatefulWidget/Stepper/node1_base.dart +++ /dev/null @@ -1,111 +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 { - @override - _StepperDemoState createState() => _StepperDemoState(); -} - -class _StepperDemoState extends State { - int _position = 0; - - final stepsData = { - "填写表单":'请按表单填写个人信息。', - "邮箱校验":'已将邮件发送至您的邮箱,请按照相关指示对您的账号进行邮箱校验。', - "注册完成":'恭喜您,注册完成!', - }; - - final steps = [ - Step( - title: Text("填写表单"), - content: Container(height: 60, child: Text("请按表单填写个人信息")), - ), - Step(title: Text("邮箱校验"), content: Text("请对您的账号进行邮箱校验")), - Step(title: Text("注册完成"), content: Text("恭喜您,注册完成")), - ]; - - @override - Widget build(BuildContext context) { - return Container( - 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: (_, - {VoidCallback onStepContinue, VoidCallback onStepCancel}) { - return Row( - children: [ - RaisedButton( - color: Colors.blue, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onPressed: onStepContinue, - child: Icon( - Icons.check, - color: Colors.white, - ), - ), - RaisedButton( - color: Colors.red, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onPressed: onStepCancel, - child: 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: Container(height: 60, child: Text(stepsData[e])), - ); - }).toList()), - ); - } - _getState(index){ - if(_position==index) return StepState.editing; - if(_position>index) return StepState.complete; - return StepState.indexed; - } -} diff --git a/lib/views/widgets/StatefulWidget/Stepper/node2_type.dart b/lib/views/widgets/StatefulWidget/Stepper/node2_type.dart deleted file mode 100644 index 126904042..000000000 --- a/lib/views/widgets/StatefulWidget/Stepper/node2_type.dart +++ /dev/null @@ -1,111 +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 { - @override - _VerticalStepperState createState() => _VerticalStepperState(); -} - -class _VerticalStepperState extends State { - int _position = 0; - - final stepsData = { - "填写表单": '请按表单填写个人信息。', - "邮箱校验": '已将邮件发送至您的邮箱,请按照相关指示对您的账号进行邮箱校验。', - "注册完成": '恭喜您,注册完成!', - }; - - final steps = [ - Step( - title: Text("填写表单"), - content: Container(height: 60, child: Text("请按表单填写个人信息")), - ), - Step(title: Text("邮箱校验"), content: Text("请对您的账号进行邮箱校验")), - Step(title: Text("注册完成"), content: Text("恭喜您,注册完成")), - ]; - - @override - Widget build(BuildContext context) { - return Container( - child: Stepper( - type: StepperType.vertical, - currentStep: _position, - onStepTapped: (index) { - setState(() { - _position = index; - }); - }, - onStepContinue: () { - setState(() { - if (_position < 2) { - _position++; - } - }); - }, - onStepCancel: () { - if (_position > 0) { - setState(() { - _position--; - }); - } - }, - controlsBuilder: (_, - {VoidCallback onStepContinue, VoidCallback onStepCancel}) { - return Row( - children: [ - RaisedButton( - color: Colors.blue, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onPressed: onStepContinue, - child: Icon( - Icons.check, - color: Colors.white, - ), - ), - RaisedButton( - color: Colors.red, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onPressed: onStepCancel, - child: 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: Container(height: 60, child: Text(stepsData[e])), - ); - }).toList()), - ); - } - - _getState(index) { - if (_position == index) return StepState.editing; - if (_position > index) return StepState.complete; - return StepState.indexed; - } -} diff --git a/lib/views/widgets/StatefulWidget/StreamBuilder/node1_base.dart b/lib/views/widgets/StatefulWidget/StreamBuilder/node1_base.dart deleted file mode 100644 index 61a23f7ca..000000000 --- a/lib/views/widgets/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 { - @override - _CustomStreamBuilderState createState() => _CustomStreamBuilderState(); -} - -class _CustomStreamBuilderState extends State { - CountGenerator _generator = CountGenerator()..increment(); - - @override - void dispose() { - _generator.dispose(); //关闭控制器 - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Container( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - FlatButton( - color: Colors.blue, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - child: Icon( - Icons.add, - color: Colors.white, - ), - onPressed: () async { - await _generator.increment(); - }, - ), - _buildStreamBuilder(), - FlatButton( - color: Colors.blue, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - child: 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 Text('Done'); - } - if (snap.connectionState == ConnectionState.active) { - return Text( - snap.data.toString(), - style: Theme.of(context).textTheme.display1, - ); - } - if (snap.connectionState == ConnectionState.waiting) { - return CircularProgressIndicator(); - } - if (snap.hasError) { - return 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/lib/views/widgets/StatefulWidget/Switch/node1_base.dart b/lib/views/widgets/StatefulWidget/Switch/node1_base.dart deleted file mode 100644 index 1fc7cbc87..000000000 --- a/lib/views/widgets/StatefulWidget/Switch/node1_base.dart +++ /dev/null @@ -1,47 +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 { - @override - _CustomSwitchState createState() => _CustomSwitchState(); -} - -class _CustomSwitchState extends State { - final 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) => - 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/lib/views/widgets/StatefulWidget/Switch/node2_image.dart b/lib/views/widgets/StatefulWidget/Switch/node2_image.dart deleted file mode 100644 index 42a28d6a3..000000000 --- a/lib/views/widgets/StatefulWidget/Switch/node2_image.dart +++ /dev/null @@ -1,45 +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 { - @override - _ImageSwitchState createState() => _ImageSwitchState(); -} - -class _ImageSwitchState extends State { - final imgs = [ - "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: AssetImage('assets/images/pica.gif'), - onChanged: (v) { - setState(() => _checked = v); - })) - .toList(), - ); - } -} - diff --git a/lib/views/widgets/StatefulWidget/TabBarView/node1_base.dart b/lib/views/widgets/StatefulWidget/TabBarView/node1_base.dart deleted file mode 100644 index a47bbdb1c..000000000 --- a/lib/views/widgets/StatefulWidget/TabBarView/node1_base.dart +++ /dev/null @@ -1,70 +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 { - @override - _CustomTabBarViewState createState() => _CustomTabBarViewState(); -} - -class _CustomTabBarViewState extends State with SingleTickerProviderStateMixin { - final tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼']; - 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 Container( - child: 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: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - unselectedLabelStyle: TextStyle(fontSize: 16), - isScrollable: true, - controller: _tabController, - labelColor: Colors.blue, - indicatorWeight: 3, - indicatorPadding: 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: TextStyle(color: Colors.white, fontSize: 20), - ))).toList()); -} diff --git a/lib/views/widgets/StatefulWidget/TableRowInkWell/node1_base.dart b/lib/views/widgets/StatefulWidget/TableRowInkWell/node1_base.dart deleted file mode 100644 index a5c8285cc..000000000 --- a/lib/views/widgets/StatefulWidget/TableRowInkWell/node1_base.dart +++ /dev/null @@ -1,90 +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 { - @override - Widget build(BuildContext context) { - var title = _ItemBean("单位称", "量纲", "单位", "单位名称", "单位符号"); - var m = _ItemBean("长度", "L", "1m", "米", "m"); - var kg = _ItemBean("质量", "M", "1Kg", "千克", "Kg"); - var s = _ItemBean("时间", "T", "1s", "秒", "s"); - var a = _ItemBean("安培", "Ι", "1A", "安培", "A"); - var k = _ItemBean("热力学温度", "θ", "1K", "开尔文", "K"); - var mol = _ItemBean("物质的量", "N", "1mol", "摩尔", "mol"); - var cd = _ItemBean("发光强度", "J", "1cd", "坎德拉", "cd"); - - var data = <_ItemBean>[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: 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/lib/views/widgets/StatefulWidget/TextButton/node1_base.dart b/lib/views/widgets/StatefulWidget/TextButton/node1_base.dart deleted file mode 100644 index 0a07887e3..000000000 --- a/lib/views/widgets/StatefulWidget/TextButton/node1_base.dart +++ /dev/null @@ -1,43 +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 { - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - height: 60, - child: Wrap( - spacing: 20, - children: [ - TextButton( - child: Text('TextButton 文字'), - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - TextButton( - child: Text('TextButton 禁用'), - onPressed: null, - onLongPress: null, - ), - ], - )); - } - - _onPressed() {} - - _onLongPress() {} -} diff --git a/lib/views/widgets/StatefulWidget/TextButton/node2_style.dart b/lib/views/widgets/StatefulWidget/TextButton/node2_style.dart deleted file mode 100644 index 8d3fe3f28..000000000 --- a/lib/views/widgets/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 { - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - child: Wrap( - spacing: 10, - children: [ - TextButton( - style: TextButton.styleFrom( - backgroundColor: Colors.blue, - padding: EdgeInsets.symmetric(horizontal: 8), - primary: Colors.white, - elevation: 2, - shadowColor: Colors.orangeAccent), - child: Text('TextButton 样式'), - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - TextButton( - style: TextButton.styleFrom( - backgroundColor: Colors.white, - primary: Colors.black, - side: BorderSide(color: Colors.blue,width: 1), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10)) - ), - // elevation: 2, - shadowColor: Colors.orangeAccent), - child: Text('TextButton 边线'), - autofocus: false, - onPressed: _onPressed, - onLongPress: _onLongPress, - ), - ], - ), - ); - } - - _onPressed() {} - - _onLongPress() {} -} diff --git a/lib/views/widgets/StatefulWidget/TextField/node1_base.dart b/lib/views/widgets/StatefulWidget/TextField/node1_base.dart deleted file mode 100644 index ae1493406..000000000 --- a/lib/views/widgets/StatefulWidget/TextField/node1_base.dart +++ /dev/null @@ -1,63 +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 { - @override - _CustomTextFieldState createState() => _CustomTextFieldState(); -} - -class _CustomTextFieldState extends State { - final FocusNode _focusNode = FocusNode(); - TextEditingController _controller; - - void initState() { - super.initState(); - _controller = TextEditingController(); - } - - @override - void dispose() { - _controller.dispose(); - _focusNode.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Container( - width: 300, - child: TextField( - controller: _controller, - style: TextStyle(color: Colors.blue), - decoration: 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/lib/views/widgets/StatefulWidget/TextField/node2_cursor.dart b/lib/views/widgets/StatefulWidget/TextField/node2_cursor.dart deleted file mode 100644 index 3e75a79f4..000000000 --- a/lib/views/widgets/StatefulWidget/TextField/node2_cursor.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": 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 { - @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), - ], - ); - } - - Container _buildTextField(BuildContext context) { - return Container( - width: 300, - child: TextField( - style: TextStyle(color: Colors.blue), - minLines: 3, - maxLines: 5, - cursorColor: Colors.green, - cursorRadius: Radius.circular(3), - cursorWidth: 5, - showCursor: true, - decoration: InputDecoration( - contentPadding: EdgeInsets.all(10), - hintText: "请输入...", - border: OutlineInputBorder(), - ), - onChanged: (v) {}, - ), - ); - } - - _buildSubmitBtn() => FlatButton( - color: Colors.blue, - child: Text( - "提交", - style: TextStyle(color: Colors.white, fontSize: 16), - ), - onPressed: () => FocusScope.of(context).requestFocus(_focusNode)); -} diff --git a/lib/views/widgets/StatefulWidget/TextField/node3_decoration.dart b/lib/views/widgets/StatefulWidget/TextField/node3_decoration.dart deleted file mode 100644 index efe48adc9..000000000 --- a/lib/views/widgets/StatefulWidget/TextField/node3_decoration.dart +++ /dev/null @@ -1,66 +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 { - @override - Widget build(BuildContext context) { - return 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/lib/views/widgets/StatefulWidget/TextFormField/node1_base.dart b/lib/views/widgets/StatefulWidget/TextFormField/node1_base.dart deleted file mode 100644 index 2605f836f..000000000 --- a/lib/views/widgets/StatefulWidget/TextFormField/node1_base.dart +++ /dev/null @@ -1,80 +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 { - @override - _CustomTextFormFieldState createState() => _CustomTextFormFieldState(); -} - -class _CustomTextFormFieldState extends State { - GlobalKey _formKey = GlobalKey(); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - SizedBox(width: 40), - Expanded( - child: TextFormField( - style: TextStyle(textBaseline: TextBaseline.alphabetic), - decoration: InputDecoration( - border: OutlineInputBorder(), - labelText: 'username', - ), - validator: _validateUsername, - onFieldSubmitted: _onFieldSubmitted, - onSaved: _onSaved, - ), - ), - _buildSubmitButton(context), - ], - ); - } - - String _validateUsername(value) { - if (value.isEmpty) { - return '用户名不能为空'; - } - return null; - } - _onSaved(value){ - print('onSaved:'+value); - } - - void _onFieldSubmitted(value) { - print('onFieldSubmitted:'+value); - } - - RaisedButton _buildSubmitButton(BuildContext context) { - return RaisedButton( - color: Colors.blue, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onPressed: _onSubmit, - child: Icon( - Icons.check, - color: Colors.white, - ), - ); - } - - _onSubmit(){ - _formKey.currentState.save(); - if (_formKey.currentState.validate()) { - FocusScope.of(context).requestFocus(FocusNode()); - } - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatefulWidget/Tooltip/node1_base.dart b/lib/views/widgets/StatefulWidget/Tooltip/node1_base.dart deleted file mode 100644 index 7b37676f2..000000000 --- a/lib/views/widgets/StatefulWidget/Tooltip/node1_base.dart +++ /dev/null @@ -1,31 +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 { - @override - Widget build(BuildContext context) { - return 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/lib/views/widgets/StatefulWidget/Tooltip/node2_decoration.dart b/lib/views/widgets/StatefulWidget/Tooltip/node2_decoration.dart deleted file mode 100644 index 861890eda..000000000 --- a/lib/views/widgets/StatefulWidget/Tooltip/node2_decoration.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": 2, -// "subtitle": -// "【decoration】 : 装饰对象 【Decoration】\n" -// "【textStyle】 : 文字样式 【double】", -// } -class DecorationTooltip extends StatelessWidget { - @override - Widget build(BuildContext context) { - return 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/lib/views/widgets/StatefulWidget/TweenAnimationBuilder/node1_base.dart b/lib/views/widgets/StatefulWidget/TweenAnimationBuilder/node1_base.dart deleted file mode 100644 index 38f8c8b3b..000000000 --- a/lib/views/widgets/StatefulWidget/TweenAnimationBuilder/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 -/// 说明: 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 { - @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: 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: Icon( - Icons.android_outlined, - color: Colors.white, - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/UniqueWidget/node1_base.dart b/lib/views/widgets/StatefulWidget/UniqueWidget/node1_base.dart deleted file mode 100644 index 118ba456b..000000000 --- a/lib/views/widgets/StatefulWidget/UniqueWidget/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 -/// 说明: 243 UniqueWidget 唯一组件 -/// 抽象类,必须提供一个 GlobalKey 进行身份标识,该类型组件只会 inflated 一个实例,同一时刻也只会有一个状态,可以通过 currentState 属性获取状态。 -/// -// { -// "widgetId": 243, -// "name": 'UniqueWidget 介绍', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】", -// } - -class UniqueWidgetDemo extends StatelessWidget { - 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: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/ValueListenableBuilder/node1_base.dart b/lib/views/widgets/StatefulWidget/ValueListenableBuilder/node1_base.dart deleted file mode 100644 index b60bade66..000000000 --- a/lib/views/widgets/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 Container( - height: 200, - child: Scaffold( - appBar: AppBar(title: Text("ValueListenableBuilder")), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('You have pushed the button this many times:'), - ValueListenableBuilder( - builder: _buildWithValue, - valueListenable: _counter, - child: const Text('I am Child!'), - ) - ], - ), - ), - floatingActionButton: FloatingActionButton( - child: 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, - ], - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/WidgetInspector/node1_base.dart b/lib/views/widgets/StatefulWidget/WidgetInspector/node1_base.dart deleted file mode 100644 index 100a49504..000000000 --- a/lib/views/widgets/StatefulWidget/WidgetInspector/node1_base.dart +++ /dev/null @@ -1,60 +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 { - - @override - Widget build(BuildContext context) { - return Container( - height: 200, - child: WidgetInspector( - child: HomePage(), - selectButtonBuilder: _selectButtonBuilder, - ), - ); - } - - Widget _selectButtonBuilder(BuildContext context, onPressed) { - onPressed(); - return Container(); - } -} - -class HomePage extends StatefulWidget { - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State { - var _count = 0; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - alignment: Alignment(0, 0.7), - child: Text( - '你点击了$_count次', - style: TextStyle(fontSize: 18, color: Colors.blue), - ), - ), - floatingActionButton: FloatingActionButton( - child: Icon(Icons.add), - onPressed: () { - setState(() { - _count++; - }); - }, - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/WidgetsApp/node1_base.dart b/lib/views/widgets/StatefulWidget/WidgetsApp/node1_base.dart deleted file mode 100644 index 3dee921ee..000000000 --- a/lib/views/widgets/StatefulWidget/WidgetsApp/node1_base.dart +++ /dev/null @@ -1,128 +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 { - @override - _WidgetsAppDemoState createState() => _WidgetsAppDemoState(); -} - -class _WidgetsAppDemoState extends State { - var _debugShowCheckedModeBanner = false; - var _debugShowWidgetInspector = false; - var _showPerformanceOverlay = false; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildSwitchers(), - Container( - 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: HomePage(), - ), - ), - ], - ); - } - - Widget _buildSwitchers() { - return DefaultTextStyle( - style: TextStyle(color: Colors.blue), - child: Wrap( - spacing: 10, - children: [ - Column( - children: [ - Switch( - value: _showPerformanceOverlay, - onChanged: (v) { - setState(() { - _showPerformanceOverlay = v; - }); - }, - ), - Text('性能浮层') - ], - ), - Column( - children: [ - Switch( - value: _debugShowCheckedModeBanner, - onChanged: (v) { - setState(() { - _debugShowCheckedModeBanner = v; - }); - }, - ), - Text('开启角标') - ], - ), - Column( - children: [ - Switch( - value: _debugShowWidgetInspector, - onChanged: (v) { - setState(() { - _debugShowWidgetInspector = v; - }); - }, - ), - Text('检查器') - ], - ) - ], - ), - ); - } -} - -class HomePage extends StatefulWidget { - @override - _HomePageState createState() => _HomePageState(); -} - -class _HomePageState extends State { - var _count = 0; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Container( - alignment: Alignment(0, 0.7), - child: Text( - '你点击了$_count次', - style: TextStyle(fontSize: 18, color: Colors.blue), - ), - ), - floatingActionButton: FloatingActionButton( - child: Icon(Icons.add), - onPressed: () { - setState(() { - _count++; - }); - }, - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/WillPopScope/node1_base.dart b/lib/views/widgets/StatefulWidget/WillPopScope/node1_base.dart deleted file mode 100644 index 4c1f9ecb0..000000000 --- a/lib/views/widgets/StatefulWidget/WillPopScope/node1_base.dart +++ /dev/null @@ -1,48 +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 { - @override - Widget build(BuildContext context) { - return Container( - child: WillPopScope(child: (BackButton()), - onWillPop: ()=>_willPop(context)), - ); - } - - Future _willPop(context) async{ - return await showDialog( - context: context, - builder: (context) => AlertDialog( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - title: Text('提示'), - content: Text('你确定要离开此页吗?'), - actions: [ - FlatButton( - onPressed: () => Navigator.of(context).pop(true), - child: Text('确定'), - ), - FlatButton( - onPressed: () => Navigator.of(context).pop(false), - child: Text('取消'), - ), - - ], - ), - ) ?? false; - - } -} diff --git a/lib/views/widgets/StatefulWidget/YearPicker/node1_base.dart b/lib/views/widgets/StatefulWidget/YearPicker/node1_base.dart deleted file mode 100644 index 2d7dee65a..000000000 --- a/lib/views/widgets/StatefulWidget/YearPicker/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": 136, -// "name": 'YearPicker基本使用', -// "priority": 1, -// "subtitle": -// "【selectedDate】 : 选中日期 【DateTime】\n" -// "【firstDate】 : 最前日期限制 【DateTime】\n" -// "【lastDate】 : 最后日期限制 【DateTime】\n" -// "【onChanged】 : 点击回调 【Function(DateTime)】", -// } -class CustomYearPicker extends StatefulWidget { - @override - _CustomYearPickerState createState() => _CustomYearPickerState(); -} - -class _CustomYearPickerState extends State { - DateTime _date = DateTime.now(); - - @override - Widget build(BuildContext context) { - return Container( - height:150, - child: YearPicker( - selectedDate: _date, - onChanged: (date) => setState(() => _date = date), - firstDate: DateTime(2018), - lastDate: DateTime(2030), - ), - ); - } -} diff --git a/lib/views/widgets/StatefulWidget/zz_no/Scrollable.dart b/lib/views/widgets/StatefulWidget/zz_no/Scrollable.dart deleted file mode 100644 index 330947971..000000000 --- a/lib/views/widgets/StatefulWidget/zz_no/Scrollable.dart +++ /dev/null @@ -1,58 +0,0 @@ -//import 'package:flutter/material.dart'; -// -///// create by 张风捷特烈 on 2020-03-28 -///// contact me by email 1981462002@qq.com -///// 说明: -// -//class CustomScrollable extends StatelessWidget { -// final 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 Container( -// height: 200, -// child: Scrollable( -// viewportBuilder: (ctx, p) => Viewport(offset: p, -// slivers: [ -// -// ], -// ), -// ), -// ); -// } -// -// Column buildColumn() { -// return Column( -// children: data -// .map((color) => Container( -// alignment: Alignment.center, -// height: 50, -// color: color, -// child: Text( -// colorString(color), -// style: 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()}"; -//} diff --git a/lib/views/widgets/StatelessWidget/AboutDialog/node1_base.dart b/lib/views/widgets/StatelessWidget/AboutDialog/node1_base.dart deleted file mode 100644 index 4d717abf7..000000000 --- a/lib/views/widgets/StatelessWidget/AboutDialog/node1_base.dart +++ /dev/null @@ -1,69 +0,0 @@ -/// 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】", -// } - -import 'package:flutter/material.dart'; - -class CustomAboutDialog extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Stack( - children: [ - _buildAboutDialog(), - Positioned(top: 50, right: 20, child: _buildRaisedButton(context)), - ], - ); - } - - Widget _buildRaisedButton(BuildContext context) => RaisedButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - color: Colors.blue, - onPressed: () { - showDialog(context: context, builder: (ctx) => _buildAboutDialog()); - }, - child: Text( - 'Just Show It', - style: TextStyle(color: Colors.white), - ), - ); - - AboutDialog _buildAboutDialog() { - return AboutDialog( - applicationIcon: FlutterLogo(), - applicationVersion: 'v0.0.1', - applicationName: 'Flutter Unit', - applicationLegalese: 'Copyright© 2018-2020 张风捷特烈', - children: [ - Container( - margin: EdgeInsets.only(top: 20), - width: 80, - height: 80, - child: Image.asset('assets/images/icon_head.webp')), - Container( - margin: EdgeInsets.only(top: 10), - alignment: Alignment.center, - child: 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/lib/views/widgets/StatelessWidget/AboutListTile/node1_base.dart b/lib/views/widgets/StatelessWidget/AboutListTile/node1_base.dart deleted file mode 100644 index cd908a7fb..000000000 --- a/lib/views/widgets/StatelessWidget/AboutListTile/node1_base.dart +++ /dev/null @@ -1,45 +0,0 @@ - -/// 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】", -// } - -import 'package:flutter/material.dart'; - -class AboutListTileDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return AboutListTile( - icon: Icon(Icons.info), - applicationIcon: FlutterLogo(), - applicationName: 'Flutter Unit', - applicationVersion: 'v0.0.1', - applicationLegalese: 'Copyright© 2018-2020 张风捷特烈', - aboutBoxChildren: [ - Padding( - padding: const 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/lib/views/widgets/StatelessWidget/ActionChip/node1_base.dart b/lib/views/widgets/StatelessWidget/ActionChip/node1_base.dart deleted file mode 100644 index 9366799da..000000000 --- a/lib/views/widgets/StatelessWidget/ActionChip/node1_base.dart +++ /dev/null @@ -1,36 +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 '../../../components/project/dialogs/dialog_about.dart'; - - -class CustomActionChip extends StatelessWidget { - @override - Widget build(BuildContext context) { - return ActionChip( - padding: EdgeInsets.all(5), - labelPadding: EdgeInsets.all(3), - label: 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/lib/views/widgets/StatelessWidget/AlertDialog/node1_base.dart b/lib/views/widgets/StatelessWidget/AlertDialog/node1_base.dart deleted file mode 100644 index 347738293..000000000 --- a/lib/views/widgets/StatelessWidget/AlertDialog/node1_base.dart +++ /dev/null @@ -1,111 +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】 : 右下角组件列表 【背景色】\n" -// "【elevation】 : 右下角组件列表 【背景色】\n" -// "【shape】 : 影深 【double】", -// } - -import 'package:flutter/material.dart'; - -class CustomAlertDialog extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildRaisedButton(context), - _buildAlertDialog(), - ], - ); - } - - Widget _buildRaisedButton(BuildContext context) => RaisedButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - color: Colors.blue, - onPressed: () { - showDialog(context: context, builder: (ctx) => _buildAlertDialog()); - }, - child: Text( - 'Just Show It !', - style: TextStyle(color: Colors.white), - ), - ); - - Widget _buildAlertDialog() { - return AlertDialog( - title: _buildTitle(), - titleTextStyle: TextStyle(fontSize: 20, color: Colors.black), - titlePadding: EdgeInsets.only( - top: 5, - left: 20, - ), - contentPadding: EdgeInsets.symmetric(horizontal: 5), - backgroundColor: Colors.white, - content: _buildContent(), - actions: [ - 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: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - ); - } - - Widget _buildTitle() { - return Row( - //标题 - children: [ - Image.asset( - "assets/images/icon_head.webp", - width: 30, - height: 30, - ), - SizedBox(width: 10,), - Expanded( - child: Text( - "关于", - style: TextStyle(fontSize: 18), - )), - CloseButton() - ], - ); - } - - Widget _buildContent() { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const 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/lib/views/widgets/StatelessWidget/AnimatedIcon/node1_base.dart b/lib/views/widgets/StatelessWidget/AnimatedIcon/node1_base.dart deleted file mode 100644 index db924a964..000000000 --- a/lib/views/widgets/StatelessWidget/AnimatedIcon/node1_base.dart +++ /dev/null @@ -1,70 +0,0 @@ -/// 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】", -// } - - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -class CustomAnimatedIcon extends StatefulWidget { - @override - _CustomAnimatedIconState createState() => _CustomAnimatedIconState(); -} - -class _CustomAnimatedIconState extends State - with SingleTickerProviderStateMixin { - AnimationController _ctrl; - - @override - void initState() { - _ctrl = AnimationController(vsync: this, duration: 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 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((e) => AnimatedIcon( - size: 50, - color: e, - icon: data[e], - progress: _ctrl, - )) - .toList(); -} diff --git a/lib/views/widgets/StatelessWidget/BackButton/node1_base.dart b/lib/views/widgets/StatelessWidget/BackButton/node1_base.dart deleted file mode 100755 index 5339c7c69..000000000 --- a/lib/views/widgets/StatelessWidget/BackButton/node1_base.dart +++ /dev/null @@ -1,27 +0,0 @@ -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: - -// { -// "widgetId": 31, -// "priority": 1, -// "name": "BackButton属性", -// "subtitle": "【color】: 颜色 【Color】\n" -// "【onPressed】: 点击事件 【Function】\n" -// " onPressed为空会退出当前栈", -// } - -import 'package:flutter/material.dart'; - -class CustomBackButton extends StatelessWidget { - @override - Widget build(BuildContext context) { - var data = [Colors.red,Colors.yellow,Colors.blue,Colors.green]; - return Wrap( - spacing: 10, - children: data.map((e)=>BackButton( - color: e, - )).toList() - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/Banner/node1_base.dart b/lib/views/widgets/StatelessWidget/Banner/node1_base.dart deleted file mode 100644 index 5fff743a3..000000000 --- a/lib/views/widgets/StatelessWidget/Banner/node1_base.dart +++ /dev/null @@ -1,46 +0,0 @@ -/// 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】", -// } - -import 'package:flutter/material.dart'; - -class CustomBanner extends StatelessWidget { - @override - Widget build(BuildContext context) { - var data = { - BannerLocation.topStart: Colors.red, - BannerLocation.topEnd: Colors.blue, - BannerLocation.bottomStart: Colors.green, - BannerLocation.bottomEnd: Colors.yellow, - }; - - return Wrap( - spacing: 10, - runSpacing: 10, - children: data.keys.map((e) => - Container( - color: Color(0xffD8F5FF), - width: 150, - height: 150 * 0.618, - child: Banner( - message: "Flutter 1.12发布", - location: e, - color: data[e], - child: Padding( - padding: EdgeInsets.all(20), - child: FlutterLogo(textColor: Colors.blue, - style: FlutterLogoStyle.horizontal,)), - ), - )).toList()); - } -} diff --git a/lib/views/widgets/StatelessWidget/BottomSheet/node1_base.dart b/lib/views/widgets/StatelessWidget/BottomSheet/node1_base.dart deleted file mode 100644 index eb7a7741e..000000000 --- a/lib/views/widgets/StatelessWidget/BottomSheet/node1_base.dart +++ /dev/null @@ -1,68 +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 { - @override - _CustomBottomSheetState createState() => _CustomBottomSheetState(); -} - -class _CustomBottomSheetState extends State { - bool opened = false; - - @override - Widget build(BuildContext context) { - return Container( - child: FlatButton( - color: Colors.blue, - onPressed: () { - opened = !opened; - opened - ? Scaffold.of(context) - .showBottomSheet((_) => _buildBottomSheet()) - : Navigator.of(context).pop(); - }, - child: Text( - '点我显隐BottomSheet', - style: TextStyle(color: Colors.white), - ))); - } - - Widget _buildBottomSheet() => BottomSheet( - enableDrag: true, - elevation: 4, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topRight: Radius.circular(60), - topLeft: Radius.circular(60), - ) - ), - backgroundColor: Colors.transparent, - onClosing: () => print('onClosing'), - builder: (_) => (Container( - height: 250, - decoration: 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/lib/views/widgets/StatelessWidget/BoxScrollView/node1_base.dart b/lib/views/widgets/StatelessWidget/BoxScrollView/node1_base.dart deleted file mode 100644 index 11705462b..000000000 --- a/lib/views/widgets/StatelessWidget/BoxScrollView/node1_base.dart +++ /dev/null @@ -1,86 +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 { - - final String info = - 'BoxScrollView 是 ScrollView 的子类,实现了它的抽象方法,且暴露出另一个抽象方法 buildChildLayout,返回 Sliver 家族 Widget,' - '其子类有 ListView 和 GridView,分别使用 Sliver 家族相关List、Gird列表组件实现的。'; - - @override - Widget build(BuildContext context) { - return Container( - height: 300, - child: Column( - children: [ - Container( - color: Colors.blue.withOpacity(0.1), - padding: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info), - ), - Expanded(child: MyBoxScrollView()), - ], - ), - ); - } -} - -class MyBoxScrollView extends BoxScrollView { - - 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: TextStyle(color: Colors.white, shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2) - ]), - ), - ), - childCount: data.length), - ); -} diff --git a/lib/views/widgets/StatelessWidget/Builder/node1_base.dart b/lib/views/widgets/StatelessWidget/Builder/node1_base.dart deleted file mode 100644 index 11fe0212e..000000000 --- a/lib/views/widgets/StatelessWidget/Builder/node1_base.dart +++ /dev/null @@ -1,38 +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 { - @override - Widget build(BuildContext context) { - return Container( - height: 200, - child: Scaffold( - appBar: AppBar( - title: Text('Builder'), - ), - floatingActionButton: Builder( - builder: (ctx) => FloatingActionButton( - onPressed: () { - Scaffold.of(ctx) - .showSnackBar(SnackBar(content: Text('hello builder'))); - }, - child: Icon(Icons.add), - ), - ), - ), - ); - } - - -} diff --git a/lib/views/widgets/StatelessWidget/ButtonBar/node1_base.dart b/lib/views/widgets/StatelessWidget/ButtonBar/node1_base.dart deleted file mode 100755 index 0dd4a36e1..000000000 --- a/lib/views/widgets/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 '../../../components/project/dialogs/dialog_about.dart'; - - -class CustomButtonBar extends StatelessWidget { - @override - Widget build(BuildContext context) { - return ButtonBar( - alignment: MainAxisAlignment.center, - children: [ - RaisedButton( - color: Colors.blue, - child: Text("Raised"), - onPressed: () => DialogAbout.show(context)), - OutlineButton( - child: Text("Outline"), - onPressed: () => DialogAbout.show(context)), - FlatButton( - color: Colors.blue, - onPressed: () => DialogAbout.show(context), - child: Text("Flat"), - ) - ], - ); - } -} - diff --git a/lib/views/widgets/StatelessWidget/ButtonBar/node2_padding.dart b/lib/views/widgets/StatelessWidget/ButtonBar/node2_padding.dart deleted file mode 100644 index 0d2486213..000000000 --- a/lib/views/widgets/StatelessWidget/ButtonBar/node2_padding.dart +++ /dev/null @@ -1,38 +0,0 @@ - -import 'package:flutter/material.dart'; -import '../../../components/project/dialogs/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 { - @override - Widget build(BuildContext context) { - return ButtonBar( - alignment: MainAxisAlignment.center, - buttonHeight: 40, - buttonPadding: EdgeInsets.only(left: 15,right: 15), - children: [ - RaisedButton( - color: Colors.blue, - child: Text("Raised"), - onPressed: () => DialogAbout.show(context)), - OutlineButton( - child: Text("Outline"), - onPressed: () => DialogAbout.show(context)), - FlatButton( - color: Colors.blue, - onPressed: () => DialogAbout.show(context), - child: Text("Flat"), - ) - ], - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/Card/node1_base.dart b/lib/views/widgets/StatelessWidget/Card/node1_base.dart deleted file mode 100644 index fca2c9174..000000000 --- a/lib/views/widgets/StatelessWidget/Card/node1_base.dart +++ /dev/null @@ -1,34 +0,0 @@ - - -/// 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】", -// } -import 'package:flutter/material.dart'; - -class CustomCard extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Card( - color: Color(0xffB3FE65), - elevation: 4, - margin: EdgeInsets.all(10), - child: Container( - alignment: Alignment.topLeft, - width: 200, - height: 0.618*200, - margin: EdgeInsets.all(10), - child: Text("Card", style: TextStyle(fontSize: 20)), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/Card/node2_shape.dart b/lib/views/widgets/StatelessWidget/Card/node2_shape.dart deleted file mode 100644 index 3f5f6226f..000000000 --- a/lib/views/widgets/StatelessWidget/Card/node2_shape.dart +++ /dev/null @@ -1,57 +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 '../../../../app/utils/pather.dart'; - -class ShapeCard extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Card( - color: Color(0xffB3FE65), - elevation: 6, - shape: StarShapeBorder(), - child: Container( - alignment: Alignment.center, - width: 100, - height: 100, - child: Text("Card", style: TextStyle(fontSize: 20)), - ), - ); - } -} - -class StarShapeBorder extends ShapeBorder { - @override - EdgeInsetsGeometry get dimensions => null; - - @override - Path getInnerPath(Rect rect, {TextDirection textDirection}) { - return null; - } - - @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 null; - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/CheckboxListTile/node1_base.dart b/lib/views/widgets/StatelessWidget/CheckboxListTile/node1_base.dart deleted file mode 100644 index b644f1c0a..000000000 --- a/lib/views/widgets/StatelessWidget/CheckboxListTile/node1_base.dart +++ /dev/null @@ -1,43 +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 { - @override - _CustomCheckBoxListTileState createState() => _CustomCheckBoxListTileState(); -} - -class _CustomCheckBoxListTileState extends State { - var _selected = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: 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: Text("张风捷特烈"), - subtitle: Text("@万花过尽知无物"), - onChanged: (v) => setState(() => _selected = !_selected), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/CheckboxListTile/node2_select.dart b/lib/views/widgets/StatelessWidget/CheckboxListTile/node2_select.dart deleted file mode 100644 index dcc38d890..000000000 --- a/lib/views/widgets/StatelessWidget/CheckboxListTile/node2_select.dart +++ /dev/null @@ -1,39 +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 { - @override - _SelectCheckBoxListTileState createState() => _SelectCheckBoxListTileState(); -} - -class _SelectCheckBoxListTileState extends State { - var _selected = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: 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: Text("张风捷特烈"), - subtitle: Text("@万花过尽知无物"), - onChanged: (v) => setState(() => _selected = !_selected), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/CheckboxListTile/node3_dense.dart b/lib/views/widgets/StatelessWidget/CheckboxListTile/node3_dense.dart deleted file mode 100644 index 81c988811..000000000 --- a/lib/views/widgets/StatelessWidget/CheckboxListTile/node3_dense.dart +++ /dev/null @@ -1,39 +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 { - @override - _DenseCheckBoxListTileState createState() => _DenseCheckBoxListTileState(); -} - -class _DenseCheckBoxListTileState extends State { - var _selected = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: 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: Text("张风捷特烈"), - subtitle: Text("@万花过尽知无物"), - onChanged: (v) => setState(() => _selected = !_selected), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/CheckedModeBanner/node1_base.dart b/lib/views/widgets/StatelessWidget/CheckedModeBanner/node1_base.dart deleted file mode 100644 index c42a5c8e1..000000000 --- a/lib/views/widgets/StatelessWidget/CheckedModeBanner/node1_base.dart +++ /dev/null @@ -1,31 +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 -/// 说明: 215 CheckedModeBanner 仅在debug运行模式中显示右上角角标,没什么太大卵用。在 MaterialApp 组件源码中有使用场景。 -// { -// "widgetId": 215, -// "name": 'CheckedModeBanner基本使用', -// "priority": 1, -// "subtitle": -// "【child】 : 组件 【Widget】", -// } - -class CheckedModeBannerDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return CheckedModeBanner( - child: Container( - alignment: Alignment.center, - width: 250, - height: 150, - color: Theme.of(context).primaryColor, - child: Text( - "CheckedModeBanner", - style: TextStyle(color: Colors.white, fontSize: 20), - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/Chip/node1_base.dart b/lib/views/widgets/StatelessWidget/Chip/node1_base.dart deleted file mode 100644 index 7c829f54e..000000000 --- a/lib/views/widgets/StatelessWidget/Chip/node1_base.dart +++ /dev/null @@ -1,41 +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 { - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [ - Chip( - avatar: Image.asset("assets/images/icon_head.webp"), - label: Text("张风捷特烈"), - padding: EdgeInsets.all(5), - labelPadding: EdgeInsets.all(5), - ), - 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/lib/views/widgets/StatelessWidget/Chip/node2_color.dart b/lib/views/widgets/StatelessWidget/Chip/node2_color.dart deleted file mode 100644 index edbc8cae2..000000000 --- a/lib/views/widgets/StatelessWidget/Chip/node2_color.dart +++ /dev/null @@ -1,43 +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 { - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [ - Chip( - avatar: Image.asset("assets/images/icon_head.webp"), - label: Text("张风捷特烈"), - padding: EdgeInsets.all(5), - labelPadding: EdgeInsets.all(5), - backgroundColor: Colors.grey.withAlpha(66), - shadowColor: Colors.orangeAccent, - elevation: 3, - ), - Chip( - avatar: Image.asset("assets/images/icon_head.webp"), - label: Text("张风捷特烈"), - padding: EdgeInsets.all(5), - labelPadding: EdgeInsets.all(5), - backgroundColor: Colors.cyanAccent.withAlpha(11), - shadowColor: Colors.blue.withAlpha(88), - elevation: 4, - ), - ], - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/Chip/node3_delete.dart b/lib/views/widgets/StatelessWidget/Chip/node3_delete.dart deleted file mode 100644 index 0f5a2ddac..000000000 --- a/lib/views/widgets/StatelessWidget/Chip/node3_delete.dart +++ /dev/null @@ -1,34 +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 '../../../components/project/dialogs/dialog_about.dart'; - -class DeleteOfChip extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Chip( - avatar: Image.asset("assets/images/icon_head.webp"), - label: Text("张风捷特烈"), - padding: EdgeInsets.all(5), - labelPadding: 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/lib/views/widgets/StatelessWidget/ChoiceChip/node1_base.dart b/lib/views/widgets/StatelessWidget/ChoiceChip/node1_base.dart deleted file mode 100644 index 5c548cf02..000000000 --- a/lib/views/widgets/StatelessWidget/ChoiceChip/node1_base.dart +++ /dev/null @@ -1,46 +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 { - @override - _CustomChoiceChipState createState() => _CustomChoiceChipState(); -} - -class _CustomChoiceChipState extends State { - bool _select = false; - - @override - Widget build(BuildContext context) { - return ChoiceChip( - selected: _select, - padding: EdgeInsets.all(5), - labelPadding: EdgeInsets.all(5), - label: Text( - _select ? - "You are selected it." : - "This is a ChoiceChip.", - style: 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/lib/views/widgets/StatelessWidget/CircleAvatar/node1_base.dart b/lib/views/widgets/StatelessWidget/CircleAvatar/node1_base.dart deleted file mode 100644 index f1fbd0d88..000000000 --- a/lib/views/widgets/StatelessWidget/CircleAvatar/node1_base.dart +++ /dev/null @@ -1,32 +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 { - @override - Widget build(BuildContext context) { - return CircleAvatar( - radius: 50, - backgroundImage: AssetImage("assets/images/wy_200x300.webp"), - foregroundColor: Colors.white, - child: Icon( - Icons.check, - size: 50, - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/CloseButton/node1_base.dart b/lib/views/widgets/StatelessWidget/CloseButton/node1_base.dart deleted file mode 100755 index b66b27827..000000000 --- a/lib/views/widgets/StatelessWidget/CloseButton/node1_base.dart +++ /dev/null @@ -1,18 +0,0 @@ -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 32, -// "priority": 1, -// "name": "CloseButton点击事件", -// "subtitle": " 点击时会退出当前栈", -// } - -import 'package:flutter/material.dart'; - -class CustomCloseButton extends StatelessWidget { - @override - Widget build(BuildContext context) { - return CloseButton(); - } -} diff --git a/lib/views/widgets/StatelessWidget/Container/node1_base.dart b/lib/views/widgets/StatelessWidget/Container/node1_base.dart deleted file mode 100644 index d7168da41..000000000 --- a/lib/views/widgets/StatelessWidget/Container/node1_base.dart +++ /dev/null @@ -1,25 +0,0 @@ - -/// 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】", -// } -import 'package:flutter/material.dart'; - -class CustomContainer extends StatelessWidget { - @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/lib/views/widgets/StatelessWidget/Container/node2_child.dart b/lib/views/widgets/StatelessWidget/Container/node2_child.dart deleted file mode 100644 index b7c78c602..000000000 --- a/lib/views/widgets/StatelessWidget/Container/node2_child.dart +++ /dev/null @@ -1,28 +0,0 @@ - -/// 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】", -// } -import 'package:flutter/material.dart'; - -class ContainerWithChild extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.topLeft, - padding: EdgeInsets.all(20), - margin: EdgeInsets.all(10), - width: 200, - height: 200 * 0.618, - color: Colors.grey.withAlpha(88), - child: Icon(Icons.android), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/Container/node3_alignment.dart b/lib/views/widgets/StatelessWidget/Container/node3_alignment.dart deleted file mode 100644 index 45d690f54..000000000 --- a/lib/views/widgets/StatelessWidget/Container/node3_alignment.dart +++ /dev/null @@ -1,27 +0,0 @@ - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 1, -// "name": '可对子组件进行对齐定位', -// "priority": 3, -// "subtitle": "【alignment】 : 对齐定位 【AlignmentGeometry】", -// } -import 'package:flutter/material.dart'; - -class ContainerAlignment extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.bottomRight, - width: 200, - height: 200 * 0.618, - color: Colors.grey.withAlpha(88), - child: Icon( - Icons.android, - color: Colors.green, - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/Container/node4_decoration.dart b/lib/views/widgets/StatelessWidget/Container/node4_decoration.dart deleted file mode 100644 index 15423df5f..000000000 --- a/lib/views/widgets/StatelessWidget/Container/node4_decoration.dart +++ /dev/null @@ -1,56 +0,0 @@ - -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 1, -// "name": '可对子组件进行装饰', -// "priority": 4, -// "subtitle": -// "【decoration】 : 装饰 【Decoration】\n " -// "可装饰: 边线、圆弧、颜色、渐变色、阴影、图片等内容", -// } -import 'package:flutter/material.dart'; - -class ContainerDecoration extends StatelessWidget { - - 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: EdgeInsets.all(20), - padding: EdgeInsets.all(20), - decoration: BoxDecoration(//添加渐变色 - gradient: LinearGradient( - stops: stops, - colors: rainbow.map((e) => Color(e)).toList()), - borderRadius: BorderRadius.only( - topLeft: Radius.circular(50), - bottomRight: Radius.circular(50)), - boxShadow: [ - BoxShadow( - color: Colors.grey, - offset: Offset(1, 1), - blurRadius: 10, - spreadRadius: 1), - ]), - child: Text( - "Container", - style: TextStyle(fontSize: 20), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/Container/node5_transform.dart b/lib/views/widgets/StatelessWidget/Container/node5_transform.dart deleted file mode 100644 index 0d47737ad..000000000 --- a/lib/views/widgets/StatelessWidget/Container/node5_transform.dart +++ /dev/null @@ -1,31 +0,0 @@ -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 1, -// "name": 'Container还具有变换性', -// "priority": 5, -// "subtitle": "【transform】 : 变换矩阵 【Matrix4】\n " -// "基于Matrix4的矩阵变换,变换详情见线性代数", -// } - -import 'package:flutter/material.dart'; -import 'dart:math'; - -class ContainerTransform extends StatelessWidget { - @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: Text( - "Container", - style: TextStyle(fontSize: 20), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/Container/node6_constraints.dart b/lib/views/widgets/StatelessWidget/Container/node6_constraints.dart deleted file mode 100644 index 848262ed6..000000000 --- a/lib/views/widgets/StatelessWidget/Container/node6_constraints.dart +++ /dev/null @@ -1,28 +0,0 @@ -/// create by 张风捷特烈 on 2020/4/25 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 1, -// "name": 'Container的约束性', -// "priority": 6, -// "subtitle": -// "【constraints】 : 约束 【BoxConstraints】\n " -// "会约束该区域的尺寸,不会小于指定的最小宽高,也不会大于指定的最大宽高。", -// } - -import 'package:flutter/material.dart'; - -class ContainerConstraints extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - //容器 - color: Colors.blue, - width: 200, - height: 200 * 0.618, - constraints: BoxConstraints( - minWidth: 100, maxWidth: 150, - minHeight: 20, maxHeight: 100), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/CupertinoActionSheet/node1_base.dart b/lib/views/widgets/StatelessWidget/CupertinoActionSheet/node1_base.dart deleted file mode 100644 index a88cc1f55..000000000 --- a/lib/views/widgets/StatelessWidget/CupertinoActionSheet/node1_base.dart +++ /dev/null @@ -1,62 +0,0 @@ - - -/// 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】", -// } -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -class CustomCupertinoActionSheet extends StatelessWidget { - @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: Text("Please chose a language"), - message: Text('the language you use in this application.'), - cancelButton: CupertinoActionSheetAction( - onPressed: () => Navigator.pop(context), child: Text("Cancel")), - actions: [ - CupertinoActionSheetAction( - onPressed: () => Navigator.pop(context), child: Text('Dart')), - CupertinoActionSheetAction( - onPressed: () => Navigator.pop(context), child: Text('Java')), - CupertinoActionSheetAction( - onPressed: () => Navigator.pop(context), child: Text('Kotlin')), - ], - ), - ); - - Widget _buildRaisedButton(BuildContext context) => RaisedButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - color: Colors.blue, - onPressed: () => showDialog( - context: context, - builder: (ctx) => _buildCupertinoActionSheet(context)), - child: Text( - 'Just Show It !', - style: TextStyle(color: Colors.white), - ), - ); -} diff --git a/lib/views/widgets/StatelessWidget/CupertinoActionSheetAction/node1_base.dart b/lib/views/widgets/StatelessWidget/CupertinoActionSheetAction/node1_base.dart deleted file mode 100644 index 8c032b555..000000000 --- a/lib/views/widgets/StatelessWidget/CupertinoActionSheetAction/node1_base.dart +++ /dev/null @@ -1,42 +0,0 @@ -/// 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()】", -// } - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; -import '../../../components/project/dialogs/dialog_about.dart'; - -class CustomCupertinoActionSheetAction extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Column( - children: [ - Container( - margin: EdgeInsets.all(5), - color: Colors.grey.withAlpha(33), - child: CupertinoActionSheetAction( - isDefaultAction: true, - onPressed: () => DialogAbout.show(context), - child: Text('张风捷特烈')), - ), - Container( - color: Colors.grey.withAlpha(33), - margin: EdgeInsets.all(5), - child: CupertinoActionSheetAction( - isDefaultAction: false, - onPressed: () => DialogAbout.show(context), - child: Text('百里·巫缨')), - ), - ], - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/CupertinoAlertDialog/node1_base.dart b/lib/views/widgets/StatelessWidget/CupertinoAlertDialog/node1_base.dart deleted file mode 100644 index f4f9f3dcc..000000000 --- a/lib/views/widgets/StatelessWidget/CupertinoAlertDialog/node1_base.dart +++ /dev/null @@ -1,99 +0,0 @@ - -/// create by 张风捷特烈 on 2020-03-24 -/// contact me by email 1981462002@qq.com -/// 说明: -/// -// { -// "widgetId": 129, -// "name": 'CupertinoAlertDialog基本使用', -// "priority": 1, -// "subtitle": -// "【title】 : 顶部组件 【Widget】\n" -// "【content】 : 内容组件 【Widget】\n" -// "【actions】 : 顶部文字样式 【List】", -// } - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -class CustomCupertinoAlertDialog extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildRaisedButton(context), - _buildCupertinoAlertDialog(context), - ], - ); - } - - Widget _buildRaisedButton(BuildContext context) => RaisedButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - color: Colors.blue, - onPressed: () { - showDialog( - context: context, - builder: (ctx) => _buildCupertinoAlertDialog(context)); - }, - child: 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: Text("Yes, Delete"), - onPressed: () => Navigator.pop(context), - ), - CupertinoButton( - child: Text("Cancle"), - onPressed: () => Navigator.pop(context), - ), - ]), - ); - } - - Widget _buildTitle(context) { - return Row( - //标题 - children: [ - Icon( - CupertinoIcons.delete_solid, - color: Colors.red, - ), - Expanded( - child: Text( - 'Delete File', - style: TextStyle(color: Colors.red, fontSize: 20), - )), - InkWell( - child: Icon(CupertinoIcons.clear_thick), - onTap: () => Navigator.pop(context), - ) - ]); - } - - Widget _buildContent() { - return Padding( - padding: const EdgeInsets.only(top: 18.0), - child: Column( - children: [ - 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/lib/views/widgets/StatelessWidget/CupertinoDialogAction/no_node1_base.dart b/lib/views/widgets/StatelessWidget/CupertinoDialogAction/no_node1_base.dart deleted file mode 100644 index 44bd1535d..000000000 --- a/lib/views/widgets/StatelessWidget/CupertinoDialogAction/no_node1_base.dart +++ /dev/null @@ -1,128 +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 { - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildRaisedButton(context), - _buildDialog(), - ], - ); - } - - Widget _buildDialog() => CupertinoDialogAction( - onPressed: (){ - - }, - child: Container( - width: 50, - child: DeleteDialog(), - ), - ); - - Widget _buildRaisedButton(BuildContext context) => RaisedButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - color: Colors.blue, - onPressed: () { - showDialog(context: context, builder: (ctx) => _buildDialog()); - }, - child: Text( - 'Just Show It !', - style: TextStyle(color: Colors.white), - ), - - ); -} - -class DeleteDialog extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildBar(context), - _buildTitle(), - _buildContent(), - _buildFooter(context), - ], - ), - ); - } - - Widget _buildTitle() { - return Text( - 'Delete Doucument', - style: TextStyle(color: Color(0xff5CC5E9), fontSize: 24), - ); - } - - Widget _buildContent() { - return Padding( - padding: const 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: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(30)), - color: Color(0xff73D1EE)), - child: 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: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(30)), - color: Colors.orangeAccent), - child: Text('Cancle', - style: TextStyle(color: Colors.white, fontSize: 16)), - ), - ) - ], - ), - ); - } - - _buildBar(context) => Container( - height: 30, - alignment: Alignment.centerRight, - margin: EdgeInsets.only(right: 10, top: 5), - child: InkWell( - onTap: ()=>Navigator.of(context).pop(), - child: Icon( - Icons.close, - color: Color(0xff82CAE3), - ), - ), - ); -} diff --git a/lib/views/widgets/StatelessWidget/CupertinoDialogAction/node1_base.dart b/lib/views/widgets/StatelessWidget/CupertinoDialogAction/node1_base.dart deleted file mode 100644 index 1f1b576a0..000000000 --- a/lib/views/widgets/StatelessWidget/CupertinoDialogAction/node1_base.dart +++ /dev/null @@ -1,41 +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 { - @override - Widget build(BuildContext context) { - return Column( - children: [ - CupertinoDialogAction( - isDestructiveAction: false, - onPressed: ()=>_toast(context), - child: Text('CupertinoDialogAction'), - ), - CupertinoDialogAction( - isDestructiveAction: true, - onPressed: ()=>_toast(context), - child: Text('CupertinoDialogAction'), - ), - ], - ); - } - - _toast(BuildContext context){ - Scaffold.of(context).showSnackBar(SnackBar( - backgroundColor: Theme.of(context).primaryColor, - content: Text('CupertinoDialogAction'),)); - } -} diff --git a/lib/views/widgets/StatelessWidget/CupertinoFullscreenDialogTransition/node1_base.dart b/lib/views/widgets/StatelessWidget/CupertinoFullscreenDialogTransition/node1_base.dart deleted file mode 100644 index 6dfce8a7c..000000000 --- a/lib/views/widgets/StatelessWidget/CupertinoFullscreenDialogTransition/node1_base.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/cupertino.dart'; -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 { - 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: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/CupertinoNavigationBarBackButton/node1_base.dart b/lib/views/widgets/StatelessWidget/CupertinoNavigationBarBackButton/node1_base.dart deleted file mode 100644 index dfd81ee19..000000000 --- a/lib/views/widgets/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 { - @override - Widget build(BuildContext context) { - return Container( - child: CupertinoNavigationBarBackButton( - color: Colors.deepPurpleAccent, - onPressed: ()=>Navigator.of(context).pop(), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/CupertinoPageTransition/node1_base.dart b/lib/views/widgets/StatelessWidget/CupertinoPageTransition/node1_base.dart deleted file mode 100644 index 8cd35c9d3..000000000 --- a/lib/views/widgets/StatelessWidget/CupertinoPageTransition/node1_base.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/cupertino.dart'; -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 { - 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: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/CupertinoPopupSurface/node1_base.dart b/lib/views/widgets/StatelessWidget/CupertinoPopupSurface/node1_base.dart deleted file mode 100644 index 0806d45c4..000000000 --- a/lib/views/widgets/StatelessWidget/CupertinoPopupSurface/node1_base.dart +++ /dev/null @@ -1,62 +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 { - 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: 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/lib/views/widgets/StatelessWidget/CupertinoTheme/node1_base.dart b/lib/views/widgets/StatelessWidget/CupertinoTheme/node1_base.dart deleted file mode 100644 index b63b08b24..000000000 --- a/lib/views/widgets/StatelessWidget/CupertinoTheme/node1_base.dart +++ /dev/null @@ -1,60 +0,0 @@ - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 169, -// "name": '文字样式-TextTheme', -// "priority": 1, -// "subtitle": "后代组件可以通过CupertinoTheme.of获取主题的数据进行使用。", -// } -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - - -class TextCupertinoTheme extends StatelessWidget { - @override - Widget build(BuildContext context) { - var queryData = CupertinoTheme.of(context).textTheme; - var 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, - }; - - return Container( - child: Column( - children: styles.keys.map((e) => buildItem(e, styles[e])).toList(), - ), - ); - } - - Widget buildItem(String e, TextStyle style) => Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - e, - style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - ), - Text( - "@toly", - style: style, - ) - ], - ), - ), - Divider( - height: 1, - ) - ], - ); -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/CupertinoTheme/node2_use.dart b/lib/views/widgets/StatelessWidget/CupertinoTheme/node2_use.dart deleted file mode 100644 index 31f3624b3..000000000 --- a/lib/views/widgets/StatelessWidget/CupertinoTheme/node2_use.dart +++ /dev/null @@ -1,50 +0,0 @@ - -/// create by 张风捷特烈 on 2020-03-29 -/// contact me by email 1981462002@qq.com -/// 说明: -// { -// "widgetId": 169, -// "name": 'CupertinoThemeData的使用', -// "priority": 2, -// "subtitle": -// "和Theme一样可以通过指定的属性,让它们在后代中共享,不过属性较少。注意如果需要使用主题,不能在当前的context中获取。", -// } - -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - - -class CustomCupertinoTheme extends StatelessWidget { - @override - Widget build(BuildContext context) { - return 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, - ), - Container( - width: 150, - child: Slider(value: 0.8, onChanged: (v) => {})), - Container( width: 150,child: Divider(color:CupertinoTheme.of(context).primaryContrastingColor,thickness: 1,)) - ]); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/DataTable/node1_base.dart b/lib/views/widgets/StatelessWidget/DataTable/node1_base.dart deleted file mode 100644 index 40f9d3747..000000000 --- a/lib/views/widgets/StatelessWidget/DataTable/node1_base.dart +++ /dev/null @@ -1,46 +0,0 @@ - - -/// 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】", -// } -import 'package:flutter/material.dart'; - -class _Bean { - final int id; - final String name; - final String type; - - _Bean(this.id, this.name, this.type); -} - -class CustomDataTable extends StatelessWidget { - final data = [ - _Bean(101, 'DataTable', 'StatelessWidget'), - _Bean(44, 'RangeSlider', 'StatefulWidget'), - _Bean(2, 'Text', 'StatelessWidget'), - _Bean(1, 'Image', 'StatefulWidget'), - ]; - - final columns = ['id', '名称', '类型']; - - @override - Widget build(BuildContext context) { - return DataTable( - columns: columns.map((e) => DataColumn(label: Text(e))).toList(), - rows: data - .map((e) => DataRow(cells: [ - DataCell(Text('${e.id}')), - DataCell(Text('${e.name}')), - DataCell(Text('${e.type}')), - ])) - .toList()); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/DataTable/node2_operation.dart b/lib/views/widgets/StatelessWidget/DataTable/node2_operation.dart deleted file mode 100644 index 29022dd6d..000000000 --- a/lib/views/widgets/StatelessWidget/DataTable/node2_operation.dart +++ /dev/null @@ -1,114 +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 { - @override - _SortDataTableState createState() => _SortDataTableState(); -} - -class _SortDataTableState extends State { - var data = [ - _BeanOp(101, 'DataTable', 'StatelessWidget', false), - _BeanOp(44, 'RangeSlider', 'StatefulWidget', false), - _BeanOp(2, 'Text', 'StatelessWidget', false), - _BeanOp(1, 'Image', 'StatefulWidget', false), - ]; - - bool _sortAscending = false; - var selectData = <_BeanOp>[]; - - @override - Widget build(BuildContext context) { - return DataTable( - columnSpacing: 20, - sortColumnIndex: 1, - sortAscending: _sortAscending, - columns: [ - DataColumn( - label: Container( - child: Checkbox( - value: selectData.length == data.length, - onChanged: _onSelectAll, - ), - ), - ), - DataColumn(label: Text('id'), numeric: false, onSort: _onSortId), - DataColumn(label: Text('名称')), - 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)); - }); - } - - _onSelectOne(bool selected, _BeanOp e) { - setState(() { - if (selected) { - //选中 - selectData.add(e); - } else { - selectData.remove(e); - } - e.select = selected; - print(selectData); - }); - } - - _onSelectAll(bool select) { - setState(() { - if (select) { - data.forEach((e) => e.select = true); - selectData = data.map((e) => e).toList(); - } else { - data.forEach((e) => e.select = false); - selectData = []; - } - }); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/DayPicker/node1_base.dart b/lib/views/widgets/StatelessWidget/DayPicker/node1_base.dart deleted file mode 100644 index b93818f9b..000000000 --- a/lib/views/widgets/StatelessWidget/DayPicker/node1_base.dart +++ /dev/null @@ -1,45 +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 StatefulWidget { - @override - _CustomDayPickerState createState() => _CustomDayPickerState(); -} - -class _CustomDayPickerState extends State { - - DateTime _date = DateTime.now(); - - @override - Widget build(BuildContext context) { - return Container( - 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/lib/views/widgets/StatelessWidget/Dialog/node1_base.dart b/lib/views/widgets/StatelessWidget/Dialog/node1_base.dart deleted file mode 100644 index 111e1c9b1..000000000 --- a/lib/views/widgets/StatelessWidget/Dialog/node1_base.dart +++ /dev/null @@ -1,139 +0,0 @@ - - -/// 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】", -// } -import 'package:flutter/cupertino.dart'; -import 'package:flutter/material.dart'; - -class CustomDialog extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Column( - children: [ - _buildRaisedButton(context), - _buildDialog(), - ], - ); - } - - Widget _buildDialog() => Dialog( - backgroundColor: Colors.white, - elevation: 5, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - child: Container( - width: 50, - child: DeleteDialog(), - ), - ); - - Widget _buildRaisedButton(BuildContext context) => RaisedButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - color: Colors.blue, - onPressed: () { - showDialog(context: context, builder: (ctx) => _buildDialog()); - }, - child: Text( - 'Just Show It !', - style: TextStyle(color: Colors.white), - ), - - ); -} - -class DeleteDialog extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - _buildBar(context), - _buildTitle(), - _buildContent(), - _buildFooter(context), - ], - ), - ); - } - - Widget _buildTitle() { - return Text( - 'Delete Doucument', - style: TextStyle(color: Color(0xff5CC5E9), fontSize: 24), - ); - } - - Widget _buildContent() { - return Padding( - padding: const 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: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(30)), - color: Color(0xff73D1EE)), - child: 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: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(30)), - color: Colors.orangeAccent), - child: Text('Cancle', - style: TextStyle(color: Colors.white, fontSize: 16)), - ), - ) - ], - ), - ); - } - - _buildBar(context) => Container( - height: 30, - alignment: Alignment.centerRight, - margin: EdgeInsets.only(right: 10, top: 5), - child: InkWell( - onTap: ()=>Navigator.of(context).pop(), - child: Icon( - Icons.close, - color: Color(0xff82CAE3), - ), - ), - ); -} diff --git a/lib/views/widgets/StatelessWidget/Divider/node1_base.dart b/lib/views/widgets/StatelessWidget/Divider/node1_base.dart deleted file mode 100644 index 377096f0a..000000000 --- a/lib/views/widgets/StatelessWidget/Divider/node1_base.dart +++ /dev/null @@ -1,33 +0,0 @@ - -/// 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】", -// } - -import 'package:flutter/material.dart'; - -class CustomDivider extends StatelessWidget { - @override - Widget build(BuildContext context) { - var dataColor = [ - Colors.red, Colors.yellow, - Colors.blue, Colors.green]; - var dataThickness = [1.0, 2.0, 4.0, 6.0]; - var 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/lib/views/widgets/StatelessWidget/Divider/node2_height.dart b/lib/views/widgets/StatelessWidget/Divider/node2_height.dart deleted file mode 100644 index 820a669c9..000000000 --- a/lib/views/widgets/StatelessWidget/Divider/node2_height.dart +++ /dev/null @@ -1,38 +0,0 @@ - -/// 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】", -// } - -import 'package:flutter/material.dart'; - - -class HeightDivider extends StatelessWidget { - @override - Widget build(BuildContext context) { - var dataColor = [ - Colors.red, Colors.yellow, - Colors.blue, Colors.green]; - var dataThickness = [10.0, 20.0, 30.0, 40.0]; - var data = Map.fromIterables(dataColor, dataThickness); - return Column( - children: dataColor - .map((e) => Divider( - color: e, - indent:data[e], - endIndent: data[e]*2, - height: data[e], - thickness: data[e]/10, - )) - .toList(), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/DraggableScrollableActuator/node1_base.dart b/lib/views/widgets/StatelessWidget/DraggableScrollableActuator/node1_base.dart deleted file mode 100644 index 710ea969a..000000000 --- a/lib/views/widgets/StatelessWidget/DraggableScrollableActuator/node1_base.dart +++ /dev/null @@ -1,117 +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 -/// 说明: 221 DraggableScrollableActuator 拖滑重置器 它可以通知后代的 DraggableScrollableSheet,将其位置重置为初始状态。 -// { -// "widgetId": 221, -// "name": '基本使用方法', -// "priority": 1, -// "subtitle": -// "【child】 : 子组件 【Widget】\n" -// "使用 DraggableScrollableActuator.reset(context) 重置后代 DraggableScrollableSheet 位初始位置。", -// } - -class DraggableScrollableActuatorDemo extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.all(10), - child: ElevatedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => DraggableScrollableActuatorPage()), - ); - }, - child: Text("进入 DraggableScrollableActuator 测试页"), - ), - ); - } -} - -class DraggableScrollableActuatorPage extends StatelessWidget { - 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: Text("DraggableScrollableActuator"), - ), - body: Container( - // height: 400, - child: DraggableScrollableActuator( - child: Builder( - builder: (ctx) => Column( - children: [ - ElevatedButton( - onPressed: () { - DraggableScrollableActuator.reset(ctx); - }, - child: 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: 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/lib/views/widgets/StatelessWidget/Drawer/node1_base.dart b/lib/views/widgets/StatelessWidget/Drawer/node1_base.dart deleted file mode 100644 index 98bf6a185..000000000 --- a/lib/views/widgets/StatelessWidget/Drawer/node1_base.dart +++ /dev/null @@ -1,71 +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 { - @override - Widget build(BuildContext context) { - return Container( - height: 400, - child: Scaffold( - appBar: AppBar( - title: 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/lib/views/widgets/StatelessWidget/DrawerHeader/node1_base.dart b/lib/views/widgets/StatelessWidget/DrawerHeader/node1_base.dart deleted file mode 100644 index 724d6f654..000000000 --- a/lib/views/widgets/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 { - @override - Widget build(BuildContext context) { - return Container( - height: 400, - child: Scaffold( - appBar: AppBar( - title: Text('Flutter Unit'), - ), - drawer: Drawer( - elevation: 3, - child: _buildChild(), - ), - ), - ); - } - - Widget _buildChild() => ListView( - padding: EdgeInsets.zero, - children: [ - _buildHeader(), - 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('我的文件'), - ), - ], - ); - - Widget _buildHeader() { - return 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/lib/views/widgets/StatelessWidget/FadeInImage/node1_base.dart b/lib/views/widgets/StatelessWidget/FadeInImage/node1_base.dart deleted file mode 100644 index 7ff17389e..000000000 --- a/lib/views/widgets/StatelessWidget/FadeInImage/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": 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 { - @override - Widget build(BuildContext context) { - var placeholder = "assets/images/icon_head.webp"; - var img = - "https://user-gold-cdn.xitu.io/2017/8/24/" - "d324efef8cbee6468a018aad7ab2ba6b?imageView2/" - "1/w/180/h/180/q/85/format/webp/interlace/1"; - return FadeInImage.assetNetwork( - placeholder: placeholder, - image: img, - width: 100, - height: 100, - fit: BoxFit.cover, - repeat:ImageRepeat.noRepeat, - alignment: Alignment.center, - fadeInDuration: Duration(seconds: 5), - fadeInCurve: Curves.easeInCubic, - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/FilterChip/node1_base.dart b/lib/views/widgets/StatelessWidget/FilterChip/node1_base.dart deleted file mode 100644 index df8714232..000000000 --- a/lib/views/widgets/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 { - @override - _CustomFilterChipState createState() => _CustomFilterChipState(); -} - -class _CustomFilterChipState extends State { - final Map map = { - 'A': 'Ant', - 'B': 'Bug', - 'C': 'Cat', - 'D': 'Dog', - }; - List _selected = []; - - @override - Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Wrap( - children: map.keys.map((key) => - _buildChild(key)).toList(), - ), - Container( - padding: EdgeInsets.all(10), - child: Text('您已选择: ${_selected.join(', ')}')), - ], - ); - } - - Padding _buildChild(String 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)), - 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/lib/views/widgets/StatelessWidget/FlatButton/node1_base.dart b/lib/views/widgets/StatelessWidget/FlatButton/node1_base.dart deleted file mode 100755 index 4f2b38bb5..000000000 --- a/lib/views/widgets/StatelessWidget/FlatButton/node1_base.dart +++ /dev/null @@ -1,32 +0,0 @@ -/// 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】", -// } - -import 'package:flutter/material.dart'; - -class CustomFlatButton extends StatelessWidget { - @override - Widget build(BuildContext context) { - return FlatButton( - onPressed: ()=>{}, - padding: EdgeInsets.all(8), - splashColor: Colors.green, - child: Text("FlatButton"), - textColor: Color(0xffFfffff), - color: Colors.blue, - highlightColor: Color(0xffF88B0A), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/FloatingActionButton/node1_base.dart b/lib/views/widgets/StatelessWidget/FloatingActionButton/node1_base.dart deleted file mode 100644 index fa2fe1323..000000000 --- a/lib/views/widgets/StatelessWidget/FloatingActionButton/node1_base.dart +++ /dev/null @@ -1,39 +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 { - @override - Widget build(BuildContext context) { - var 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/lib/views/widgets/StatelessWidget/FloatingActionButton/node2_mini.dart b/lib/views/widgets/StatelessWidget/FloatingActionButton/node2_mini.dart deleted file mode 100644 index 2c47d8a00..000000000 --- a/lib/views/widgets/StatelessWidget/FloatingActionButton/node2_mini.dart +++ /dev/null @@ -1,40 +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 { - @override - Widget build(BuildContext context) { - var 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/lib/views/widgets/StatelessWidget/FloatingActionButton/node3_shape.dart b/lib/views/widgets/StatelessWidget/FloatingActionButton/node3_shape.dart deleted file mode 100644 index 9b5551c28..000000000 --- a/lib/views/widgets/StatelessWidget/FloatingActionButton/node3_shape.dart +++ /dev/null @@ -1,66 +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 '../../../../app/utils/pather.dart'; - -class ShapeFAB extends StatelessWidget { - @override - Widget build(BuildContext context) { - var 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 => null; - - @override - Path getInnerPath(Rect rect, {TextDirection textDirection}) { - return null; - } - - @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) { - return null; - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/FlutterLogo/node1_base.dart b/lib/views/widgets/StatelessWidget/FlutterLogo/node1_base.dart deleted file mode 100644 index fe426b738..000000000 --- a/lib/views/widgets/StatelessWidget/FlutterLogo/node1_base.dart +++ /dev/null @@ -1,34 +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 { - @override - Widget build(BuildContext context) { - - var 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/lib/views/widgets/StatelessWidget/FlutterLogo/node2_style.dart b/lib/views/widgets/StatelessWidget/FlutterLogo/node2_style.dart deleted file mode 100644 index 559a501ed..000000000 --- a/lib/views/widgets/StatelessWidget/FlutterLogo/node2_style.dart +++ /dev/null @@ -1,33 +0,0 @@ - -/// 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】", -// } - -import 'package:flutter/material.dart'; -class FlutterLogoWithText extends StatelessWidget { - @override - Widget build(BuildContext context) { - var data = { - FlutterLogoStyle.horizontal:Colors.blue, - FlutterLogoStyle.markOnly:Colors.red, - FlutterLogoStyle.stacked:Colors.green, - }; - - return Wrap( - spacing: 20, - children: data.keys.map((e) => FlutterLogo( - size: 80, - style: e, - textColor: data[e], - )) - .toList(), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/GestureDetector/node1_base.dart b/lib/views/widgets/StatelessWidget/GestureDetector/node1_base.dart deleted file mode 100644 index 040abfdd3..000000000 --- a/lib/views/widgets/StatelessWidget/GestureDetector/node1_base.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 -/// 说明: - -/// 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 { - @override - _CustomGestureDetectorState createState() => _CustomGestureDetectorState(); -} - -class _CustomGestureDetectorState extends State { - var _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: TextStyle(fontSize: 18, color: Colors.blue), - ), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/GestureDetector/node2_tap.dart b/lib/views/widgets/StatelessWidget/GestureDetector/node2_tap.dart deleted file mode 100644 index 2d3600274..000000000 --- a/lib/views/widgets/StatelessWidget/GestureDetector/node2_tap.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": 146, -// "name": 'GestureDetector详情信息', -// "priority": 2, -// "subtitle": -// "【onTapDown】 : 按下回调 【GestureTapDownCallback】\n" -// "【onTapUp】 : 点击抬起回调 【GestureTapUpCallback】\n" -// "【onTapCancel】 : 点击取消 【GestureTapCancelCallback】", -// } -class TapGestureDetector extends StatefulWidget { - @override - _TapGestureDetectorState createState() => _TapGestureDetectorState(); -} - -class _TapGestureDetectorState extends State { - var _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: TextStyle(fontSize: 18, color: Colors.blue), - ), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/GestureDetector/node3_pan.dart b/lib/views/widgets/StatelessWidget/GestureDetector/node3_pan.dart deleted file mode 100644 index d4989fac9..000000000 --- a/lib/views/widgets/StatelessWidget/GestureDetector/node3_pan.dart +++ /dev/null @@ -1,52 +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 { - @override - _PanGestureDetectorState createState() => _PanGestureDetectorState(); -} - -class _PanGestureDetectorState extends State { - var _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: TextStyle(fontSize: 18, color: Colors.blue), - ), - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/GirdView/node1_base.dart b/lib/views/widgets/StatelessWidget/GirdView/node1_base.dart deleted file mode 100644 index f8dfa484e..000000000 --- a/lib/views/widgets/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 { - final data = List.generate(128, (i) => Color(0xFFFF00FF - 2*i)); - - @override - Widget build(BuildContext context) { - return Container( - 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: 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/lib/views/widgets/StatelessWidget/GirdView/node2_direction.dart b/lib/views/widgets/StatelessWidget/GirdView/node2_direction.dart deleted file mode 100644 index c72f57d6c..000000000 --- a/lib/views/widgets/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 { - final data = List.generate(128, (i) => Color(0xFF00FFFF - 2*i)); - - @override - Widget build(BuildContext context) { - return Container( - 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: 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/lib/views/widgets/StatelessWidget/GirdView/node3_extend.dart b/lib/views/widgets/StatelessWidget/GirdView/node3_extend.dart deleted file mode 100644 index 5fc92eada..000000000 --- a/lib/views/widgets/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 { - final data = List.generate(128, (i) => Color(0xFF00FFFF - 2*i)); - - @override - Widget build(BuildContext context) { - return Container( - 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: 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/lib/views/widgets/StatelessWidget/GirdView/node4_builder.dart b/lib/views/widgets/StatelessWidget/GirdView/node4_builder.dart deleted file mode 100644 index 695cae7bf..000000000 --- a/lib/views/widgets/StatelessWidget/GirdView/node4_builder.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.builder构造', -// "priority": 4, -// "subtitle": -// "【itemCount】 : 条目数量 【int】\n" -// "【gridDelegate】 : 网格代理 【SliverGridDelegate】\n" -// "【itemBuilder】 : 条目构造器 【IndexedWidgetBuilder】", -// } -class BuilderGridView extends StatelessWidget { - final data = List.generate(128, (i) => Color(0xFF33FFF - 2*i)); - - @override - Widget build(BuildContext context) { - return Container( - height: 200, - child: GridView.builder( - itemCount: data.length, - scrollDirection: Axis.vertical, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(//网格代理:定交叉轴数目 - crossAxisCount: 4,//条目个数 - mainAxisSpacing: 5,//主轴间距 - crossAxisSpacing: 5,//交叉轴间距 - childAspectRatio:1/0.618), - itemBuilder: (_, int position)=> _buildItem(data[position]) - ), - ); - } - - Container _buildItem(Color color) => Container( - alignment: Alignment.center, - width: 100, - height: 30, - color: color, - child: Text( - colorString(color), - style: 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/lib/views/widgets/StatelessWidget/GridPaper/node1_base.dart b/lib/views/widgets/StatelessWidget/GridPaper/node1_base.dart deleted file mode 100644 index e4bdb812f..000000000 --- a/lib/views/widgets/StatelessWidget/GridPaper/node1_base.dart +++ /dev/null @@ -1,29 +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": "GridPage基础属性", -// "subtitle": "【child】: 子组件 【Widget】\n" -// "【color】: 颜色 【Color】\n" -// "【interval】: 小块边长 【double】", -// } -class CustomGridPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - 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/lib/views/widgets/StatelessWidget/GridPaper/node2_divisions.dart b/lib/views/widgets/StatelessWidget/GridPaper/node2_divisions.dart deleted file mode 100644 index 14b4d01b8..000000000 --- a/lib/views/widgets/StatelessWidget/GridPaper/node2_divisions.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": 2, -// "name": "GridPage再分割", -// "subtitle": "【child】: 子组件 【Widget】\n" -// "【color】: 颜色 【Color】\n" -// "【subdivisions】: 小块中子块个数 【int】\n" -// "【divisions】: 小块中子块的分割数 【int】", -// } -class DivisionsGridPage extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Container( - 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/lib/views/widgets/StatelessWidget/GridTile/node1_base.dart b/lib/views/widgets/StatelessWidget/GridTile/node1_base.dart deleted file mode 100644 index f3085d5e9..000000000 --- a/lib/views/widgets/StatelessWidget/GridTile/node1_base.dart +++ /dev/null @@ -1,55 +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 { - @override - Widget build(BuildContext context) { - return Container( - width: 200, height: 200, - child: GridTile( - header: GridTileBar( - backgroundColor: Colors.blue.withAlpha(120), - trailing: Icon( - Icons.star, - color: Colors.red, - ), - leading: CircleAvatar( - backgroundImage: AssetImage("assets/images/wy_200x300.webp"), - ), - title: Text("百里·巫缨"), - subtitle: Text("倾国必倾城"), - ), - child: Opacity( - opacity: 0.5, - child: Image.asset( - "assets/images/sabar.webp", - fit: BoxFit.cover, - ), - ), - footer: Padding( - padding: const 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/lib/views/widgets/StatelessWidget/GridTileBar/node1_base.dart b/lib/views/widgets/StatelessWidget/GridTileBar/node1_base.dart deleted file mode 100644 index eb6658da9..000000000 --- a/lib/views/widgets/StatelessWidget/GridTileBar/node1_base.dart +++ /dev/null @@ -1,36 +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": "【value】 : 条目对象 【T】\n" -// "【leading】: 左侧组件 【Widget】\n" -// "【trailing】: 尾组件 【Widget】\n" -// "【title】: 中间上组件 【Widget】\n" -// "【subtitle】: 中间下组件 【Widget】\n" -// "【backgroundColor】: 背景色 【Color】", -// } - - -class CustomGridTileBar extends StatelessWidget { - @override - Widget build(BuildContext context) { - return GridTileBar( - backgroundColor: Colors.blue.withAlpha(120), - trailing: Icon( - Icons.star, - color: Colors.red, - ), - leading: CircleAvatar( - backgroundImage: AssetImage("assets/images/wy_200x300.webp"), - ), - title: Text("百里·巫缨"), - subtitle: Text("倾国必倾城"), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/HtmlElementView/node1_base.dart b/lib/views/widgets/StatelessWidget/HtmlElementView/node1_base.dart deleted file mode 100644 index d2e28d41f..000000000 --- a/lib/views/widgets/StatelessWidget/HtmlElementView/node1_base.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/cupertino.dart'; -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 { - final String info = - '该组件只能用于 Flutter Web 中,嵌入 Html 元素的较为昂贵。' - '内部基于 PlatformViewLink 和 PlatformViewSurface 组件实现。'; - - @override - Widget build(BuildContext context) { - return Container( - color: Colors.blue.withOpacity(0.1), - padding: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/Icon/node1_base.dart b/lib/views/widgets/StatelessWidget/Icon/node1_base.dart deleted file mode 100644 index 64aa69209..000000000 --- a/lib/views/widgets/StatelessWidget/Icon/node1_base.dart +++ /dev/null @@ -1,38 +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 { - @override - Widget build(BuildContext context) { - return Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - 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/lib/views/widgets/StatelessWidget/Icon/node2_diy.dart b/lib/views/widgets/StatelessWidget/Icon/node2_diy.dart deleted file mode 100644 index cd0cb2429..000000000 --- a/lib/views/widgets/StatelessWidget/Icon/node2_diy.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../../../app/res/toly_icon.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 { - @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/lib/views/widgets/StatelessWidget/IconButton/node1_base.dart b/lib/views/widgets/StatelessWidget/IconButton/node1_base.dart deleted file mode 100755 index e1d2386a6..000000000 --- a/lib/views/widgets/StatelessWidget/IconButton/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": 30, -// "priority": 1, -// "name": "IconButton属性", -// "subtitle": "【child】: 子组件 【Widget】\n" -// "【icon】: 内边距 【Widget】\n" -// "【tooltip】: 长按提示文字 【String】\n" -// "【highlightColor】: 长按高亮色 【Color】\n" -// "【splashColor】: 水波纹色 【Color】\n" -// "【onPressed】: 点击事件 【Function】", -// } - -class CustomIconButton extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: IconButton( - padding: EdgeInsets.only(), - onPressed: () {}, - icon: Icon(Icons.android, size: 40, color: Colors.green), - tooltip: "android", - highlightColor: Colors.orangeAccent, - splashColor: Colors.blue, - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/ImageIcon/node1_base.dart b/lib/views/widgets/StatelessWidget/ImageIcon/node1_base.dart deleted file mode 100644 index a13615c11..000000000 --- a/lib/views/widgets/StatelessWidget/ImageIcon/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": 7, -// "priority": 1, -// "name": "用于显示一个纯色图片", -// "subtitle": "【入参】 : 图片资源 【ImageProvider】\n" -// "【size】 : 大小 【double】\n" -// "【color】: 角标颜色 【Color】", -// } - -class CustomImageIcon extends StatelessWidget { - @override - Widget build(BuildContext context) { - var 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( - AssetImage("assets/images/leaf.webp"), - color: e, - size: data[e], - )) - .toList(), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/InputChip/node1_base.dart b/lib/views/widgets/StatelessWidget/InputChip/node1_base.dart deleted file mode 100644 index c4d4bebd4..000000000 --- a/lib/views/widgets/StatelessWidget/InputChip/node1_base.dart +++ /dev/null @@ -1,40 +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 { - @override - _PressInputChipState createState() => _PressInputChipState(); -} - -class _PressInputChipState extends State { - bool _delete = false; - - @override - Widget build(BuildContext context) { - return InputChip( - padding: EdgeInsets.all(5), - labelPadding: 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/lib/views/widgets/StatelessWidget/InputChip/node2_select.dart b/lib/views/widgets/StatelessWidget/InputChip/node2_select.dart deleted file mode 100644 index ed4bcfa86..000000000 --- a/lib/views/widgets/StatelessWidget/InputChip/node2_select.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": 14, -// "priority": 2, -// "name": "可以接受选中事件", -// "subtitle": "【selected】: 是否选中 【bool】\n" -// "【onSelected】: 选中事件 【Function(bool)】", -// } - -class SelectInputChip extends StatefulWidget { - @override - _SelectInputChipState createState() => _SelectInputChipState(); -} - -class _SelectInputChipState extends State { - bool _select = false; - - @override - Widget build(BuildContext context) { - return InputChip( - selected: _select, - padding: EdgeInsets.all(5), - labelPadding: EdgeInsets.all(3), - label: 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/lib/views/widgets/StatelessWidget/ListTile/node1_base.dart b/lib/views/widgets/StatelessWidget/ListTile/node1_base.dart deleted file mode 100644 index 7e8417ec6..000000000 --- a/lib/views/widgets/StatelessWidget/ListTile/node1_base.dart +++ /dev/null @@ -1,34 +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 { - @override - Widget build(BuildContext context) { - return Container( - margin: EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: ListTile( - leading: Image.asset("assets/images/icon_head.webp"), - title: Text("以梦为马"), - subtitle: Text("海子"), - contentPadding: EdgeInsets.all(5), - trailing: Icon(Icons.more_vert), - onLongPress: () => Navigator.of(context).pushNamed('AboutMePage'), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/ListTile/node2_select.dart b/lib/views/widgets/StatelessWidget/ListTile/node2_select.dart deleted file mode 100644 index fa8f8f0d5..000000000 --- a/lib/views/widgets/StatelessWidget/ListTile/node2_select.dart +++ /dev/null @@ -1,38 +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 { - @override - _SelectListTileState createState() => _SelectListTileState(); -} - -class _SelectListTileState extends State { - bool _selected = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: ListTile( - leading: Image.asset("assets/images/icon_head.webp"), - selected: _selected, - title: Text("以梦为马"), - subtitle: Text("海子"), - contentPadding: EdgeInsets.all(5), - trailing: Icon(Icons.more_vert), - onTap: () => setState(() => _selected = !_selected), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/ListTile/node3_dense.dart b/lib/views/widgets/StatelessWidget/ListTile/node3_dense.dart deleted file mode 100644 index bb67d1205..000000000 --- a/lib/views/widgets/StatelessWidget/ListTile/node3_dense.dart +++ /dev/null @@ -1,38 +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 { - @override - _DenseListTileState createState() => _DenseListTileState(); -} - -class _DenseListTileState extends State { - bool _dense = false; - - @override - Widget build(BuildContext context) { - return Container( - margin: EdgeInsets.all(10), - color: Colors.grey.withAlpha(22), - child: ListTile( - leading: Image.asset("assets/images/icon_head.webp"), - title: Text("以梦为马"), - subtitle: Text("海子"), - selected: false, - contentPadding: EdgeInsets.all(5), - trailing: Icon(Icons.more_vert), - dense: _dense, - onTap: () => setState(() => _dense = !_dense), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/ListView/node1_base.dart b/lib/views/widgets/StatelessWidget/ListView/node1_base.dart deleted file mode 100644 index 0d417a666..000000000 --- a/lib/views/widgets/StatelessWidget/ListView/node1_base.dart +++ /dev/null @@ -1,57 +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 { - final 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 Container( - height: 200, - child: ListView( - padding: EdgeInsets.symmetric(horizontal: 5), - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: color, - child: Text( - colorString(color), - style: 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/lib/views/widgets/StatelessWidget/ListView/node2_direction.dart b/lib/views/widgets/StatelessWidget/ListView/node2_direction.dart deleted file mode 100644 index 7a5803a68..000000000 --- a/lib/views/widgets/StatelessWidget/ListView/node2_direction.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": 162, -// "name": 'ListView横向滑动', -// "priority": 2, -// "subtitle": -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【reverse】 : 是否反向滑动 【bool】\n" -// "【shrinkWrap】 : 无边界时是否包裹 【bool】", -// } -class HorizontalListView extends StatelessWidget { - final 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 Container( - 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(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/lib/views/widgets/StatelessWidget/ListView/node3_builder.dart b/lib/views/widgets/StatelessWidget/ListView/node3_builder.dart deleted file mode 100644 index f34be647e..000000000 --- a/lib/views/widgets/StatelessWidget/ListView/node3_builder.dart +++ /dev/null @@ -1,55 +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 { - final 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 Container( - height: 200, - child: ListView.builder( - itemCount: data.length, - itemBuilder: (context, index) => _buildItem(data[index]), - ), - ); - } - - 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(color: Colors.white, shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ]), - ), - ); -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/ListView/node4_separated.dart b/lib/views/widgets/StatelessWidget/ListView/node4_separated.dart deleted file mode 100644 index 52cd7e594..000000000 --- a/lib/views/widgets/StatelessWidget/ListView/node4_separated.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": 162, -// "name": 'ListView.separated构造', -// "priority": 3, -// "subtitle": -// "【separatorBuilder】 : 条目构造器 【IndexedWidgetBuilder】", -// } -class SeparatedListView extends StatelessWidget { - final 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 Container( - height: 200, - child: ListView.separated( - separatorBuilder: (context, index) => 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()}"; - - Widget _buildItem(Color color) => Container( - alignment: Alignment.center, - width: 100, - height: 50, - color: color, - child: Text( - colorString(color), - style: TextStyle(color: Colors.white, shadows: [ - Shadow(color: Colors.black, offset: Offset(.5, .5), blurRadius: 2) - ]), - ), - ); -} diff --git a/lib/views/widgets/StatelessWidget/Listener/node1_base.dart b/lib/views/widgets/StatelessWidget/Listener/node1_base.dart deleted file mode 100644 index f515e9f28..000000000 --- a/lib/views/widgets/StatelessWidget/Listener/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": 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 { - @override - _CustomListenerState createState() => _CustomListenerState(); -} - -class _CustomListenerState extends State { - var _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: TextStyle(fontSize: 16, color: Colors.blue), - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/MaterialBanner/node1_one_btn.dart b/lib/views/widgets/StatelessWidget/MaterialBanner/node1_one_btn.dart deleted file mode 100644 index 5db4583f6..000000000 --- a/lib/views/widgets/StatelessWidget/MaterialBanner/node1_one_btn.dart +++ /dev/null @@ -1,47 +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 { - final info = - 'Welcome to Flutter Unit!'; - - @override - Widget build(BuildContext context) { - return Column( - children: [MaterialBanner( - content: Text( - info, - style: TextStyle(color: Colors.white), - ), - backgroundColor: Colors.purple, - leading: Icon(Icons.info, color: Colors.lightBlueAccent), - padding: EdgeInsetsDirectional.only(start: 16.0, top: 2.0), - forceActionsBelow: false, // 默认false - actions: [ - Text( - 'I KNOW', - style:TextStyle( - color: Colors.orange, - fontWeight: FontWeight.bold, - fontSize: 14) , - ) - ], - )], - ); - } - -} diff --git a/lib/views/widgets/StatelessWidget/MaterialBanner/node2_two_btn.dart b/lib/views/widgets/StatelessWidget/MaterialBanner/node2_two_btn.dart deleted file mode 100644 index 46e976c9a..000000000 --- a/lib/views/widgets/StatelessWidget/MaterialBanner/node2_two_btn.dart +++ /dev/null @@ -1,60 +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 { - 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, - style: TextStyle(color: Colors.white), - ), - backgroundColor: Colors.purple, - leading: Icon(Icons.warning, color: Colors.yellow), - padding: EdgeInsetsDirectional.only(start: 16.0, top: 2.0,end: 2), - leadingPadding:EdgeInsetsDirectional.only(end: 16.0) , - actions: [ - RaisedButton( - color: Colors.white, - onPressed: () {}, - child: Text( - 'I KNOW', - style: TextStyle( - color: Colors.purple, - fontWeight: FontWeight.bold, - fontSize: 14), - ), - ), - - RaisedButton( - color: Colors.white, - onPressed: () {}, - child: Text( - 'I IGNORE', - style: TextStyle( - color: Colors.purple, - fontWeight: FontWeight.bold, - fontSize: 14), - ), - ), - ], - )], - ); - } - -} diff --git a/lib/views/widgets/StatelessWidget/MaterialButton/node1_base.dart b/lib/views/widgets/StatelessWidget/MaterialButton/node1_base.dart deleted file mode 100644 index 9e3c20ef4..000000000 --- a/lib/views/widgets/StatelessWidget/MaterialButton/node1_base.dart +++ /dev/null @@ -1,35 +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 { - @override - Widget build(BuildContext context) { - return MaterialButton( - height: 40, - elevation: 5, - color: Colors.orangeAccent, - textColor: Colors.white, - splashColor: Colors.blue, - padding: EdgeInsets.all(8), - child: Text("MaterialButton"), - onPressed: () => Navigator.of(context).pushNamed('AboutMePage')); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/MaterialButton/node2_onLongPress.dart b/lib/views/widgets/StatelessWidget/MaterialButton/node2_onLongPress.dart deleted file mode 100644 index b033435ec..000000000 --- a/lib/views/widgets/StatelessWidget/MaterialButton/node2_onLongPress.dart +++ /dev/null @@ -1,29 +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 { - @override - Widget build(BuildContext context) { - return MaterialButton( - height: 40, - elevation: 5, - color: Colors.blue, - highlightColor: Colors.green, - textColor: Colors.white, - padding: EdgeInsets.all(8), - child: Text("MaterialButton"), - onLongPress: () => Navigator.of(context).pushNamed('AboutMePage'), - onPressed: () => Navigator.of(context).pushNamed('AboutMePage')); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/MaterialButton/node3_shape.dart b/lib/views/widgets/StatelessWidget/MaterialButton/node3_shape.dart deleted file mode 100644 index 6a1dadacf..000000000 --- a/lib/views/widgets/StatelessWidget/MaterialButton/node3_shape.dart +++ /dev/null @@ -1,62 +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 { - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 20, - children: [ - Container( - width: 40, - height: 40, - child: MaterialButton( - padding: EdgeInsets.all(0), - textColor: Color(0xffFfffff), - elevation: 3, - color: Colors.blue, - highlightColor: Color(0xffF88B0A), - splashColor: Colors.red, - child: Icon( - Icons.add, - color: Colors.white, - ), - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - ), - onLongPress: () => Navigator.of(context).pushNamed('AboutMePage'), - onPressed: () => Navigator.of(context).pushNamed('AboutMePage')), - ), - Container( - width: 100, - height: 40, - child: MaterialButton( - padding: EdgeInsets.all(0), - textColor: Color(0xffFfffff), - elevation: 3, - color: Colors.blue, - highlightColor: Color(0xffF88B0A), - splashColor: Colors.red, - child: Icon( - Icons.remove, - color: Colors.white, - ), - shape: 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/lib/views/widgets/StatelessWidget/ModalBarrier/node1_base.dart b/lib/views/widgets/StatelessWidget/ModalBarrier/node1_base.dart deleted file mode 100644 index 8f5f2cd9f..000000000 --- a/lib/views/widgets/StatelessWidget/ModalBarrier/node1_base.dart +++ /dev/null @@ -1,32 +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 { - - @override - Widget build(BuildContext context) { - return Container( - width: 200, - height: 100, - child: Stack(alignment: Alignment.center, children: [ - ModalBarrier( - dismissible: true, - color: Colors.grey.withOpacity(0.3), - ), - Text('点击背景返回') - ]), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/NavigationToolbar/node1_base.dart b/lib/views/widgets/StatelessWidget/NavigationToolbar/node1_base.dart deleted file mode 100644 index 669157ebe..000000000 --- a/lib/views/widgets/StatelessWidget/NavigationToolbar/node1_base.dart +++ /dev/null @@ -1,45 +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 { - @override - Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - height: 60, - child: NavigationToolbar( - leading: Icon(Icons.ac_unit), - middle: Text('middleSpacing#true'), - middleSpacing: 20, - centerMiddle: true, - trailing: Icon(Icons.more_vert), - ), - ), - Container( - 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/lib/views/widgets/StatelessWidget/NotificationListener/node1_base.dart b/lib/views/widgets/StatelessWidget/NotificationListener/node1_base.dart deleted file mode 100644 index 87e932be9..000000000 --- a/lib/views/widgets/StatelessWidget/NotificationListener/node1_base.dart +++ /dev/null @@ -1,65 +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 { - @override - _NotificationListenerDemoState createState() => _NotificationListenerDemoState(); -} - -class _NotificationListenerDemoState extends State { - final data = List.generate(30, (i) => '第${i + 1}条'); - - @override - Widget build(BuildContext context) { - return Container( - height: 250, - child: NotificationListener( - onNotification: _onNotification, - child: CupertinoScrollbar( - child: ListView.separated( - itemBuilder: _buildItem, - itemCount: data.length, - separatorBuilder: (_,__)=>Divider(height: 5,), - ), - )), - ); - } - - bool _onNotification(OverscrollIndicatorNotification notification) { - if (notification.leading) { - notification.disallowGlow(); - Scaffold.of(context).showSnackBar(SnackBar( - content: Text('已滑到顶部'), - backgroundColor: Colors.blue, - duration: Duration(milliseconds: 200), - )); - } else { - notification.disallowGlow(); - Scaffold.of(context).showSnackBar(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/lib/views/widgets/StatelessWidget/NotificationListener/node2_update.dart b/lib/views/widgets/StatelessWidget/NotificationListener/node2_update.dart deleted file mode 100644 index 40e0fa85a..000000000 --- a/lib/views/widgets/StatelessWidget/NotificationListener/node2_update.dart +++ /dev/null @@ -1,87 +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 { - @override - _NotificationListenerUpdateState createState() => - _NotificationListenerUpdateState(); -} - -class _NotificationListenerUpdateState - extends State { - final data = List.generate(30, (i) => '第${i + 1}条'); - - var _info = ''; - - @override - Widget build(BuildContext context) { - return Container( - 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: (_, __) => 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: EdgeInsets.only(right: 8), - child: Text( - data[index], - style: TextStyle(color: Theme.of(context).primaryColor, fontSize: 18), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/OrientationBuilder/node1_base.dart b/lib/views/widgets/StatelessWidget/OrientationBuilder/node1_base.dart deleted file mode 100644 index dacb663ff..000000000 --- a/lib/views/widgets/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 { - @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 Icon(Icons.phone_android,size: 60,); - break; - case Orientation.landscape: - return RotatedBox( - quarterTurns: 1, - child: Icon(Icons.phone_android,size: 60,)); - break; - default: return Container(); - } - } - - Widget _buildSlider() =>Slider( - value: _width, - max: 350.0, - min: 80.0, - divisions: 17, - onChanged: (v)=> setState(() => _width= v), - ); -} diff --git a/lib/views/widgets/StatelessWidget/OutlineButton/node1_base.dart b/lib/views/widgets/StatelessWidget/OutlineButton/node1_base.dart deleted file mode 100755 index 3f9c9f90b..000000000 --- a/lib/views/widgets/StatelessWidget/OutlineButton/node1_base.dart +++ /dev/null @@ -1,34 +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 { - @override - Widget build(BuildContext context) { - return OutlineButton(//边线按钮 - onPressed: () {}, - child: Text("OutlineButton"), - padding: EdgeInsets.all(8), - splashColor: Colors.green, - highlightColor: Colors.orangeAccent, - highlightedBorderColor: Colors.grey, - textColor: Color(0xff000000), - borderSide: BorderSide(color: Color(0xff0A66F8), width: 2), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/PageStorage/node1_base.dart b/lib/views/widgets/StatelessWidget/PageStorage/node1_base.dart deleted file mode 100644 index 09e4931ec..000000000 --- a/lib/views/widgets/StatelessWidget/PageStorage/node1_base.dart +++ /dev/null @@ -1,115 +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 { - @override - _PageStorageDemoState createState() => _PageStorageDemoState(); -} - -class _PageStorageDemoState extends State { - int _pageIndex = 0; - final PageStorageBucket _bucket = PageStorageBucket(); - - @override - Widget build(BuildContext context) { - return Container( - 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: [ - BottomNavigationBarItem( - icon: Icon(Icons.home), - title: Text('Home'), - ), - BottomNavigationBarItem( - icon: Icon(Icons.settings), - title: Text('Setting'), - ), - ], - ), - ), - ); - } - - Widget _buildContentByIndex() { - if (_pageIndex == 0) { - return CountWidget(key: PageStorageKey('CountWidget1')); - } - - if (_pageIndex == 1) { - return CountWidget(key: PageStorageKey('CountWidget2')); - } - - return ListView(); - } -} - -class CountWidget extends StatefulWidget { - 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) as int ?? 0; - } - - @override - Widget build(BuildContext context) { - return Container( - alignment: Alignment.center, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text('点击了$_count次'), - MaterialButton( - onPressed: _addCount, - child: Icon( - Icons.add, - color: Colors.white, - ), - color: Colors.green, - shape: CircleBorder( - side: BorderSide(width: 2.0, color: Color(0xFFFFDFDFDF)), - )) - ], - ), - ); - } - - void _addCount() { - setState(() { - _count++; - PageStorage.of(context)?.writeState(context, _count); - }); - } -} diff --git a/lib/views/widgets/StatelessWidget/Placeholder/node1_base.dart b/lib/views/widgets/StatelessWidget/Placeholder/node1_base.dart deleted file mode 100644 index aee4a58f3..000000000 --- a/lib/views/widgets/StatelessWidget/Placeholder/node1_base.dart +++ /dev/null @@ -1,25 +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 { - @override - Widget build(BuildContext context) { - return Container( - width: 100, - height: 100*0.618, - child: Placeholder( - color: Colors.orangeAccent, - strokeWidth: 2, - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/Placeholder/node2_fallback.dart b/lib/views/widgets/StatelessWidget/Placeholder/node2_fallback.dart deleted file mode 100644 index bed98ef26..000000000 --- a/lib/views/widgets/StatelessWidget/Placeholder/node2_fallback.dart +++ /dev/null @@ -1,26 +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 { - @override - Widget build(BuildContext context) { - return UnconstrainedBox( - child: Placeholder( - color: Colors.blue, - strokeWidth: 2, - fallbackHeight: 100, - fallbackWidth: 150, - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/PositionedDirectional/node1_base.dart b/lib/views/widgets/StatelessWidget/PositionedDirectional/node1_base.dart deleted file mode 100644 index 2dc195f68..000000000 --- a/lib/views/widgets/StatelessWidget/PositionedDirectional/node1_base.dart +++ /dev/null @@ -1,62 +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 { - @override - Widget build(BuildContext context) { - var yellowBox = Container( - color: Colors.yellow, - height: 100, - width: 100, - ); - - var redBox = Container( - color: Colors.red, - height: 90, - width: 90, - ); - - var greenBox = Container( - color: Colors.green, - height: 80, - width: 80, - ); - - var 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/lib/views/widgets/StatelessWidget/PreferredSize/node1_base.dart b/lib/views/widgets/StatelessWidget/PreferredSize/node1_base.dart deleted file mode 100644 index 7b5257001..000000000 --- a/lib/views/widgets/StatelessWidget/PreferredSize/node1_base.dart +++ /dev/null @@ -1,27 +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 { - @override - Widget build(BuildContext context) { - return Container( - height: 200, - child: Scaffold( - appBar: PreferredSize( - preferredSize: Size.fromHeight(150), - child: AppBar( - title: Text('PreferredSize'), - ), - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/PreferredSize/node2_adapter.dart b/lib/views/widgets/StatelessWidget/PreferredSize/node2_adapter.dart deleted file mode 100644 index 60cee0a1a..000000000 --- a/lib/views/widgets/StatelessWidget/PreferredSize/node2_adapter.dart +++ /dev/null @@ -1,34 +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 { - @override - Widget build(BuildContext context) { - return Container( - height: 200, - child: Scaffold( - appBar: PreferredSize( - preferredSize: Size.fromHeight(150), - child: AppBar( - title: Text('PreferredSize'), - bottom: PreferredSize( - preferredSize: Size.fromHeight(40), - child: Container( - height: 40, - color: Colors.orange, - ), - ), - ), - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/RadioListTile/node1_base.dart b/lib/views/widgets/StatelessWidget/RadioListTile/node1_base.dart deleted file mode 100644 index 76879947a..000000000 --- a/lib/views/widgets/StatelessWidget/RadioListTile/node1_base.dart +++ /dev/null @@ -1,71 +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 { - @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"), - }; - var _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: (type) => setState(() => _type = type), - )) - .toList()), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/RadioListTile/node2_dense.dart b/lib/views/widgets/StatelessWidget/RadioListTile/node2_dense.dart deleted file mode 100644 index df7b7436f..000000000 --- a/lib/views/widgets/StatelessWidget/RadioListTile/node2_dense.dart +++ /dev/null @@ -1,68 +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 { - @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"), - }; - var _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: (type) => setState(() => _type = type), - )) - .toList()), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/RaisedButton/node1_base.dart b/lib/views/widgets/StatelessWidget/RaisedButton/node1_base.dart deleted file mode 100755 index ffa04444e..000000000 --- a/lib/views/widgets/StatelessWidget/RaisedButton/node1_base.dart +++ /dev/null @@ -1,34 +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 { - @override - Widget build(BuildContext context) { - return RaisedButton( - color: Colors.blue, - splashColor: Colors.green, - onPressed: () {}, - child: Text("RaisedButton"), - textColor: Color(0xffFfffff), - padding: EdgeInsets.all(8), - elevation: 5, - highlightColor: Color(0xffF88B0A), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/SafeArea/node1_base.dart b/lib/views/widgets/StatelessWidget/SafeArea/node1_base.dart deleted file mode 100644 index 370206737..000000000 --- a/lib/views/widgets/StatelessWidget/SafeArea/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 -/// 说明: 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 { - @override - Widget build(BuildContext context) { - return Container( - padding: EdgeInsets.all(10), - child: ElevatedButton( - onPressed: () { - Navigator.push( - context, - MaterialPageRoute(builder: (context) => SafeAreaPage()), - ); - }, - child: Text("进入 SafeArea 测试页"), - ), - ); - } -} - -class SafeAreaPage extends StatefulWidget { - @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: Text( - 'SafeArea 测试', - ), - ), - body: Column( - children: [ - ..._buildSlider(), - Expanded( - child: ListView.separated( - itemCount: 20, - separatorBuilder: (_, __) => Divider( - height: 1, - ), - itemBuilder: (_, index) => Container( - color: Colors.blue, - // padding: EdgeInsets.only(left: 20), - alignment: Alignment.center, - height: 50, - child: Text( - "第$index个", - style: 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/lib/views/widgets/StatelessWidget/ScrollView/node1_base.dart b/lib/views/widgets/StatelessWidget/ScrollView/node1_base.dart deleted file mode 100644 index 09e8ed6cf..000000000 --- a/lib/views/widgets/StatelessWidget/ScrollView/node1_base.dart +++ /dev/null @@ -1,115 +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 { - @override - Widget build(BuildContext context) { - return Container( - height: 300, - child: MyScrollView(), - ); - } -} - -class MyScrollView extends ScrollView { - 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: EdgeInsets.all(10), - margin: EdgeInsets.all(10), - child: Text(info), - ), - ), - _buildSliverFixedExtentList() - ]; - } - - _buildSliverAppBar() { - return SliverAppBar( - expandedHeight: 190.0, - leading: Container( - margin: EdgeInsets.all(10), - child: Image.asset('assets/images/icon_head.webp')), - flexibleSpace: FlexibleSpaceBar( - //伸展处布局 - titlePadding: EdgeInsets.only(left: 55, bottom: 15), //标题边距 - collapseMode: CollapseMode.parallax, //视差效果 - title: 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: 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/lib/views/widgets/StatelessWidget/SimpleDialog/node1_base.dart b/lib/views/widgets/StatelessWidget/SimpleDialog/node1_base.dart deleted file mode 100644 index f566d99f6..000000000 --- a/lib/views/widgets/StatelessWidget/SimpleDialog/node1_base.dart +++ /dev/null @@ -1,121 +0,0 @@ - -import 'package:flutter/cupertino.dart'; -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】 : 右下角组件列表 【背景色】\n" -// "【elevation】 : 右下角组件列表 【背景色】\n" -// "【shape】 : 影深 【double】", -// } -class CustomSimpleDialog extends StatelessWidget { - final info = [ - '性别: 男 未婚', - '微信: 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) => RaisedButton( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(10))), - color: Colors.blue, - onPressed: () { - showDialog(context: context, builder: (ctx) => _buildSimpleDialog(ctx)); - }, - child: Text( - 'Just Show It', - style: TextStyle(color: Colors.white), - ), - ); - - SimpleDialog _buildSimpleDialog(BuildContext context) { - return SimpleDialog( - title: _buildTitle(), - titlePadding: EdgeInsets.only( - top: 5, - left: 20, - ), - contentPadding: EdgeInsets.symmetric(horizontal: 5), - children: _buildChild(context), - backgroundColor: Colors.white, - elevation: 4, - shape: 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: Container( - width: double.infinity, - child: Text( - str, - style: 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, - ), - SizedBox( - width: 10, - ), - Expanded( - child: Text( - "张风捷特烈", - style: TextStyle(fontSize: 18), - )), - CloseButton() - ], - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/SimpleDialogOption/node1_base.dart b/lib/views/widgets/StatelessWidget/SimpleDialogOption/node1_base.dart deleted file mode 100644 index 321f27c1b..000000000 --- a/lib/views/widgets/StatelessWidget/SimpleDialogOption/node1_base.dart +++ /dev/null @@ -1,42 +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 { - @override - Widget build(BuildContext context) { - return Column( - children: [ - Container( - alignment: Alignment.center, - width: double.infinity, - height: 50, - margin: EdgeInsets.all(5), - color: Colors.grey.withAlpha(33), - child: SimpleDialogOption( - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - child: Text('张风捷特烈')), - ), - Container( - height: 50, - alignment: Alignment.center, - width: double.infinity, - color: Colors.grey.withAlpha(33), - margin: EdgeInsets.all(5), - child: SimpleDialogOption( - onPressed: () => Navigator.of(context).pushNamed('AboutMePage'), - child: Text('百里·巫缨')), - ), - ], - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/SingleChildScrollView/node1_base.dart b/lib/views/widgets/StatelessWidget/SingleChildScrollView/node1_base.dart deleted file mode 100644 index d6bc4fd27..000000000 --- a/lib/views/widgets/StatelessWidget/SingleChildScrollView/node1_base.dart +++ /dev/null @@ -1,59 +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 { - final 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 Container( - height: 200, - child: SingleChildScrollView( - padding: EdgeInsets.symmetric(horizontal: 10), - child: Column( - children: data - .map((color) => Container( - alignment: Alignment.center, - height: 50, - color: color, - child: Text( - colorString(color), - style: 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/lib/views/widgets/StatelessWidget/SingleChildScrollView/node2_direction.dart b/lib/views/widgets/StatelessWidget/SingleChildScrollView/node2_direction.dart deleted file mode 100644 index 77d9a0dc7..000000000 --- a/lib/views/widgets/StatelessWidget/SingleChildScrollView/node2_direction.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": 164, -// "name": 'SingleChildScrollView滑动方向', -// "priority": 2, -// "subtitle": -// "【scrollDirection】 : 滑动方向 【Axis】\n" -// "【reverse】 : 是否反向 【Axis】", -// } -class DirectionSingleChildScrollView extends StatelessWidget { - final 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 Container( - height: 200, - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - reverse: true, - padding: EdgeInsets.symmetric(horizontal: 10), - child: Row( - children: data - .map((color) => Container( - alignment: Alignment.center, - width: 90, - color: color, - child: Text( - colorString(color), - style: 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/lib/views/widgets/StatelessWidget/SnackBar/node1_base.dart b/lib/views/widgets/StatelessWidget/SnackBar/node1_base.dart deleted file mode 100644 index 5e6a0e9f0..000000000 --- a/lib/views/widgets/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 { - @override - _CustomSnackBarState createState() => _CustomSnackBarState(); -} - -class _CustomSnackBarState extends State { - @override - Widget build(BuildContext context) { - return Container( - child: FlatButton( - color: Colors.blue, - onPressed: () => - Scaffold.of(context).showSnackBar(_buildSnackBar()), - child: Text( - '点我弹出SnackBar', - style: TextStyle(color: Colors.white), - ))); - } - - Widget _buildSnackBar() { - return SnackBar( - elevation: 3, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(10), topRight: Radius.circular(10))), - content: Text('Wellcome to for join Flutter Unit!'), - duration: Duration(seconds: 3), - //持续时间 - backgroundColor: Colors.red, - onVisible: () => print('onVisible'), - action: SnackBarAction( - textColor: Colors.white, label: '确定', onPressed: () {}), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/SnackBarAction/node1_base.dart b/lib/views/widgets/StatelessWidget/SnackBarAction/node1_base.dart deleted file mode 100644 index f15db8813..000000000 --- a/lib/views/widgets/StatelessWidget/SnackBarAction/node1_base.dart +++ /dev/null @@ -1,26 +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 { - @override - Widget build(BuildContext context) { - return Container( - child: SnackBarAction( - disabledTextColor: Colors.red, - textColor: Colors.blue, - label: '确定', - onPressed: () => print('onPressed'))); - } -} diff --git a/lib/views/widgets/StatelessWidget/Spacer/node1_base.dart b/lib/views/widgets/StatelessWidget/Spacer/node1_base.dart deleted file mode 100644 index 1fa5e3b91..000000000 --- a/lib/views/widgets/StatelessWidget/Spacer/node1_base.dart +++ /dev/null @@ -1,38 +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 { - - @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, - ), - Spacer(), - Container( - alignment: Alignment.center, - width: 60, - height: 50, - color: Colors.blue, - ), - ],), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/Spacer/node2_flex.dart b/lib/views/widgets/StatelessWidget/Spacer/node2_flex.dart deleted file mode 100644 index 0f9004930..000000000 --- a/lib/views/widgets/StatelessWidget/Spacer/node2_flex.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": 107, -// "name": '多个Spacer空间分配', -// "priority": 2, -// "subtitle": -// "【flex】 : 剩余空间分配占比 【int】", -// } -class ManySpacer extends StatefulWidget { - @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/lib/views/widgets/StatelessWidget/SwitchListTile/node1_base.dart b/lib/views/widgets/StatelessWidget/SwitchListTile/node1_base.dart deleted file mode 100644 index 2a53db2dc..000000000 --- a/lib/views/widgets/StatelessWidget/SwitchListTile/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": 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 { - @override - _CustomSwitchListTileState createState() => _CustomSwitchListTileState(); -} - -class _CustomSwitchListTileState extends State { - var _value=false; - - @override - Widget build(BuildContext context) { - return Container( - margin: 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: Text("张风捷特烈"), - subtitle: Text("@万花过尽知无物"), - onChanged: (v) => setState(() => _value = !_value), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/SwitchListTile/node2_select.dart b/lib/views/widgets/StatelessWidget/SwitchListTile/node2_select.dart deleted file mode 100644 index 2b3a1cf71..000000000 --- a/lib/views/widgets/StatelessWidget/SwitchListTile/node2_select.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": 18, -// "priority": 2, -// "name": "SwitchListTile的选中效果", -// "subtitle": "【selected】: 是否选中 【bool】\n" -// "【inactiveThumbImage】: 未选中时圆圈图片 【ImageProvider】\n" -// "【activeThumbImage】: 选中时圆圈图片 【ImageProvider】", -// } -class SelectSwitchListTile extends StatefulWidget { - @override - _SelectSwitchListTileState createState() => _SelectSwitchListTileState(); -} - -class _SelectSwitchListTileState extends State { - var _value=false; - - @override - Widget build(BuildContext context) { - return Container( - margin: 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: AssetImage("assets/images/pica.gif"), - activeThumbImage: AssetImage("assets/images/icon_head.webp"), - title: Text("张风捷特烈"), - subtitle: Text("@万花过尽知无物"), - onChanged: (v) => setState(() => _value = !_value), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/SwitchListTile/node3_dense.dart b/lib/views/widgets/StatelessWidget/SwitchListTile/node3_dense.dart deleted file mode 100644 index 29d84b52f..000000000 --- a/lib/views/widgets/StatelessWidget/SwitchListTile/node3_dense.dart +++ /dev/null @@ -1,38 +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 { - @override - _DenseSwitchListTileState createState() => _DenseSwitchListTileState(); -} - -class _DenseSwitchListTileState extends State { - var _value=false; - - @override - Widget build(BuildContext context) { - return Container( - margin: 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: Text("张风捷特烈"), - subtitle: Text("@万花过尽知无物"), - onChanged: (v) => setState(() => _value = !_value), - ), - ); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/Tab/node1_base.dart b/lib/views/widgets/StatelessWidget/Tab/node1_base.dart deleted file mode 100644 index 3ebd4b058..000000000 --- a/lib/views/widgets/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 { - @override - Widget build(BuildContext context) { - return Container( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - 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/lib/views/widgets/StatelessWidget/TabBar/node1_base.dart b/lib/views/widgets/StatelessWidget/TabBar/node1_base.dart deleted file mode 100644 index f2b9cc00f..000000000 --- a/lib/views/widgets/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 { - @override - _CustomTabBarState createState() => _CustomTabBarState(); -} - -class _CustomTabBarState extends State - with SingleTickerProviderStateMixin { - final tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼']; - 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: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - unselectedLabelStyle: TextStyle(fontSize: 16), - isScrollable: true, - controller: _tabController, - labelColor: Colors.blue, - indicatorWeight: 3, - indicatorPadding: 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/lib/views/widgets/StatelessWidget/TabBar/node2_noShadow.dart b/lib/views/widgets/StatelessWidget/TabBar/node2_noShadow.dart deleted file mode 100644 index 281c3ea62..000000000 --- a/lib/views/widgets/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 { - @override - _NSTabBarState createState() => _NSTabBarState(); -} - -class _NSTabBarState extends State - with SingleTickerProviderStateMixin { - final tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼']; - 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: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), - unselectedLabelStyle: TextStyle(fontSize: 16), - isScrollable: true, - controller: _tabController, - labelColor: Colors.blue, - indicatorWeight: 3, - indicatorPadding: EdgeInsets.symmetric(horizontal: 10), - unselectedLabelColor: Colors.grey, - indicatorColor: Colors.orangeAccent, - tabs: tabs.map((e) => Tab(text: e)).toList(), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/TabPageSelector/node1_base.dart b/lib/views/widgets/StatelessWidget/TabPageSelector/node1_base.dart deleted file mode 100644 index dfddfccc5..000000000 --- a/lib/views/widgets/StatelessWidget/TabPageSelector/node1_base.dart +++ /dev/null @@ -1,72 +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 { - @override - _TabPageSelectorDemoState createState() => _TabPageSelectorDemoState(); -} - -class _TabPageSelectorDemoState extends State - with SingleTickerProviderStateMixin { - final tabs = ['风画庭', '雨韵舍', '雷鸣殿', '电疾堂', '霜寒阁', '雪月楼']; - 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 Container( - 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: TextStyle(color: Colors.white, fontSize: 20), - ))) - .toList()); -} diff --git a/lib/views/widgets/StatelessWidget/TabPageSelectorIndicator/node1_base.dart b/lib/views/widgets/StatelessWidget/TabPageSelectorIndicator/node1_base.dart deleted file mode 100644 index fa9403429..000000000 --- a/lib/views/widgets/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 { - @override - Widget build(BuildContext context) { - return Container( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - 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/lib/views/widgets/StatelessWidget/Text/node1_base.dart b/lib/views/widgets/StatelessWidget/Text/node1_base.dart deleted file mode 100644 index c49082dbb..000000000 --- a/lib/views/widgets/StatelessWidget/Text/node1_base.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": 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 { - @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/lib/views/widgets/StatelessWidget/Text/node2_shadows.dart b/lib/views/widgets/StatelessWidget/Text/node2_shadows.dart deleted file mode 100644 index 6770a0981..000000000 --- a/lib/views/widgets/StatelessWidget/Text/node2_shadows.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": 2, -// "name": "文字阴影", -// "subtitle": "【shadows】 : 文字 【List】\n" -// "【backgroundColor】: 背景颜色 【Color】", -// } -class ShadowText extends StatelessWidget { - @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/lib/views/widgets/StatelessWidget/Text/node3_decoration.dart b/lib/views/widgets/StatelessWidget/Text/node3_decoration.dart deleted file mode 100644 index bf6d736e1..000000000 --- a/lib/views/widgets/StatelessWidget/Text/node3_decoration.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": 2, -// "priority": 3, -// "name": "文字装饰线", -// "subtitle": "【fontFamily】 : 文字字体 【String】\n" -// "【decoration】: 装饰线 【TextDecoration】\n" -// "【decorationColor】: 装饰线颜色 【Color】\n" -// "【decorationThickness】: 装饰线粗 【double】\n" -// "【decorationStyle】: 装饰线样式 【TextDecorationStyle】", -// } -class DecorationText extends StatelessWidget { - @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/lib/views/widgets/StatelessWidget/Text/node4_textAlign.dart b/lib/views/widgets/StatelessWidget/Text/node4_textAlign.dart deleted file mode 100644 index 44bd4b53c..000000000 --- a/lib/views/widgets/StatelessWidget/Text/node4_textAlign.dart +++ /dev/null @@ -1,34 +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 { - @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/lib/views/widgets/StatelessWidget/Text/node5_textDirection.dart b/lib/views/widgets/StatelessWidget/Text/node5_textDirection.dart deleted file mode 100644 index dd301de96..000000000 --- a/lib/views/widgets/StatelessWidget/Text/node5_textDirection.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": 5, -// "name": "文字方向与最大行数", -// "subtitle": "【maxLines】 : 最大行数 【int】\n" -// "【textDirection】 : 文字方向 【TextDirection】\n" -// "下面依次是:rtl、ltr ", -// } -class TextDirectionText extends StatelessWidget { - @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/lib/views/widgets/StatelessWidget/Text/node6_softWrap.dart b/lib/views/widgets/StatelessWidget/Text/node6_softWrap.dart deleted file mode 100644 index abbe4e09f..000000000 --- a/lib/views/widgets/StatelessWidget/Text/node6_softWrap.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": 6, -// "name": "是否包裹与越界效果", -// "subtitle": "【softWrap】 : 是否换行 【bool】\n" -// "【overflow】 : 越界效果 【TextOverflow】\n" -// "下面softWrap=false; overflow依次是:clip、fade、ellipsis、visible ", -// } -class SoftWrapText extends StatelessWidget { - @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/lib/views/widgets/StatelessWidget/Theme/node1_base.dart b/lib/views/widgets/StatelessWidget/Theme/node1_base.dart deleted file mode 100644 index 5ca9fe944..000000000 --- a/lib/views/widgets/StatelessWidget/Theme/node1_base.dart +++ /dev/null @@ -1,65 +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 { - @override - Widget build(BuildContext context) { - TextTheme queryData = Theme.of(context).textTheme; - Map styles = { - "headline: ": queryData.headline, - "title: ": queryData.title, - "subhead: ": queryData.subhead, - "body1: ": queryData.body1, - "body2: ": queryData.body2, - "button: ": queryData.button, - "overline: ": queryData.overline, - "subtitle: ": queryData.subtitle, - "caption: ": queryData.caption, - "display1: ": queryData.display1, - "display2: ": queryData.display2, - "display3: ": queryData.display3, - "display4: ": queryData.display4, - }; - - return Container( - child: Column( - children: styles.keys - .map((String styleInfo) => buildItem(styleInfo, styles[styleInfo])) - .toList(), - ), - ); - } - - 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(fontSize: 16, fontWeight: FontWeight.bold), - ), - Text( - "@toly", - style: style, - ) - ], - ), - ), - const Divider( - height: 1, - ) - ], - ); -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/Theme/node2_use.dart b/lib/views/widgets/StatelessWidget/Theme/node2_use.dart deleted file mode 100644 index 6944cd5c5..000000000 --- a/lib/views/widgets/StatelessWidget/Theme/node2_use.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": 168, -// "name": 'Theme的用法', -// "priority": 2, -// "subtitle": -// "使用Theme,可以指定非常多的属性作为主题,这些属性将应用于所有的后代组件,如指定字体、滑块、卡片、文字、分割线、按钮等属性。", -// } -class CustomTheme extends StatelessWidget { - @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, - ), - ), - Container( - width: 150, - child: Slider(value: 0.8, onChanged: null), - ), - Container( - width: 150, - child: const Divider(), - ) - ])); - } -} \ No newline at end of file diff --git a/lib/views/widgets/StatelessWidget/Title/node1_base.dart b/lib/views/widgets/StatelessWidget/Title/node1_base.dart deleted file mode 100644 index 35ffab2a0..000000000 --- a/lib/views/widgets/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 { - @override - Widget build(BuildContext context) { - return Container( - child: Title( - title: '张风捷特烈', - color: const Color(0xFF9C27B0), - child: const Center( - child: Text('应用菜单栏中该页的名称为: 张风捷特烈'), - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/ToggleButtons/node1_single.dart b/lib/views/widgets/StatelessWidget/ToggleButtons/node1_single.dart deleted file mode 100644 index a66cdfdbb..000000000 --- a/lib/views/widgets/StatelessWidget/ToggleButtons/node1_single.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": 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 { - @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/lib/views/widgets/StatelessWidget/ToggleButtons/node2_color.dart b/lib/views/widgets/StatelessWidget/ToggleButtons/node2_color.dart deleted file mode 100644 index 697469fe0..000000000 --- a/lib/views/widgets/StatelessWidget/ToggleButtons/node2_color.dart +++ /dev/null @@ -1,49 +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 { - @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/lib/views/widgets/StatelessWidget/ToggleButtons/node3_multi.dart b/lib/views/widgets/StatelessWidget/ToggleButtons/node3_multi.dart deleted file mode 100644 index 95d2b976f..000000000 --- a/lib/views/widgets/StatelessWidget/ToggleButtons/node3_multi.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": 33, -// "priority": 3, -// "name": "ToggleButtons多选切换", -// "subtitle": " 可以控制状态转化的逻辑来形成不同的效果。", -// } -class ProToggleButtons extends StatefulWidget { - @override - _ProToggleButtonsState createState() => _ProToggleButtonsState(); -} - -class _ProToggleButtonsState extends State { - 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/lib/views/widgets/StatelessWidget/UserAccountsDrawerHeader/node1_base.dart b/lib/views/widgets/StatelessWidget/UserAccountsDrawerHeader/node1_base.dart deleted file mode 100644 index 3e77bafb6..000000000 --- a/lib/views/widgets/StatelessWidget/UserAccountsDrawerHeader/node1_base.dart +++ /dev/null @@ -1,56 +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 { - @override - Widget build(BuildContext context) { - return Container( - width: MediaQuery.of(context).size.width / 3 * 2, - child: UserAccountsDrawerHeader( - accountName: Container( - padding: const EdgeInsets.all(8.0), - child: Text( - "张风捷特烈", - style: - TextStyle(color: Colors.orangeAccent, fontSize: 22, shadows: [ - Shadow( - color: Colors.black, offset: Offset(.5, .5), blurRadius: 2), - ]), - ), - ), - accountEmail: Padding( - padding: const 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: CircleAvatar( - backgroundImage: AssetImage("assets/images/icon_head.webp"), - ), - ), - decoration: BoxDecoration( - image: DecorationImage(image: AssetImage("assets/images/caver.webp")), - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/UserAccountsDrawerHeader/node2_pro.dart b/lib/views/widgets/StatelessWidget/UserAccountsDrawerHeader/node2_pro.dart deleted file mode 100644 index 54669d382..000000000 --- a/lib/views/widgets/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 { - @override - Widget build(BuildContext context) { - return Container( - width: MediaQuery.of(context).size.width / 3 * 2, - child: UserAccountsDrawerHeader( - margin: EdgeInsets.all(10), - accountName: Container( - padding: const EdgeInsets.all(8.0), - child: Text( - "张风捷特烈", - style: - TextStyle(color: Colors.orangeAccent, fontSize: 22, - shadows: [ - Shadow( - color: Colors.black, - offset: Offset(.5, .5), - blurRadius: 2), - ]), - ), - ), - accountEmail: Padding( - padding: const 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: CircleAvatar( - backgroundImage: AssetImage("assets/images/icon_head.webp"), - ), - ), - otherAccountsPictures: [ - FlutterLogo(textColor: Colors.green), - ], - onDetailsPressed: () { - - }, - arrowColor: Colors.white, - decoration: BoxDecoration( - image: DecorationImage( - image: AssetImage("assets/images/caver.webp")), - ), - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/VerticalDivider/node1_base.dart b/lib/views/widgets/StatelessWidget/VerticalDivider/node1_base.dart deleted file mode 100644 index 28e0f16ae..000000000 --- a/lib/views/widgets/StatelessWidget/VerticalDivider/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": 35, -// "priority": 1, -// "name": "VerticalDivider颜色和粗细", -// "subtitle": "【color】: 颜色 【Color】\n" -// "【thickness】: 线粗细 【double】", -// } -class CustomVerticalDivider extends StatelessWidget { - @override - Widget build(BuildContext context) { - var dataColor = [ - Colors.red, Colors.yellow, - Colors.blue, Colors.green]; - var dataThickness = [1.0, 2.0, 4.0, 6.0]; - var data = Map.fromIterables(dataColor, dataThickness); - return Container( - 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/lib/views/widgets/StatelessWidget/VerticalDivider/node2_height.dart b/lib/views/widgets/StatelessWidget/VerticalDivider/node2_height.dart deleted file mode 100644 index 87c6843eb..000000000 --- a/lib/views/widgets/StatelessWidget/VerticalDivider/node2_height.dart +++ /dev/null @@ -1,41 +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" -// "【height】: 占位高 【double】", -// } -class HeightVerticalDivider extends StatelessWidget { - @override - Widget build(BuildContext context) { - var dataColor = [ - Colors.red, Colors.yellow, - Colors.blue, Colors.green]; - var dataThickness = [10.0, 20.0, 30.0, 40.0]; - var data = Map.fromIterables(dataColor, dataThickness); - return Container( - 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/lib/views/widgets/StatelessWidget/Visibility/node1_base.dart b/lib/views/widgets/StatelessWidget/Visibility/node1_base.dart deleted file mode 100644 index 232981ba6..000000000 --- a/lib/views/widgets/StatelessWidget/Visibility/node1_base.dart +++ /dev/null @@ -1,57 +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 { - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: [ - _buildVisibility(true), - _buildVisibility(false), - ], - ); - } - - _buildVisibility(bool visible) { - var 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: Text( - "visible\ntrue", - style: TextStyle(fontSize: 20), - ), - )), - box, - ], - ), - ); - } -} diff --git a/lib/views/widgets/StatelessWidget/Visibility/node2_replacement.dart b/lib/views/widgets/StatelessWidget/Visibility/node2_replacement.dart deleted file mode 100644 index 4c083cf4e..000000000 --- a/lib/views/widgets/StatelessWidget/Visibility/node2_replacement.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": 10, -// "priority": 2, -// "name": "replacement可在隐藏时进行占位", -// "subtitle": "【replacement】 : 隐藏时的占位组件 【Widget】", -// } -class ReplacementVisibility extends StatelessWidget { - @override - Widget build(BuildContext context) { - return Wrap( - spacing: 10, - children: [ - _buildVisibility(true), - _buildVisibility(false), - ], - ); - } - - _buildVisibility(bool visible) { - var 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: Text( - "visible\ntrue", - style: TextStyle(fontSize: 20), - ), - )), - box, - ], - ), - ); - } -} diff --git a/lib/views/widgets/exp/other_unit.dart b/lib/views/widgets/exp/other_unit.dart deleted file mode 100644 index 8309e77f3..000000000 --- a/lib/views/widgets/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/lib/views/widgets/exp/proxy_unit.dart b/lib/views/widgets/exp/proxy_unit.dart deleted file mode 100644 index fdc1ec8ac..000000000 --- a/lib/views/widgets/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/lib/views/widgets/exp/render_object_unit.dart b/lib/views/widgets/exp/render_object_unit.dart deleted file mode 100644 index 9ac215437..000000000 --- a/lib/views/widgets/exp/render_object_unit.dart +++ /dev/null @@ -1,97 +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'; \ No newline at end of file diff --git a/lib/views/widgets/exp/sliver_unit.dart b/lib/views/widgets/exp/sliver_unit.dart deleted file mode 100644 index 290cb253c..000000000 --- a/lib/views/widgets/exp/sliver_unit.dart +++ /dev/null @@ -1,27 +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'; diff --git a/lib/views/widgets/exp/stateful_unit.dart b/lib/views/widgets/exp/stateful_unit.dart deleted file mode 100644 index 96ec68e71..000000000 --- a/lib/views/widgets/exp/stateful_unit.dart +++ /dev/null @@ -1,175 +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/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' hide HomePage; - -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'; \ No newline at end of file diff --git a/lib/views/widgets/exp/stateless_unit.dart b/lib/views/widgets/exp/stateless_unit.dart deleted file mode 100644 index c5e4f0a9f..000000000 --- a/lib/views/widgets/exp/stateless_unit.dart +++ /dev/null @@ -1,197 +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/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/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/UserAccountsDrawerHeader/node2_pro.dart'; -export '../StatelessWidget/VerticalDivider/node2_height.dart'; - -export '../StatelessWidget/BackButton/node1_base.dart'; -export '../StatelessWidget/ButtonBar/node1_base.dart'; -export '../StatelessWidget/ButtonBar/node2_padding.dart'; -export '../StatelessWidget/CloseButton/node1_base.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/GridPaper/node1_base.dart'; -export '../StatelessWidget/GridPaper/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'; - - diff --git a/lib/views/widgets/widgets_map.dart b/lib/views/widgets/widgets_map.dart deleted file mode 100644 index 900dd56ac..000000000 --- a/lib/views/widgets/widgets_map.dart +++ /dev/null @@ -1,1366 +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 [ - CustomContainer(), - ContainerWithChild(), - ContainerAlignment(), - ContainerDecoration(), - ContainerTransform(), - ContainerConstraints() - ]; - case "CupertinoSegmentedControl": - return [ - CupertinoSegmentedControlDemo(), - CupertinoSegmentedControlColor() - ]; - case "CompositedTransformTarget": - return [ - const CompositedTransformTargetDemo(), - ]; - case "CompositedTransformFollower": - return [ - const CompositedTransformFollowerDemo(), - ]; case "PrimaryScrollController": - return [ - PrimaryScrollControllerDemo(), - ];case "CupertinoFullscreenDialogTransition": - return [ - CupertinoFullscreenDialogTransitionDemo(), - ];case "CupertinoPageTransition": - return [ - CupertinoPageTransitionDemo(), - ];case "HtmlElementView": - return [ - HtmlElementViewDemo(), - ]; - case "Text": - return [ - CustomText(), - ShadowText(), - DecorationText(), - TextAlignText(), - TextDirectionText(), - SoftWrapText() - ]; - case "Card": - return [ - CustomCard(), - ShapeCard(), - ]; - case "ElevatedButton": - return [ - ElevatedButtonDemo(), - ElevatedButtonStyleDemo(), - ]; - case "TextButton": - return [ - TextButtonDemo(), - TextButtonStyleDemo(), - ]; - case "OutlinedButton": - return [ - OutlinedButtonDemo(), - OutlinedButtonStyleDemo(), - ]; - case "FlutterLogo": - return [ - CustomFlutterLogo(), - FlutterLogoWithText(), - ]; - case "Banner": - return [ - CustomBanner(), - ]; - case "UniqueWidget": - return [ - UniqueWidgetDemo(), - ]; - case "ShrinkWrappingViewport": - return [ - ShrinkWrappingViewportDemo(), - ]; case "NestedScrollViewViewport": - return [ - NestedScrollViewViewportDemo(), - ]; - case "ParentDataWidget": - return [ - ParentDataWidgetDemo(), - ]; - case "AutomaticKeepAlive": - return [ - AutomaticKeepAliveDemo(), - ]; - case "StatusTransitionWidget": - return [ - StatusTransitionWidgetDemo(), - ]; - case "KeepAlive": - return [ - KeepAliveDemo(), - ]; - case "ListWheelViewport": - return [ - ListWheelViewportDemo(), - ListWheelViewportDemo2(), - ListWheelViewportDemo3(), - ListWheelViewportDemo4(), - ]; - case "InheritedTheme": - return [ - InheritedThemeDemo(), - ]; - case "ModalBarrier": - return [ - ModalBarrierDemo(), - ]; - case "AnimatedModalBarrier": - return [ - AnimatedModalBarrierDemo(), - ]; - case "DefaultAssetBundle": - return [ - DefaultAssetBundleDemo(), - ]; - case "DropdownButtonFormField": - return [ - DropdownButtonFormFieldDemo(), - ]; - case "FormField": - return [ - FormFieldDemo(), - ]; - case "PaginatedDataTable": - return [ - PaginatedDataTableDemo(), - ]; - case "InheritedWidget": - return [ - InheritedWidgetDemo(), - ]; - case "ScrollView": - return [ - ScrollViewDemo(), - ]; - case "SliverWithKeepAliveWidget": - return [ - SliverWithKeepAliveWidgetDemo(), - ]; - case "BoxScrollView": - return [ - BoxScrollViewDemo(), - ]; - case "CupertinoUserInterfaceLevel": - return [ - CupertinoUserInterfaceLevelDemo(), - ]; - case "CupertinoSliverNavigationBar": - return [ - CupertinoSliverNavigationBarDemo(), - ]; - case "CupertinoSliverRefreshControl": - return [ - CupertinoSliverRefreshControlDemo(), - ]; - case "SliverAnimatedList": - return [ - SliverAnimatedListDemo(), - ]; - case "SliverFillRemaining": - return [ - SliverFillRemainingDemo(), - ]; - case "SliverIgnorePointer": - return [ - SliverIgnorePointerDemo(), - ]; - case "Viewport": - return [ - ViewportDemo(), - ]; - case "CupertinoPopupSurface": - return [ - CupertinoPopupSurfaceDemo(), - ]; - case "RenderObjectToWidgetAdapter": - return [ - RenderObjectToWidgetAdapterDemo(), - ]; - case "SliverPrototypeExtentList": - return [ - SliverPrototypeExtentListDemo(), - ]; - case "InputDecorator": - return [ - InputDecoratorDemo(), - ]; - case "ColoredBox": - return [ - ColoredBoxDemo(), - ]; - case "StatefulBuilder": - return [ - StatefulBuilderDemo(), - ]; - case "RawKeyboardListener": - return [ - RawKeyboardListenerDemo(), - ]; - case "RawGestureDetector": - return [ - RawGestureDetectorDemo(), - ]; - case "SafeArea": - return [ - SafeAreaDemo(), - ]; - case "AnimatedBuilder": - return [ - AnimatedBuilderDemo(), - ]; - case "TweenAnimationBuilder": - return [ - TweenAnimationBuilderDemo(), - ]; - case "WidgetInspector": - return [ - WidgetInspectorDemo(), - ]; - case "PageStorage": - return [ - PageStorageDemo(), - ]; - case "NotificationListener": - return [NotificationListenerDemo(), NotificationListenerUpdate()]; - - case "Scrollable": - return [ - ScrollableDemo(), - ]; - case "PhysicalModel": - return [ - PhysicalModelDemo(), - ]; - case "Directionality": - return [ - DirectionalityDemo(), - ]; - case "AnimatedTheme": - return [ - AnimatedThemeDemo(), - ]; - case "CupertinoSlidingSegmentedControl": - return [ - CupertinoSlidingSegmentedControlDemo(), - ]; - case "WidgetsApp": - return [ - WidgetsAppDemo(), - ]; - case "TableCell": - return [ - TableCellDemo(), - ]; - - case "EditableText": - return [ - EditableTextDemo(), - ]; - case "PhysicalShape": - return [ - PhysicalShapeDemo(), - ]; - case "ListBody": - return [ - ListBodyDemo(), - ]; - case "FractionalTranslation": - return [ - FractionalTranslationDemo(), - ]; - case "MouseRegion": - return [ - MouseRegionDemo(), - ]; - case "TabPageSelector": - return [ - TabPageSelectorDemo(), - ]; - case "CupertinoNavigationBarBackButton": - return [ - CupertinoNavigationBarBackButtonDemo(), - ]; - case "TabPageSelectorIndicator": - return [ - TabPageSelectorIndicatorDemo(), - ]; - case "Title": - return [ - TitleDemo(), - ]; - case "ToggleButtonsTheme": - return [ - ToggleButtonsThemeDemo(), - ]; - case "PerformanceOverlay": - return [ - PerformanceOverlayDemo(), - ]; - case "NavigationToolbar": - return [ - NavigationToolbarDemo(), - ]; - case "CupertinoDialogAction": - return [ - CupertinoDialogActionDemo(), - ]; - case "ButtonBarTheme": - return [ - ButtonBarThemeDemo(), - ]; - case "RawImage": - return [ - RawImageDemo(), - ]; - case "TooltipTheme": - return [ - TooltipThemeDemo(), - ]; - case "CupertinoTextField": - return [ - CupertinoTextFieldDemo(), - CupertinoTextFieldStyle(), - ]; - case "MaterialBanner": - return [ - MaterialBannerDemo(), - MaterialBannerDemoTwo(), - ]; - case "OrientationBuilder": - return [ - OrientationBuilderDemo(), - ]; - case "Icon": - return [ - CustomIcon(), - MyIcon(), - ]; - case "ImageIcon": - return [ - CustomImageIcon(), - ]; - case "FadeInImage": - return [ - CustomFadeInImage(), - ]; - case "InteractiveViewer": - return [ - InteractiveViewerDemo(), - InteractiveViewerDemo2(), - InteractiveViewerDemo3(), - ]; - case "CircleAvatar": - return [ - CustomCircleAvatar(), - ]; - case "SliverLayoutBuilder": - return [ - SliverLayoutBuilderDemo(), - ]; - case "Visibility": - return [ - CustomVisibility(), - ReplacementVisibility(), - ]; - case "RepaintBoundary": - return [ - RepaintBoundaryDemo(), - RepaintBoundarySave(), - ]; - case "Chip": - return [ - CustomChip(), - ColorOfChip(), - DeleteOfChip(), - ]; - case "ChoiceChip": - return [CustomChoiceChip()]; - case "ListTileTheme": - return [ListTileThemeDemo()]; - case "ValueListenableBuilder": - return [ValueListenableBuilderDemo()]; - case "ActionChip": - return [CustomActionChip()]; - case "MaterialBannerTheme": - return [MaterialBannerThemeDemo()]; - case "PopupMenuTheme": - return [PopupMenuThemeDemo()]; - case "InputChip": - return [PressInputChip(), SelectInputChip()]; - case "FilterChip": - return [CustomFilterChip()]; - case "IntrinsicHeight": - return [IntrinsicHeightDemo()]; - case "IntrinsicWidth": - return [IntrinsicWidthDemo()]; - case "ChipTheme": - return [ChipThemeDemo()]; - case "ListTile": - return [CustomListTile(), SelectListTile(), DenseListTile()]; - case "CheckboxListTile": - return [ - CustomCheckBoxListTile(), - SelectCheckBoxListTile(), - DenseCheckBoxListTile() - ]; - case "SwitchListTile": - return [ - CustomSwitchListTile(), - SelectSwitchListTile(), - DenseSwitchListTile() - ]; - - case "RadioListTile": - return [ - CustomRadioListTile(), - DenseRadioListTile(), - ]; - - case "GridTileBar": - return [ - CustomGridTileBar(), - ]; - - case "GridTile": - return [ - CustomGridTile(), - ]; - case "UserAccountsDrawerHeader": - return [ - CustomUAGHP(), - ProUAGHP(), - ]; - - case "MaterialButton": - return [ - CustomMaterialButton(), - LongPressMaterialButton(), - ShapeMaterialButton(), - ]; - case "CupertinoButton": - return [CustomCupertinoButton()]; - case "FlatButton": - return [CustomFlatButton()]; - case "RaisedButton": - return [CustomRaisedButton()]; - case "OutlineButton": - return [CustomOutlineButton()]; - case "FloatingActionButton": - return [CustomFAB(), MiniFAB(), ShapeFAB()]; - - case "ButtonBar": - return [ - CustomButtonBar(), - PaddingButtonBar(), - ]; - - case "IconButton": - return [ - CustomIconButton(), - ]; - case "BackButton": - return [ - CustomBackButton(), - ]; - - case "CloseButton": - return [ - CustomCloseButton(), - ]; - case "ToggleButtons": - return [ - CustomToggleButtons(), - ColorToggleButtons(), - ProToggleButtons(), - ]; - case "ToggleButtons": - return [ - CustomToggleButtons(), - ColorToggleButtons(), - ProToggleButtons(), - ]; - case "NestedScrollView": - return [ - NestedScrollViewDemo(), - ]; - case "SliverOverlapAbsorber": - return [ - SliverOverlapAbsorberDemo(), - ]; - case "SliverOverlapInjector": - return [ - SliverOverlapInjectorDemo(), - ]; - case "Divider": - return [ - CustomDivider(), - HeightDivider(), - ]; - case "VerticalDivider": - return [ - CustomVerticalDivider(), - HeightVerticalDivider(), - ]; - case "Placeholder": - return [ - CustomPlaceholder(), - FallbackPlaceholder(), - ]; - case "GridPager": - return [ - CustomGridPage(), - DivisionsGridPage(), - ]; - case "Image": - return [ - LoadImage(), - FitImage(), - AlignmentImage(), - BlendModeImage(), - RepeatImage(), - CenterSliceImage(), - ]; - case "Checkbox": - return [ - CustomCheckbox(), - TristateCheckBok(), - ]; - case "Switch": - return [ - CustomSwitch(), - ImageSwitch(), - ]; - case "CupertinoSwitch": - return [ - CustomCupertinoSwitch(), - ]; - case "Slider": - return [ - CustomSlider(), - DivisionsSlider(), - ]; - case "CupertinoSlider": - return [ - CustomCupertinoSlider(), - ]; - case "RangeSlider": - return [ - CustomRangeSlider(), - ]; - case "Radio": - return [ - CustomRadio(), - ]; - case "CustomMultiChildLayout": - return [ - CustomMultiChildLayoutDemo(), - ]; - case "LayoutId": - return [ - LayoutIdDemo(), - ]; - case "CircularProgressIndicator": - return [ - CustomCircularProgressIndicator(), - ]; - case "LinearProgressIndicator": - return [ - CustomLinearProgressIndicator(), - ]; - case "CupertinoActivityIndicator": - return [ - CustomCupertinoActivityIndicator(), - ]; - case "RefreshIndicator": - return [ - CustomRefreshIndicator(), - ]; - case "Tooltip": - return [ - CustomTooltip(), - DecorationTooltip(), - ]; - case "ExpandIcon": - return [ - CustomExpandIcon(), - ]; - case "ExpansionTile": - return [ - CustomExpansionTile(), - ]; - - case "AnnotatedRegion": - return [ - AnnotatedRegionDemo(), - ]; - case "CheckedModeBanner": - return [ - CheckedModeBannerDemo(), - ]; - case "DefaultTabController": - return [ - DefaultTabControllerDemo(), - ]; - case "CupertinoTabView": - return [ - CupertinoTabViewDemo(), - ]; - case "CupertinoTextSelectionToolbar": - return [ - CupertinoTextSelectionToolbarDemo(), - ]; - case "DraggableScrollableActuator": - return [ - DraggableScrollableActuatorDemo(), - ]; - case "GlowingOverscrollIndicator": - return [ - GlowingOverscrollIndicatorDemo(), - ]; - case "DraggableScrollableSheet": - return [ - DraggableScrollableSheetDemo(), - ]; - case "DrawerController": - return [ - DrawerControllerDemo(), - ]; - case "MergeableMaterial": - return [ - MergeableMaterialDemo(), - ]; - case "SizeChangedLayoutNotifier": - return [ - SizeChangedLayoutNotifierDemo(), - ]; - case "SelectableText": - return [ - CustomSelectableText(), - AlignSelectableText(), - ]; - case "TextField": - return [ - CustomTextField(), - CursorTextField(), - ComplexTextField(), - ]; - case "DropdownButton": - return [ - CustomDropDownButton(), - StyleDropDownButton(), - ]; - case "PopupMenuButton": - return [ - CustomPopupMenuButton(), - ]; - case "AppBar": - return [ - CustomAppBar(), - TabAppBar(), - ]; - case "TabBar": - return [ - CustomTabBar(), - NoShadowTabBarDemo(), - ]; - case "TabBarView": - return [ - CustomTabBarView(), - ]; - case "BottomNavigationBar": - return [CustomBottomNavigationBar(), BottomNavigationBarWithPageView()]; - case "BottomAppBar": - return [ - CustomBottomAppBar(), - ]; - case "CupertinoNavigationBar": - return [ - CustomCupertinoNavigationBar(), - ]; - case "CupertinoTabBar": - return [ - CustomCupertinoTabBar(), - ]; - case "Scaffold": - return [ - CustomScaffold(), - ]; - case "MaterialApp": - return [ - MaterialAppDemo(), - ]; - case "ClipOval": - return [ - CustomClipOval(), - ]; - case "ClipRect": - return [ - CustomClipRect(), - ]; - case "ClipRRect": - return [ - CustomClipRRect(), - ]; - case "ClipPath": - return [ - CustomClipPath(), - ]; - case "DecoratedBox": - return [ - BoxDecorationDemo(), - ShapeImageDemo(), - BorderDemo(), - ShapeDecorationDemo(), - UnderlineTabIndicatorDemo(), - FlutterLogoDecorationDemo(), - ]; - case "Offstage": - return [ - CustomOffstage(), - ]; - case "RotatedBox": - return [ - CustomRotatedBox(), - ]; - case "Opacity": - return [ - CustomOpacity(), - ]; - case "Padding": - return [ - PaddingAll(), - PaddingOnly(), - PaddingSymmetric(), - ]; - case "Baseline": - return [ - CustomBaseline(), - ]; - case "SizedBox": - return [ - CustomSizedBox(), - ]; - case "AspectRatio": - return [ - CustomAspectRatio(), - ]; - case "Transform": - return [ - SkewTransform(), - TranslationTransform(), - ScaleTransform(), - RotateTransform(), - R3C2(), - ]; - case "LimitedBox": - return [ - CustomLimitedBox(), - ]; - case "ConstrainedBox": - return [ - CustomConstrainedBox(), - ]; - case "UnconstrainedBox": - return [ - CustomUnConstrainedBox(), - ]; - case "FractionallySizedBox": - return [ - CustomFractionallySizedBox(), - ]; - case "OverflowBox": - return [ - CustomOverflowBox(), - ]; - case "SizedOverflowBox": - return [ - CustomSizedOverflowBox(), - ]; - case "Align": - return [CustomAlign(), SinLayout()]; - case "Center": - return [ - CustomCenter(), - ]; - case "FittedBox": - return [ - CustomFittedBox(), - ]; - case "ColorFiltered": - return [ - CustomColorFiltered(), - ]; - case "FadeTransition": - return [ - CustomFadeTransition(), - ]; - case "RotationTransition": - return [ - CustomRotationTransition(), - ]; - case "ScaleTransition": - return [ - CustomScaleTransition(), - ]; - case "SizeTransition": - return [ - CustomSizeTransition(), - ]; - case "PositionedTransition": - return [ - CustomPositionedTransition(), - ]; - case "Flex": - return [ - DirectionFlex(), - MainAxisAlignmentFlex(), - CrossAxisAlignmentFlex(), - VerticalDirectionFlex(), - TextDirectionFlex(), - ]; - case "Row": - return [ - CustomRow(), - ]; - case "Column": - return [ - CustomColumn(), - ]; - case "Stack": - return [CustomStack(), PositionedStack()]; - case "Wrap": - return [ - DirectionWrap(), - WrapAlignmentWrap(), - CrossAxisAlignmentWrap(), - TextDirectionWrap(), - VerticalDirectionWrap(), - ]; - case "Flow": - return [ - CircleFlow(), - BurstFlow.show, - ]; - case "AnimatedCrossFade": - return [ - CustomAnimatedCrossFade(), - CurveAnimatedCrossFade(), - ]; - case "RichText": - return [ - CustomRichText(), - RichTextWithWidget(), - ]; - case "DataTable": - return [ - CustomDataTable(), - SortDataTable(), - ]; - case "Draggable": - return [ - CustomDraggable(), - DraggablePage(), - DeleteDraggable(), - ]; - case "DragTarget": - return [ - CustomDragTarget(), - ]; - case "LongPressDraggable": - return [ - CustomLongPressDraggable(), - ]; - case "Expanded": - return [ - CustomExpended(), - ]; - case "Spacer": - return [ - OneSpacer(), - ManySpacer(), - ]; - case "Positioned": - return [ - CustomPositioned(), - ]; - case "Flexible": - return [ - CustomFlexible(), - ]; - case "Table": - return [ - CustomTable(), - ]; - case "AlignTransition": - return [ - CustomAlignTransition(), - ]; - case "SlideTransition": - return [ - CustomSlideTransition(), - ]; - case "DecoratedBoxTransition": - return [ - CustomDecoratedBoxTransition(), - ]; - case "DefaultTextStyleTransition": - return [ - CustomDefaultTextStyleTransition(), - ]; - case "RelativePositionedTransition": - return [ - CustomRelativePositionedTransition(), - ]; - case "AnimatedSwitcher": - return [ - CustomAnimatedSwitcher(), - ]; - case "AnimatedList": - return [ - CustomAnimatedList(), - ]; - case "AnimatedOpacity": - return [ - CustomAnimatedOpacity(), - ]; - case "AnimatedPadding": - return [ - CustomAnimatedPadding(), - ]; - case "AnimatedAlign": - return [ - CustomAnimatedAlign(), - ]; - case "AnimatedPositioned": - return [ - CustomAnimatedPositioned(), - ]; - case "AnimatedPositionedDirectional": - return [ - CustomAnimatedPositionedDirectional(), - ]; - case "AnimatedContainer": - return [ - CustomAnimatedContainer(), - ]; - case "AnimatedDefaultTextStyle": - return [ - CustomAnimatedDefaultTextStyle(), - ]; - case "AnimatedIcon": - return [ - CustomAnimatedIcon(), - ]; - case "Dialog": - return [ - CustomDialog(), - ]; - case "AlertDialog": - return [ - CustomAlertDialog(), - ]; - case "SimpleDialog": - return [ - CustomSimpleDialog(), - ]; - case "CupertinoAlertDialog": - return [ - CustomCupertinoAlertDialog(), - ]; - case "AboutDialog": - return [ - CustomAboutDialog(), - ]; - case "CupertinoActionSheet": - return [ - CustomCupertinoActionSheet(), - ]; - case "CupertinoActionSheetAction": - return [ - CustomCupertinoActionSheetAction(), - ]; - case "SimpleDialogOption": - return [ - CustomSimpleDialogOption(), - ]; - case "DayPicker": - return [ - CustomDayPicker(), - ]; - case "MonthPicker": - return [ - CustomMonthPicker(), - ]; - case "YearPicker": - return [ - CustomYearPicker(), - ]; - case "CupertinoDatePicker": - return [ - CustomCupertinoDatePicker(), - ]; - case "CupertinoTimerPicker": - return [ - CustomCupertinoTimerPicker(), - ]; - case "CupertinoPicker": - return [ - CustomCupertinoPicker(), - ]; - case "SnackBar": - return [ - CustomSnackBar(), - ]; - case "SnackBarAction": - return [ - CustomSnackBarAction(), - ]; - case "BottomSheet": - return [ - CustomBottomSheet(), - ]; - case "CupertinoContextMenu": - return [ - CustomCupertinoContextMenu(), - ]; - case "CupertinoContextMenuAction": - return [ - CustomCupertinoContextMenuAction(), - ]; - case "LicensePage": - return [ - CustomLicensePage(), - ]; - case "Builder": - return [ - BuilderDemo(), - ]; - case "GestureDetector": - return [ - CustomGestureDetector(), - TapGestureDetector(), - PanGestureDetector(), - ]; - case "Listener": - return [ - CustomListener(), - ]; - case "Tab": - return [ - CustomTab(), - ]; - case "PreferredSize": - return [ - CustomPreferredSize(), - AdapterPreferredSize(), - ]; - case "InkResponse": - return [ - CustomInkResponse(), - ColorInkResponse(), - ]; - case "InkWell": - return [ - CustomInkWell(), - ColorInkWell(), - ]; - case "TableRowInkWell": - return [ - CustomTableRowInkWell(), - ]; - case "Ink": - return [ - CustomInk(), - InkImage(), - ]; - case "RawChip": - return [ - PressRawChip(), - SelectRawChip(), - ]; - case "Drawer": - return [ - CustomDrawer(), - ]; - case "DrawerHeader": - return [ - CustomDrawerHeader(), - ]; - case "CupertinoApp": - return [ - CustomCupertinoApp(), - ]; - case "CupertinoPageScaffold": - return [ - CustomCupertinoPageScaffold(), - ]; - case "CupertinoTabScaffold": - return [ - CustomCupertinoTabScaffold(), - ]; - case "PositionedDirectional": - return [ - CustomPositionedDirectional(), - ]; - case "Material": - return [ - CustomMaterial(), - ShapeMaterial(), - ]; - case "IndexedStack": - return [ - 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(), - CtrlPageView(), - ]; - case "CustomPaint": - return [ - ClockPage(), - PlayBezier3Page(), - ]; - case "MediaQuery": - return [ - CustomMediaQuery(), - ]; - case "Theme": - return [TextThemeDemo(), CustomTheme()]; - case "CupertinoTheme": - return [TextCupertinoTheme(), CustomCupertinoTheme()]; - case "WillPopScope": - return [ - CustomWillPopScope(), - ]; - case "Hero": - return [ - CustomHero(), - ]; - case "FutureBuilder": - return [ - CustomFutureBuilder(), - ]; - case "StreamBuilder": - return [ - CustomStreamBuilder(), - ]; - case "PopupMenuDivider": - return [ - CustomPopupMenuDivider(), - ]; - case "RawMaterialButton": - return [ - CustomRawMaterialButton(), - ShapeRawMaterialButton(), - ]; - case "Dismissible": - return [ - CustomDismissible(), - DirectionDismissible(), - ]; - case "ReorderableListView": - return [ - CustomReorderableListView(), - DirectionReorderableListView(), - ]; - case "ExpansionPanelList": - return [ - CustomExpansionPanelList(), - ]; - case "ListWheelScrollView": - return [ - CustomListWheelScrollView(), - ]; - case "ScrollConfiguration": - return [ - CustomScrollConfiguration(), - ]; - case "DropdownButtonHideUnderline": - return [ - CustomDropDownButtonHideUnderline(), - ]; - case "Overlay": - return [ - CustomOverlay(), - ]; - case "CustomScrollView": - return [ - CustomScrollViewDemo(), - ]; - case "SliverAppBar": - return [ - SliverAppBarDemo(), - ]; - case "SliverList": - return [ - SliverListDemo(), - ]; - case "SliverFixedExtentList": - return [ - SliverFixedExtentListDemo(), - ]; - case "SliverFillViewport": - return [ - SliverFillViewportDemo(), - ]; - case "SliverGird": - return [ - SliverGirdDemo(), - ]; - case "SliverToBoxAdapter": - return [ - SliverToBoxAdapterDemo(), - ]; - case "SliverPersistentHeader": - return [ - SliverPersistentHeaderDemo(), - ]; - case "SliverPadding": - return [ - SliverPaddingDemo(), - ]; - case "SliverOpacity": - return [ - SliverOpacityDemo(), - ]; - case "AboutListTile": - return [ - AboutListTileDemo(), - ]; - case "Scrollbar": - return [ - CustomScrollbar(), - ]; - case "CupertinoScrollbar": - return [ - CustomCupertinoScrollbar(), - ]; - case "FlexibleSpaceBar": - return [ - FlexibleSpaceBarDemo(), - ]; - case "ErrorWidget": - return [ - ErrorWidgetDemo(), - ]; - case "Form": - return [ - CustomForm(), - ]; - case "TextFormField": - return [ - CustomTextFormField(), - ]; - case "Stepper": - return [ - StepperDemo(), - VerticalStepper(), - ]; - case "AnimatedSize": - return [ - CustomAnimatedSize(), - ]; - case "ShaderMask": - return [ - RadialShaderMask(), - LinearShaderMask(), - ]; - case "DefaultTextStyle": - return [ - DefaultTextStyleDemo(), - ]; - case "IconTheme": - return [ - IconThemeDemo(), - ]; - case "ButtonTheme": - return [ - ButtonThemeDemo(), - ]; - case "DividerTheme": - return [ - DividerThemeDemo(), - ]; - case "Navigator": - return [ - NavigatorDemo(), - ]; - case "SliderTheme": - return [ - SliderThemeDemo(), - DIYSliderTheme(), - ]; - case "LayoutBuilder": - return [ - CustomLayoutBuilder(), - FitByLayoutBuilder(), - SimpleExpandableText(), - ]; - case "IgnorePointer": - return [ - CustomIgnorePointer(), - ]; - case "AbsorbPointer": - return [ - CustomAbsorbPointer(), - ]; - case "BackdropFilter": - return [ - CustomBackdropFilter(), - ]; - case "AnimatedPhysicalModel": - return [ - AnimatedPhysicalModelDemo(), - ]; - case "CustomSingleChildLayout": - return [ - CustomSingleChildLayoutDemo(), - OffSetWidgetDemo(), - ]; - default: return []; - } - } -} diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 000000000..d3896c984 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 000000000..423c6d0ea --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,138 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "flutter_unit") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.toly1994.flutter_unit") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 000000000..d5bd01648 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index d38195aa0..faea87d4e 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -2,8 +2,22 @@ // Generated file. Do not edit. // +// clang-format off + #include "generated_plugin_registrant.h" +#include +#include +#include void fl_register_plugins(FlPluginRegistry* registry) { + 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); + g_autoptr(FlPluginRegistrar) window_manager_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin"); + window_manager_plugin_register_with_registrar(window_manager_registrar); } diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h index 9bf747894..e0f0a47bc 100644 --- a/linux/flutter/generated_plugin_registrant.h +++ b/linux/flutter/generated_plugin_registrant.h @@ -2,6 +2,8 @@ // Generated file. Do not edit. // +// clang-format off + #ifndef GENERATED_PLUGIN_REGISTRANT_ #define GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 51436ae8c..4f427dd2a 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -3,6 +3,12 @@ # list(APPEND FLUTTER_PLUGIN_LIST + screen_retriever_linux + url_launcher_linux + window_manager +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST ) set(PLUGIN_BUNDLED_LIBRARIES) @@ -13,3 +19,8 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST}) list(APPEND PLUGIN_BUNDLED_LIBRARIES $) list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/flutter/generated_plugins.mk b/linux/flutter/generated_plugins.mk deleted file mode 100644 index 63a60db84..000000000 --- a/linux/flutter/generated_plugins.mk +++ /dev/null @@ -1,25 +0,0 @@ -# Plugins to include in the build. -GENERATED_PLUGINS=\ - -GENERATED_PLUGINS_DIR=flutter/ephemeral/.plugin_symlinks -# A plugin library name plugin name with _plugin appended. -GENERATED_PLUGIN_LIB_NAMES=$(foreach plugin,$(GENERATED_PLUGINS),$(plugin)_plugin) - -# Variables for use in the enclosing Makefile. Changes to these names are -# breaking changes. -PLUGIN_TARGETS=$(GENERATED_PLUGINS) -PLUGIN_LIBRARIES=$(foreach plugin,$(GENERATED_PLUGIN_LIB_NAMES),\ - $(OUT_DIR)/lib$(plugin).so) -PLUGIN_LDFLAGS=$(patsubst %,-l%,$(GENERATED_PLUGIN_LIB_NAMES)) -PLUGIN_CPPFLAGS=$(foreach plugin,$(GENERATED_PLUGINS),\ - -I$(GENERATED_PLUGINS_DIR)/$(plugin)/linux) - -# Targets - -# Implicit rules don't match phony targets, so list plugin builds explicitly. - -.PHONY: $(GENERATED_PLUGINS) -$(GENERATED_PLUGINS): - make -C $(GENERATED_PLUGINS_DIR)/$@/linux \ - OUT_DIR=$(OUT_DIR) \ - FLUTTER_EPHEMERAL_DIR="$(abspath flutter/ephemeral)" diff --git a/linux/main.cc b/linux/main.cc new file mode 100644 index 000000000..e7c5c5437 --- /dev/null +++ b/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/my_application.cc b/linux/my_application.cc new file mode 100644 index 000000000..e6c7cf7fc --- /dev/null +++ b/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "flutter_unit"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "flutter_unit"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/linux/my_application.h b/linux/my_application.h new file mode 100644 index 000000000..72271d5e4 --- /dev/null +++ b/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 000000000..746adbb6b --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 000000000..4b81f9b2d --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 000000000..5caa9d157 --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 000000000..ab2f2a3fc --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,30 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import file_picker +import package_info_plus +import path_provider_foundation +import screen_retriever_macos +import share_plus +import shared_preferences_foundation +import sqflite_darwin +import url_launcher_macos +import webview_flutter_wkwebview +import window_manager + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + 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 b/macos/Podfile new file mode 100644 index 000000000..049abe295 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,40 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/macos/Podfile.lock b/macos/Podfile.lock new file mode 100644 index 000000000..6fea947e4 --- /dev/null +++ b/macos/Podfile.lock @@ -0,0 +1,92 @@ +PODS: + - file_picker (0.0.1): + - FlutterMacOS + - FlutterMacOS (1.0.0) + - open_file_mac (0.0.1): + - FlutterMacOS + - package_info_plus (0.0.1): + - FlutterMacOS + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - quill_native_bridge_macos (0.0.1): + - FlutterMacOS + - screen_retriever_macos (0.0.1): + - FlutterMacOS + - share_plus (0.0.1): + - FlutterMacOS + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS + - 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_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_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`) + +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_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_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 + 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.14.3 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 000000000..a19f9ac58 --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,636 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 04C873F32A42825600BAB8F5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70442F268D8DC1467C4B98E1 /* Pods_Runner.framework */; }; + 04C873F42A42825600BAB8F5 /* Pods_Runner.framework in Bundle Framework */ = {isa = PBXBuildFile; fileRef = 70442F268D8DC1467C4B98E1 /* Pods_Runner.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 33CC110E2044A8840003C045 /* Bundle Framework */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 04C873F42A42825600BAB8F5 /* Pods_Runner.framework in Bundle Framework */, + ); + name = "Bundle Framework"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 1DFE55036C5A10261BA01266 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* flutter_unit.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = flutter_unit.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 70442F268D8DC1467C4B98E1 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + B432D8A33593253D20752670 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + F04E92F35B305ECDEB7ABD24 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 04C873F32A42825600BAB8F5 /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 09AE68A50FF2EF7290782883 /* Pods */ = { + isa = PBXGroup; + children = ( + 1DFE55036C5A10261BA01266 /* Pods-Runner.debug.xcconfig */, + F04E92F35B305ECDEB7ABD24 /* Pods-Runner.release.xcconfig */, + B432D8A33593253D20752670 /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 09AE68A50FF2EF7290782883 /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* flutter_unit.app */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 70442F268D8DC1467C4B98E1 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 284E66EAF54928405B1CB4E9 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 33CC110E2044A8840003C045 /* Bundle Framework */, + 3399D490228B24CF009A79C7 /* ShellScript */, + 393027C358E745B59CB4E335 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* flutter_unit.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 284E66EAF54928405B1CB4E9 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + 393027C358E745B59CB4E335 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=macosx*]" = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = XQPP7CHG9D; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 000000000..46ae587f6 --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..21a3cc14c --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift new file mode 100644 index 000000000..b3c176141 --- /dev/null +++ b/macos/Runner/AppDelegate.swift @@ -0,0 +1,13 @@ +import Cocoa +import FlutterMacOS + +@main +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..a2ec33f19 --- /dev/null +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 000000000..fe2a961e9 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 000000000..fe2a961e9 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 000000000..fe2a961e9 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 000000000..fe2a961e9 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 000000000..fe2a961e9 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 000000000..fe2a961e9 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 000000000..fe2a961e9 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 000000000..80e867a4e --- /dev/null +++ b/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 000000000..cc56c390b --- /dev/null +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = flutter_unit + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.toly1994.flutterUnit + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2022 com.toly1994. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 000000000..36b0fd946 --- /dev/null +++ b/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 000000000..dff4f4956 --- /dev/null +++ b/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 000000000..42bcbf478 --- /dev/null +++ b/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements new file mode 100644 index 000000000..2a58eea8d --- /dev/null +++ b/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.network.client + + com.apple.security.device.camera + + com.apple.security.device.microphone + + + diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist new file mode 100644 index 000000000..4789daa6a --- /dev/null +++ b/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 000000000..cdd9a6384 --- /dev/null +++ b/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,21 @@ +import Cocoa +import FlutterMacOS +import window_manager + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController.init() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } + + override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) { + super.order(place, relativeTo: otherWin) + hiddenWindowAtLaunch() + } +} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements new file mode 100644 index 000000000..2a58eea8d --- /dev/null +++ b/macos/Runner/Release.entitlements @@ -0,0 +1,18 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.network.client + + com.apple.security.device.camera + + com.apple.security.device.microphone + + + 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/modules/basic_system/app/.gitignore b/modules/basic_system/app/.gitignore new file mode 100644 index 000000000..96486fd93 --- /dev/null +++ b/modules/basic_system/app/.gitignore @@ -0,0 +1,30 @@ +# 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/modules/basic_system/app/.metadata b/modules/basic_system/app/.metadata new file mode 100644 index 000000000..fbfa6dfba --- /dev/null +++ b/modules/basic_system/app/.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: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + channel: stable + +project_type: package diff --git a/modules/basic_system/app/CHANGELOG.md b/modules/basic_system/app/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/basic_system/app/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/basic_system/app/LICENSE b/modules/basic_system/app/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/basic_system/app/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/basic_system/app/README.md b/modules/basic_system/app/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/basic_system/app/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/basic_system/app/analysis_options.yaml b/modules/basic_system/app/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/basic_system/app/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/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/modules/basic_system/app/lib/app/cons/sp.dart b/modules/basic_system/app/lib/app/cons/sp.dart new file mode 100644 index 000000000..2dc863d45 --- /dev/null +++ b/modules/basic_system/app/lib/app/cons/sp.dart @@ -0,0 +1,23 @@ +/// create by 张风捷特烈 on 2020-04-10 +/// contact me by email 1981462002@qq.com +/// 说明: + +class SpKey{ + SpKey._(); + + static const themeColorIndex = 'theme_color_index'; + + static const iconFontGenConfig = 'icon_font_gen_config'; + + static const showBackground = 'show_background'; + static const showTool = 'show_tool'; + static const fontFamily = 'font_family'; + static const codeStyleIndex = 'code_style'; + static const itemStyleIndex = 'item_style_index'; + static const appStyleIndex = 'app_style_index'; + + static String dbVersionKey= "db_version_key"; + static String tokenKey= "token_key"; + static String userKey= "user_key"; + +} \ No newline at end of file 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/modules/basic_system/app/lib/app/res/toly_icon.dart b/modules/basic_system/app/lib/app/res/toly_icon.dart new file mode 100644 index 000000000..012bb0a88 --- /dev/null +++ b/modules/basic_system/app/lib/app/res/toly_icon.dart @@ -0,0 +1,42 @@ +import 'package:flutter/widgets.dart'; +// Power By 张风捷特烈--- Generated file. Do not edit. +// 欢迎支持: https://github.com/toly1994328/FlutterUnit +class TolyIcon { + TolyIcon._(); + static const IconData icon_artifact = IconData(0xe726, fontFamily: "TolyIcon"); +static const IconData dark = IconData(0xe72f, fontFamily: "TolyIcon"); +static const IconData wb_sunny = IconData(0xe746, fontFamily: "TolyIcon"); +static const IconData icon_fast = IconData(0xe607, fontFamily: "TolyIcon"); +static const IconData icon_layout = IconData(0xe85e, fontFamily: "TolyIcon"); +static const IconData upload_success = IconData(0xe60b, fontFamily: "TolyIcon"); +static const IconData download = IconData(0xea51, fontFamily: "TolyIcon"); +static const IconData upload = IconData(0xea52, fontFamily: "TolyIcon"); +static const IconData error = IconData(0xe614, fontFamily: "TolyIcon"); +static const IconData dingzhi1 = IconData(0xe60e, fontFamily: "TolyIcon"); +static const IconData icon_collect = IconData(0xe672, fontFamily: "TolyIcon"); +static const IconData yonghu = IconData(0xe619, fontFamily: "TolyIcon"); +static const IconData icon_common = IconData(0xe634, fontFamily: "TolyIcon"); +static const IconData icon_see = IconData(0xe608, fontFamily: "TolyIcon"); +static const IconData icon_issues = IconData(0xe7a7, fontFamily: "TolyIcon"); +static const IconData icon_fork = IconData(0xe623, fontFamily: "TolyIcon"); +static const IconData icon_github_star = IconData(0xe7df, fontFamily: "TolyIcon"); +static const IconData icon_show = IconData(0xe648, fontFamily: "TolyIcon"); +static const IconData icon_hide = IconData(0xe649, fontFamily: "TolyIcon"); +static const IconData icon_email = IconData(0xe694, fontFamily: "TolyIcon"); +static const IconData icon_github = IconData(0xe689, fontFamily: "TolyIcon"); +static const IconData icon_juejin = IconData(0xe601, fontFamily: "TolyIcon"); +static const IconData icon_share = IconData(0xe613, fontFamily: "TolyIcon"); +static const IconData icon_background = IconData(0xe60a, fontFamily: "TolyIcon"); +static const IconData icon_code = IconData(0xe70b, fontFamily: "TolyIcon"); +static const IconData icon_item = IconData(0xe66f, fontFamily: "TolyIcon"); +static const IconData icon_kafei = IconData(0xe6aa, fontFamily: "TolyIcon"); +static const IconData icon_tag = IconData(0xe6e7, fontFamily: "TolyIcon"); +static const IconData icon_them = IconData(0xe6c2, fontFamily: "TolyIcon"); +static const IconData icon_bug = IconData(0xe7af, fontFamily: "TolyIcon"); +static const IconData icon_sound = IconData(0xe606, fontFamily: "TolyIcon"); +static const IconData icon_search = IconData(0xe604, fontFamily: "TolyIcon"); +static const IconData icon_star_ok = IconData(0xe6ae, fontFamily: "TolyIcon"); +static const IconData icon_star = IconData(0xe609, fontFamily: "TolyIcon"); +static const IconData icon_star_add = IconData(0xe68e, fontFamily: "TolyIcon"); + +} 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/modules/basic_system/app/lib/app/style/gap.dart b/modules/basic_system/app/lib/app/style/gap.dart new file mode 100644 index 000000000..2eed71fb1 --- /dev/null +++ b/modules/basic_system/app/lib/app/style/gap.dart @@ -0,0 +1,17 @@ +// ignore_for_file: constant_identifier_names + +import 'package:flutter/material.dart'; + +import 'unit_color.dart'; + +class Gap{ + static const Widget H5 = SizedBox(width: 5); + static const Widget H10 = SizedBox(height: 10); + + static const Widget W5 = SizedBox(width: 5); + static const Widget W10 = SizedBox(width: 10); + + static const Widget sfl10 = SizedBox(height: 10,); + + +} \ No newline at end of file diff --git a/lib/app/res/style/shape/coupon_shape_border.dart b/modules/basic_system/app/lib/app/style/shape/coupon_shape_border.dart similarity index 87% rename from lib/app/res/style/shape/coupon_shape_border.dart rename to modules/basic_system/app/lib/app/style/shape/coupon_shape_border.dart index 4b6831ffc..4b5f51999 100644 --- a/lib/app/res/style/shape/coupon_shape_border.dart +++ b/modules/basic_system/app/lib/app/style/shape/coupon_shape_border.dart @@ -14,9 +14,9 @@ class CouponShapeBorder extends ShapeBorder { final Color color; final bool hasTopHole; final bool hasBottomHole; - final double edgeRadius; + final double? edgeRadius; - CouponShapeBorder( + const CouponShapeBorder( {this.holeCount = 6, this.hasTopHole =true, this.hasBottomHole =false, @@ -26,15 +26,15 @@ class CouponShapeBorder extends ShapeBorder { this.color = Colors.white,this.edgeRadius}); @override - EdgeInsetsGeometry get dimensions => null; + EdgeInsetsGeometry get dimensions => EdgeInsets.zero; @override - Path getInnerPath(Rect rect, {TextDirection textDirection}) { - return null; + Path getInnerPath(Rect rect, {TextDirection? textDirection}) { + return Path(); } @override - Path getOuterPath(Rect rect, {TextDirection textDirection}) { + Path getOuterPath(Rect rect, {TextDirection? textDirection}) { double w = rect.width; double h = rect.height; @@ -52,10 +52,10 @@ class CouponShapeBorder extends ShapeBorder { } if(edgeRadius!=null){ if(hasTopHole){ - _formHoleTop(path, rect, edgeRadius); + _formHoleTop(path, rect, edgeRadius!); } if(hasBottomHole){ - _formHoleBottom(path, rect, edgeRadius); + _formHoleBottom(path, rect, edgeRadius!); } } @@ -103,7 +103,7 @@ class CouponShapeBorder extends ShapeBorder { } @override - void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) { + void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) { if(!hasLine) return; Paint paint = Paint() ..color = color @@ -131,7 +131,6 @@ class CouponShapeBorder extends ShapeBorder { @override ShapeBorder scale(double t) { - // TODO: implement scale - return null; + return this; } } diff --git a/lib/app/res/style/shape/techno_shape.dart b/modules/basic_system/app/lib/app/style/shape/techno_shape.dart similarity index 81% rename from lib/app/res/style/shape/techno_shape.dart rename to modules/basic_system/app/lib/app/style/shape/techno_shape.dart index ab1c3297b..0c75e6ed3 100644 --- a/lib/app/res/style/shape/techno_shape.dart +++ b/modules/basic_system/app/lib/app/style/shape/techno_shape.dart @@ -1,5 +1,3 @@ - -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; /// create by 张风捷特烈 on 2020-03-06 @@ -14,11 +12,11 @@ class TechnoShapeBorder extends ShapeBorder { final Path innerLinePathTop = Path(); final Color color; - final cornerWidth; + final double cornerWidth; - final spanWidth; - final storkWidth; - final innerRate; + final double spanWidth; + final double storkWidth; + final double innerRate; TechnoShapeBorder( {this.color = Colors.green, @@ -32,17 +30,17 @@ class TechnoShapeBorder extends ShapeBorder { } @override - EdgeInsetsGeometry get dimensions => null; + EdgeInsetsGeometry get dimensions => EdgeInsets.zero; @override - Path getInnerPath(Rect rect, {TextDirection textDirection}) { + Path getInnerPath(Rect rect, {TextDirection? textDirection}) { Path path = Path(); - path.addRRect(RRect.fromRectAndRadius(rect, Radius.circular(5))); + path.addRRect(RRect.fromRectAndRadius(rect, const Radius.circular(5))); return path; } @override - Path getOuterPath(Rect rect, {TextDirection textDirection}) { + Path getOuterPath(Rect rect, {TextDirection? textDirection}) { double width = rect.width; outLinePath @@ -64,7 +62,7 @@ class TechnoShapeBorder extends ShapeBorder { } @override - void paint(Canvas canvas, Rect rect, {TextDirection textDirection}) { + void paint(Canvas canvas, Rect rect, {TextDirection? textDirection}) { canvas.drawPath( outLinePath, _paint..style = PaintingStyle.stroke); @@ -81,6 +79,6 @@ class TechnoShapeBorder extends ShapeBorder { @override ShapeBorder scale(double t) { - return null; + return this; } } diff --git a/modules/basic_system/app/lib/app/style/unit_color.dart b/modules/basic_system/app/lib/app/style/unit_color.dart new file mode 100644 index 000000000..2808c6059 --- /dev/null +++ b/modules/basic_system/app/lib/app/style/unit_color.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +/// create by 张风捷特烈 on 2020-04-19 +/// contact me by email 1981462002@qq.com +/// 说明: + +class UnitColor { + + // 收藏夹提供的颜色 + static const collectColorSupport = [ + Color(0xFFF2F2F2), + Colors.black, + Colors.red, + Colors.orange, + Colors.yellow, + Colors.green, + Colors.blue, + Colors.indigo, + Colors.purple, + + Colors.cyanAccent, + Color(0xffd1d08f), + Colors.pink, + Colors.amber, + Colors.lime, + Colors.teal, + Colors.cyan, + Color(0xff586CF2), + Colors.purpleAccent, + ]; + + + // 文字相关 + static const Color input_border_color = Color(0xffD0D7DD); + static const Color text_color = Color(0xff323C47); + static const Color input_hit_text_color = Color(0xff939EA7); + static const Color head_text_color = Color(0xff666666); + static const Color scaffoldBgLight = Color(0xffF3F4F6); + + // 缺省相关 + static const Color error_color = Colors.red; + static const Color warning_color = Colors.orangeAccent; +} diff --git a/modules/basic_system/app/lib/app/style/unit_text_style.dart b/modules/basic_system/app/lib/app/style/unit_text_style.dart new file mode 100644 index 000000000..1fdf7c193 --- /dev/null +++ b/modules/basic_system/app/lib/app/style/unit_text_style.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +import '../theme/size_unit.dart'; +import 'unit_color.dart'; + +///文本样式 +class UnitTextStyle { + + // 标题加黑 + static const labelBold = TextStyle(fontWeight: FontWeight.bold, fontSize: 16); + + // 闪屏页文字阴影样式 + static const splashShadows = TextStyle( + color: Colors.grey, + shadows: [ + Shadow( + color: Colors.black, + blurRadius: 0.5, + offset: Offset(0.1, 0.1)) + ], + fontSize: 12); + + static const shadowTextStyle = TextStyle(color: Colors.grey, shadows: [ + Shadow(color: Colors.white, offset: Offset(.5, .5), blurRadius: .5) + ]); + + + // 过时文字样式 + static const deprecatedChip = TextStyle( + fontSize: 12, + color: Colors.white, + decoration: TextDecoration.lineThrough, + decorationThickness: 2, + ); + + static const commonChip = TextStyle( + fontSize: 12, + color: Colors.white, + ); + + static const TextStyle hintStyle = TextStyle( + color: UnitColor.input_hit_text_color, + fontSize: SizeUnit.input_hit_text_size); + + static const TextStyle primary = TextStyle( + color: UnitColor.text_color, fontSize: SizeUnit.input_text_size); + + static const TextStyle headTextStyle = TextStyle( + color: UnitColor.head_text_color, fontSize: SizeUnit.head_text_size); + + static const TextStyle smallSubTextStyle = TextStyle( + color: UnitColor.input_hit_text_color, + fontSize: SizeUnit.small_text_size); + + static const TextStyle bigTextStyle = TextStyle( + color: UnitColor.text_color, fontSize: SizeUnit.big_text_size); +} 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/lib/app/res/size_unit.dart b/modules/basic_system/app/lib/app/theme/size_unit.dart similarity index 94% rename from lib/app/res/size_unit.dart rename to modules/basic_system/app/lib/app/theme/size_unit.dart index 4702ad519..4c0da3a0d 100644 --- a/lib/app/res/size_unit.dart +++ b/modules/basic_system/app/lib/app/theme/size_unit.dart @@ -2,6 +2,8 @@ /// contact me by email 1981462002@qq.com /// 说明: +// ignore_for_file: constant_identifier_names + class SizeUnit { // 小文字大小 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/modules/basic_system/app/lib/app_config/repository/repository.dart b/modules/basic_system/app/lib/app_config/repository/repository.dart new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/modules/basic_system/app/lib/app_config/repository/repository.dart @@ -0,0 +1 @@ + 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/modules/basic_system/app/lib/view/about/about_app_page.dart b/modules/basic_system/app/lib/view/about/about_app_page.dart new file mode 100644 index 000000000..59eb83255 --- /dev/null +++ b/modules/basic_system/app/lib/view/about/about_app_page.dart @@ -0,0 +1,236 @@ +/// create by 张风捷特烈 on 2020-04-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: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); + + @override + Widget build(BuildContext context) { + return Theme( + data: ThemeData( + brightness: Brightness.light + ), + child: Scaffold( + backgroundColor: Colors.white, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + Container( + height: 150, + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.only(bottom: 50), + child: Image.asset( + 'assets/images/sabar.webp', + fit: BoxFit.cover, + ), + ), + _buildBar(context), + Positioned( + bottom: 0, + left: 50, + child: FeedbackWidget( + onEnd : (){ + Navigator.push(context, SlidePageRoute(child: const FlutterUnitTimeLine())); + }, + child: CircleImage( + size: 100, + shadowColor: Theme.of(context).primaryColor, + image: const AssetImage('assets/images/icon_head.webp'), + ), + )), + ], + ), + Expanded( + child: SingleChildScrollView( + child: Container( + margin: const EdgeInsets.all(24), + child: _buildInfo(), + ), + ), + ), + ], + ), + ), + ); + } + + Widget _buildBar(BuildContext context) { + return Container( + height: kToolbarHeight, + margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top), + child: Row( + children: [ + GestureDetector( + onTap: () => Navigator.of(context).pop(), + child: Container( + padding: const EdgeInsets.only(left: 10), + child: Icon( + Icons.arrow_back, + size: 30, + color: Theme.of(context).primaryColor, + ), + ), + ), + const Spacer(), + FeedbackWidget( + onPressed: () => + _launchURL("mailto:1981462002@qq.com?subject=来自Flutter Unit"), + child: Icon( + TolyIcon.icon_email, + size: 20, + color: Theme.of(context).primaryColor, + ), + ), + const SizedBox( + width: 20, + ) + ], + ), + ); + } + + _launchURL(String url) async { + if (await canLaunch(url)) { + await launch(url); + } else { + + } + } + + Widget _buildInfo() { + return Stack( + children: [ + Positioned( + right: 10, + top: 0, + child: Wrap( + spacing: 20, + children: [ + FeedbackWidget( + onPressed: () => + _launchURL("https://github.com/toly1994328/FlutterUnit"), + child: Wrap( + direction: Axis.vertical, + crossAxisAlignment: WrapCrossAlignment.center, + children: const [ + Icon( + TolyIcon.icon_github, + size: 35, + ), + Text('Github') + ], + )), + ], + ), + ), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + Text( + 'Flutter Unit', + style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold), + ), + SizedBox(height: 20), + Text( + 'The Unity Of Flutter, The Unity Of Coder.', + style: TextStyle(fontSize: 16), + ), + SizedBox(height: 10), + Text( + 'Flutter的联合,编程者的联合。', + style: TextStyle(fontSize: 16), + ), + Divider( + height: 20, + ), + 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' + '○ 实现应用内更新功能,方便使用者及时更新到最新版体验。' + ) + ], + ), + ], + ); + } +} + +class InfoPanel extends StatelessWidget { + final String title; + final String info; + + + const InfoPanel({Key? key, required this.title,required this.info}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + 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(title,style: const TextStyle(fontSize: 16,fontWeight: FontWeight.bold),), + ) + ], + ), + Panel( + color: Theme.of(context).primaryColor.withAlpha(33), + child: Text( + info, + style: const TextStyle(color: Colors.grey, + fontSize: 13, + shadows: [ + Shadow( + color: Colors.white, + offset: Offset(1,1) + ) + ]), + ), + ), + ], + ); + } +} diff --git a/modules/basic_system/app/lib/view/about/about_me_page.dart b/modules/basic_system/app/lib/view/about/about_me_page.dart new file mode 100644 index 000000000..1c435bac9 --- /dev/null +++ b/modules/basic_system/app/lib/view/about/about_me_page.dart @@ -0,0 +1,179 @@ +/// create by 张风捷特烈 on 2020-04-13 +/// contact me by email 1981462002@qq.com +/// 说明: ... +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:toly_ui/toly_ui.dart'; + +import 'package:url_launcher/url_launcher.dart'; + +class AboutMePage extends StatelessWidget { + const AboutMePage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Theme( + data: ThemeData( + brightness: Brightness.light + ), + child:Scaffold( + backgroundColor: Colors.white, + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + Container( + height: 180, + width: MediaQuery.of(context).size.width, + margin:const EdgeInsets.only(bottom: 50), + child: Image.asset( + 'assets/images/sabar.webp', + fit: BoxFit.cover, + ), + ), + _buildBar(context), + Positioned( + bottom: 0, + left: 50, + child: CircleImage( + size: 100, + shadowColor: Theme.of(context).primaryColor, + image: const AssetImage('assets/images/icon_head.webp'), + )), + ], + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 15), + child: Stack(children: [ + Positioned( + right: 10, + top: 0, + child: _buildLinkIcon(), + ), + _buildInfo() + ]), + ), + ), + ], + ), + )); + } + + Widget _buildBar(BuildContext context) { + return Container( + height: kToolbarHeight, + margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top), + child: Row( + children: [ + GestureDetector( + onTap: () => Navigator.of(context).pop(), + child: Padding( + padding: const EdgeInsets.only(left: 10), + child: Icon( + Icons.arrow_back, + size: 30, + color: Theme.of(context).primaryColor, + ), + ), + ), + const Spacer(), + FeedbackWidget( + onPressed: () => + _launchURL("mailto:1981462002@qq.com?subject=来自Flutter Unit"), + child: Icon( + TolyIcon.icon_email, + size: 20, + color: Theme.of(context).primaryColor, + ), + ), + const SizedBox(width: 20) + ], + ), + ); + } + + _launchURL(String url) async { + if (await canLaunch(url)) { + await launch(url); + } else { + debugPrint('Could not launch $url'); + } + } + + Widget _buildInfo() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text('张风捷特烈', + style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold)), + const SizedBox(height: 20), + const Divider(height: 18), + const Text('The King Of Coder. 「编程之王」', style: TextStyle(fontSize: 16)), + const SizedBox(height: 10), + const Text('海的彼岸有我未曾见证的风采。', style: TextStyle(fontSize: 16)), + const SizedBox(height: 10), + const Text( + '微信群: 编程技术交流圣地 -【Flutter群】\n' + '愿青梅煮酒,与君天涯共话。', + style: TextStyle(color: Colors.grey)), + const SizedBox(height: 10), + Expanded( + child: Padding( + padding: const EdgeInsets.all(30.0), + child: AspectRatio( + aspectRatio: 1, + child: Image.asset( + 'assets/images/wechat.webp', + fit: BoxFit.fitWidth, + )), + )), + const Center( + child: Text( + '我的微信', + style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ), + ], + ); + } + + Wrap _buildLinkIcon() { + return Wrap( + spacing: 20, + children: [ + FeedbackWidget( + onPressed: () => + _launchURL("https://juejin.im/user/5b42c0656fb9a04fe727eb37"), + child: Wrap( + direction: Axis.vertical, + crossAxisAlignment: WrapCrossAlignment.center, + children:const [ + Icon( + TolyIcon.icon_juejin, + size: 35, + color: Colors.blue, + ), + Text('掘金') + ], + )), + FeedbackWidget( + onPressed: () => + _launchURL("https://github.com/toly1994328"), + child: Wrap( + direction: Axis.vertical, + crossAxisAlignment: WrapCrossAlignment.center, + children: const[ + Icon( + TolyIcon.icon_github, + size: 35, + ), + SizedBox(height: 4,), + Text('Github') + ], + )), + ], + ); + } +} 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/modules/basic_system/app/lib/view/about/version_info.dart b/modules/basic_system/app/lib/view/about/version_info.dart new file mode 100644 index 000000000..601322da1 --- /dev/null +++ b/modules/basic_system/app/lib/view/about/version_info.dart @@ -0,0 +1,130 @@ + +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:toly_ui/toly_ui.dart'; +import 'package:url_launcher/url_launcher.dart'; + + +import 'version/version_shower.dart'; + +/// create by 张风捷特烈 on 2020/6/16 +/// contact me by email 1981462002@qq.com +/// 说明: + +class VersionInfo extends StatelessWidget { + const VersionInfo({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + + Color? bgColor = Theme.of(context).listTileTheme.tileColor; + + return Scaffold( + backgroundColor: bgColor, + appBar: AppBar( + backgroundColor: bgColor, + elevation: 0, + iconTheme: const IconThemeData( + color: Colors.grey + ), + ), + body:ConstrainedBox( + constraints: const BoxConstraints.expand(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(top:28.0), + child: _buildTop(), + ), + + _buildCenter(context), + const Spacer(), + Padding( + padding: const EdgeInsets.only(bottom:8.0), + child: buildBottom(context), + ) + + ], + ), + ), + ); + } + + Widget _buildTop() { + return Wrap( + direction: Axis.vertical, + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 10, + children: const [ + CircleImage(image: AssetImage("assets/images/icon_head.webp"),size: 80,), + Text('Flutter Unit',style: TextStyle(fontSize: 20,fontWeight: FontWeight.bold),), + VersionShower(), + ], + ); + } + + Widget _buildCenter(BuildContext context) { + const TextStyle labelStyle= TextStyle(fontSize: 13); + return Padding( + padding: const EdgeInsets.only(left:20.0,right: 20,top: 20), + child: ScrollConfiguration( + behavior: NoScrollBehavior(), + child: ListView( + shrinkWrap: true, + children: [ + const Divider(height: 1,), + ListTile( + title: Text(context.l10n.appDetails,style: labelStyle,), + trailing: _nextIcon(context), + onTap: () => context.push('/about_app'), + ), + const Divider(height: 1,indent: 10), + const AppUpdatePanel(), + const Divider(height: 1,indent: 10), + ListTile( + title: Text(context.l10n.checkDatabaseNewVersion,style: labelStyle), + trailing: _nextIcon(context), + onTap: () async{ + + }, + ), + const Divider(height: 1,), + ], + ), + ), + ); + } + + Widget _nextIcon(BuildContext context) => + const Icon(Icons.chevron_right, color: Colors.grey); + + Widget buildBottom(BuildContext context) { + return Wrap( + direction: Axis.vertical, + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 4, + children: [ + FeedbackWidget( + onPressed: (){ + _launchURL("https://github.com/toly1994328/FlutterUnit"); + }, + 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 © 2018-2024 Toly1994',style: TextStyle(fontSize: 12,color: Colors.grey),), + ], + ); + } + + void _launchURL(String url) async { + if (await canLaunch(url)) { + await launch(url); + } else { + + } + } +} 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/modules/basic_system/app/lib/view/setting/app_style_setting.dart b/modules/basic_system/app/lib/view/setting/app_style_setting.dart new file mode 100644 index 000000000..56d99b515 --- /dev/null +++ b/modules/basic_system/app/lib/view/setting/app_style_setting.dart @@ -0,0 +1,73 @@ +import 'package:app/app.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +/// create by 张风捷特烈 on 2020-04-10 +/// contact me by email 1981462002@qq.com +/// 说明: + + +void showAppStyleSelectDialog(BuildContext context) { + // List data = Cons.kAppStyleStringMap.values.toList(); + showCupertinoModalPopup( + context: context, + builder: (context) => AppThemeSettingDialog( + data: [], + )); +} + +class AppThemeSettingDialog extends StatelessWidget { + final List data; + + + const AppThemeSettingDialog({Key? key,required this.data}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Material( + child: SizedBox( + height: 350, + width: MediaQuery.of(context).size.width, + child: Column( + children: [ + const Padding( + padding: EdgeInsets.all(16.0), + child: Text( + '选择应用风格样式', + style: + TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + ), + const Divider(height: 1,), + Expanded( + child: ListView.builder( + padding: EdgeInsets.zero, + itemBuilder: _buildItem, + itemCount: data.length, + ), + ) + ], + ), + // color: Colors.orange, + ), + ); + } + + Widget? _buildItem(BuildContext context, int index) { + // AppStyle locale = Cons.kAppStyleStringMap.keys.toList()[index]; + // AppStyle style = BlocProvider.of(context).state.appStyle; + // bool checked = style == locale; + // Color color = Theme.of(context).primaryColor; + // return ListTile( + // title: Text(data[index]), + // onTap: () => _onSelect(context, index), + // trailing: checked ? Icon(Icons.check, size: 20, color: color) : null, + // ); + } + void _onSelect(BuildContext context, int index) { + // AppStyle appStyle = Cons.kAppStyleStringMap.keys.toList()[index]; + // BlocProvider.of(context).changeThemeMode(appStyle); + // Navigator.of(context).pop(); + } +} diff --git a/modules/basic_system/app/lib/view/setting/code_style_setting.dart b/modules/basic_system/app/lib/view/setting/code_style_setting.dart new file mode 100644 index 000000000..dba01045f --- /dev/null +++ b/modules/basic_system/app/lib/view/setting/code_style_setting.dart @@ -0,0 +1,88 @@ +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'; + +/// create by 张风捷特烈 on 2020-04-10 +/// contact me by email 1981462002@qq.com +/// 说明: + +class CodeStyleSettingPage extends StatelessWidget { + + const CodeStyleSettingPage({Key? key}) : super(key: key); + + final String code = """ +const String _kCounty = 'China'; + +class Hello { + final String name;//言语 + final String county;//国家 + final int age;//年龄 + + Hello({ + this.name = "张风捷特烈", + this.age = 26, + this.county = _kCounty + }); +}"""; + + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar(title: Text(context.l10n.codeHighlightStyle)), + body: BlocBuilder( + builder: (_, state) => _buildFontCell(context, + Cons.codeThemeSupport.keys.toList(), state.codeStyleIndex)), + ); + } + + Widget _buildFontCell( + BuildContext context, List styles, int index) { + return ListView.builder( + itemCount: styles.length, + itemBuilder: (_ctx, i) => FeedbackWidget( + a: 0.95, + duration: const Duration(milliseconds: 200), + onPressed: (){ + BlocProvider.of(context).switchCoderTheme(i); + }, + child: Stack( + fit: StackFit.passthrough, + children: [ + Card( + margin: const EdgeInsets.all(10), + child: CodeWidget( + code: code, + style: styles[i], + ), + ), + + Positioned( + right: 20, + bottom: 20, + child: Text(Cons.codeThemeSupport.values.toList()[i],style: TextStyle( + fontSize: 14, + color: styles[i].stringStyle!.color, + shadows: const [Shadow( + color: Colors.white, + offset: Offset(.5,.5), + blurRadius: 1 + ),] + ),), + ), + + if(index == i) + Positioned( + right: 20, + top: 20, + child: Circle(radius: 10, + color: Theme.of(context).primaryColor, + child: const Icon(Icons.check,color:Colors.white,size: 15,),), + ) + ], + ), + )); + } +} diff --git a/modules/basic_system/app/lib/view/setting/font_setting.dart b/modules/basic_system/app/lib/view/setting/font_setting.dart new file mode 100644 index 000000000..8321d6b9c --- /dev/null +++ b/modules/basic_system/app/lib/view/setting/font_setting.dart @@ -0,0 +1,100 @@ +import 'package:app/app.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +/// create by 张风捷特烈 on 2020-04-10 +/// contact me by email 1981462002@qq.com +/// 说明: + +class FontSettingPage extends StatelessWidget { + const FontSettingPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (_, state) =>Scaffold( + appBar: AppBar(title: Text('字体设置 - font setting',style: TextStyle(fontFamily:state.fontFamily ),)), + body: _buildFontCell( + context, Cons.kFontFamilySupport, state.fontFamily)), + ); + } + + Widget _buildFontCell( + BuildContext context, List fontFamilySupport, String fontFamily) { + return GridView.count( + padding: const EdgeInsets.only(top: 20, left: 10, right: 10), + crossAxisCount: kIsDesk?4:2, + mainAxisSpacing: 10, + crossAxisSpacing: 10, + childAspectRatio: 1.5, + children: fontFamilySupport.map((e) { + return FontCell( + active: fontFamily == e, + fontFamily: e, + onSelect: (font) { + BlocProvider.of(context).switchFontFamily(font); + }, + ); + }).toList(), + ); + } +} + +class FontCell extends StatelessWidget { + final bool active; + final ValueChanged onSelect; + final String fontFamily; + + const FontCell( + {Key? key, + required this.active, + required this.onSelect, + required this.fontFamily}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return FeedbackWidget( + a: 0.95, + duration: const Duration(milliseconds: 200), + onPressed: () => onSelect(fontFamily), + child: GridTile( + header: Container( + padding: const EdgeInsets.only(left: 10, right: 5), + height: 30, + color: active + ? Colors.blue.withAlpha(88) + : Colors.grey.withAlpha(88), + child: Row( + children: [ + Text(fontFamily, + style: TextStyle( + color: Colors.black, + fontFamily: fontFamily, + )), + const Spacer(), + if (active) + Circle(color: Theme.of(context).primaryColor) + ], + ), + ), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(8), + bottomRight: Radius.circular(8), + ), + gradient: LinearGradient(colors: [ + Colors.blueAccent.withAlpha(22), + Colors.blueAccent.withAlpha(22), + Theme.of(context).primaryColor.withAlpha(88) + ])), + alignment: const Alignment(0, 0.4), + child: Text( + '张风捷特烈\n@toly1994', + style: TextStyle(fontFamily: fontFamily, fontSize: 16), + )), + )); + } +} diff --git a/modules/basic_system/app/lib/view/setting/item_style_setting.dart b/modules/basic_system/app/lib/view/setting/item_style_setting.dart new file mode 100644 index 000000000..6037a5884 --- /dev/null +++ b/modules/basic_system/app/lib/view/setting/item_style_setting.dart @@ -0,0 +1,58 @@ +// import 'package:app/app.dart'; +// import 'package:components/components.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; +// +// /// create by 张风捷特烈 on 2020-04-10 +// /// contact me by email 1981462002@qq.com +// /// 说明: item样式切换支持 +// +// class ItemStyleSettingPage extends StatelessWidget { +// const ItemStyleSettingPage({Key? key}) : super(key: key); +// +// @override +// Widget build(BuildContext context) { +// return Scaffold( +// backgroundColor: UnitColor.scaffoldBgLight, +// appBar: const UnitAppbar(title: 'item样式设置'), +// body: BlocBuilder(builder: (_, state) { +// return _buildCell(context, state.itemStyleIndex); +// }), +// ); +// } +// +// List get items=> HomeItemSupport.itemSimples(); +// +// Widget _buildCell(BuildContext context, int index) { +// return ListView.builder( +// itemCount: items.length, +// itemBuilder: (_, i) => Padding( +// padding: const EdgeInsets.only(bottom: 8,left: 8,right: 8), +// child: FeedbackWidget( +// a: 0.95, +// duration: const Duration(milliseconds: 200), +// onPressed: () { +// BlocProvider.of(context).changeItemStyle(i); +// }, +// child: Stack( +// children: [ +// items[i], +// if (index == i) +// Positioned( +// left: 25, +// top: 15, +// child: Circle( +// color: Theme.of(context).primaryColor, +// radius: 10, +// child: const Icon( +// Icons.check, +// color: Colors.white, +// size: 15, +// ), +// ), +// ) +// ], +// )), +// )); +// } +// } 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/views/pages/unit_todo/attr_unit_page.dart b/modules/basic_system/app/lib/view/unit_todo/attr_unit_page.dart similarity index 85% rename from lib/views/pages/unit_todo/attr_unit_page.dart rename to modules/basic_system/app/lib/view/unit_todo/attr_unit_page.dart index afb2dc215..7b58610a0 100644 --- a/lib/views/pages/unit_todo/attr_unit_page.dart +++ b/modules/basic_system/app/lib/view/unit_todo/attr_unit_page.dart @@ -1,17 +1,18 @@ +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/text_typer.dart'; class AttrUnitPage extends StatelessWidget { - final info = '【Flutter属性集录】是Unit项目计划的第二阶段的功能之一。' + final String info = '【Flutter属性集录】是Unit项目计划的第二阶段的功能之一。' '会对所有Widget的所有属性进行收录整理到数据库,进行数据分析和组件关联。' '并且对一些重要属性,进行全面讲解。'; + const AttrUnitPage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('属性集录'), + title: const Text('属性集录'), ), body: Stack( alignment: Alignment.center, @@ -19,7 +20,7 @@ class AttrUnitPage extends StatelessWidget { Positioned( top: 50, child: Column( - children: [ + children: const [ CircleImage( image: AssetImage('assets/images/icon_head.webp'), size: 80, @@ -38,13 +39,13 @@ class AttrUnitPage extends StatelessWidget { ), Container( alignment: Alignment.center, - padding: EdgeInsets.all(20), + padding: const EdgeInsets.all(20), child: ShaderMask( shaderCallback: (rect) => _buildShader(rect, Theme.of(context).primaryColor), child: TextTyper( text:info, - textStyle: TextStyle( + textStyle: const TextStyle( shadows: [ Shadow( color: Colors.black, @@ -73,7 +74,7 @@ class AttrUnitPage extends StatelessWidget { .createShader(bounds); Widget buildPower() { - return Positioned( + return const Positioned( bottom: 30, right: 30, child: diff --git a/lib/views/pages/unit_todo/layout_unit_page.dart b/modules/basic_system/app/lib/view/unit_todo/layout_unit_page.dart similarity index 86% rename from lib/views/pages/unit_todo/layout_unit_page.dart rename to modules/basic_system/app/lib/view/unit_todo/layout_unit_page.dart index 29da0c28b..ae7beee78 100644 --- a/lib/views/pages/unit_todo/layout_unit_page.dart +++ b/modules/basic_system/app/lib/view/unit_todo/layout_unit_page.dart @@ -1,27 +1,32 @@ +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/text_typer.dart'; + class LayoutUnitPage extends StatelessWidget { - final info = '【Flutter布局集录】是Unit项目计划的第二阶段的功能之一。' + + const LayoutUnitPage({Key? key}) : super(key: key); + + + final String info = '【Flutter布局集录】是Unit项目计划的第二阶段的功能之一。' '将收录大量的布局样板,一者,方便直接使用;二者,方便布局的学习。' '本集录将支持布局征集,愿开发者共同集录。'; + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('布局集录'), + title: const Text('布局集录'), ), body: Stack( alignment: Alignment.center, - children: [ + children: [ Positioned( top: 50, child: Column( - children: [ + children: const [ CircleImage( image: AssetImage('assets/images/icon_head.webp'), size: 80, @@ -40,13 +45,13 @@ class LayoutUnitPage extends StatelessWidget { ), Container( alignment: Alignment.center, - padding: EdgeInsets.all(20), + padding: const EdgeInsets.all(20), child: ShaderMask( shaderCallback: (rect) => _buildShader(rect, Theme.of(context).primaryColor), child: TextTyper( text:info, - textStyle: TextStyle( + textStyle: const TextStyle( shadows: [ Shadow( color: Colors.black, @@ -76,7 +81,7 @@ class LayoutUnitPage extends StatelessWidget { .createShader(bounds); Widget buildPlan() { - return Positioned( + return const Positioned( bottom: 80, child: Text("Flutter Unit 布局征集方案(待完成)", @@ -94,7 +99,7 @@ class LayoutUnitPage extends StatelessWidget { } Widget buildPower() { - return Positioned( + return const Positioned( bottom: 30, right: 30, child: diff --git a/lib/views/pages/unit_todo/paint_unit_page.dart b/modules/basic_system/app/lib/view/unit_todo/paint_unit_page.dart similarity index 86% rename from lib/views/pages/unit_todo/paint_unit_page.dart rename to modules/basic_system/app/lib/view/unit_todo/paint_unit_page.dart index 57a2f73a7..bf9ae950c 100644 --- a/lib/views/pages/unit_todo/paint_unit_page.dart +++ b/modules/basic_system/app/lib/view/unit_todo/paint_unit_page.dart @@ -1,19 +1,19 @@ - - +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/text_typer.dart'; + class PaintUnitPage extends StatelessWidget { - final info = '【Flutter绘制集录】是Unit项目计划的第二阶段的功能之一。' + final String info = '【Flutter绘制集录】是Unit项目计划的第二阶段的功能之一。' '将收录大量绘制作品,展现Flutter强大的绘制表现力,' '以供学习绘制技能。本集录将支持绘制征集,愿开发者共同集录。'; + const PaintUnitPage({Key? key}) : super(key: key); + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('绘制集录'), + title: const Text('绘制集录'), ), body: Stack( alignment: Alignment.center, @@ -21,7 +21,7 @@ class PaintUnitPage extends StatelessWidget { Positioned( top: 50, child: Column( - children: [ + children: const [ CircleImage( image: AssetImage('assets/images/icon_head.webp'), size: 80, @@ -40,13 +40,13 @@ class PaintUnitPage extends StatelessWidget { ), Container( alignment: Alignment.center, - padding: EdgeInsets.all(20), + padding: const EdgeInsets.all(20), child: ShaderMask( shaderCallback: (rect) => _buildShader(rect, Theme.of(context).primaryColor), child: TextTyper( text:info, - textStyle: TextStyle( + textStyle: const TextStyle( shadows: [ Shadow( color: Colors.black, @@ -76,7 +76,7 @@ class PaintUnitPage extends StatelessWidget { .createShader(bounds); Widget buildPlan() { - return Positioned( + return const Positioned( bottom: 80, child: Text("Flutter Unit 绘制征集方案(待完成)", @@ -94,7 +94,7 @@ class PaintUnitPage extends StatelessWidget { } Widget buildPower() { - return Positioned( + return const Positioned( bottom: 30, right: 30, child: diff --git a/lib/views/pages/unit_todo/point_unit_page.dart b/modules/basic_system/app/lib/view/unit_todo/point_unit_page.dart similarity index 79% rename from lib/views/pages/unit_todo/point_unit_page.dart rename to modules/basic_system/app/lib/view/unit_todo/point_unit_page.dart index 0bf31284e..92ac9be8b 100644 --- a/lib/views/pages/unit_todo/point_unit_page.dart +++ b/modules/basic_system/app/lib/view/unit_todo/point_unit_page.dart @@ -1,22 +1,21 @@ - - +import 'package:app/app.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/permanent/text_typer.dart'; - +import 'package:toly_ui/toly_ui.dart'; class BugUnitPage extends StatelessWidget { - final info = '【Flutter要点集录】是Unit项目计划的第二阶段的功能之一。' + const BugUnitPage({Key? key}) : super(key: key); + + + final String info = '【Flutter要点集录】是Unit项目计划的第二阶段的功能之一。' '将收录Flutter的常见异常及解决方案,也可以是Flutter中的特点或注意点,' '以供学习参考。本集录将支持异常/特色征集,愿开发者共同集录。'; + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: Text('要点集录'), + title: const Text('要点集录'), ), body: Stack( alignment: Alignment.center, @@ -27,15 +26,15 @@ class BugUnitPage extends StatelessWidget { children: [ FeedbackWidget( onPressed: (){ - Navigator.of(context).pushNamed(UnitRouter.issues_point); + // Navigator.of(context).pushNamed(UnitRouter.issues_point); }, - child: CircleImage( + child: const CircleImage( image: AssetImage('assets/images/icon_head.webp'), size: 80, ), ), - SizedBox(height: 10,), - Text( + const SizedBox(height: 10,), + const Text( 'Flutter Unit 2.0 计划', style: TextStyle( color: Colors.green, @@ -48,13 +47,13 @@ class BugUnitPage extends StatelessWidget { ), Container( alignment: Alignment.center, - padding: EdgeInsets.all(20), + padding: const EdgeInsets.all(20), child: ShaderMask( shaderCallback: (rect) => _buildShader(rect, Theme.of(context).primaryColor), child: TextTyper( text:info, - textStyle: TextStyle( + textStyle: const TextStyle( shadows: [ Shadow( color: Colors.black, @@ -62,7 +61,6 @@ class BugUnitPage extends StatelessWidget { blurRadius: 1) ], color: Colors.white, -// color: Theme.of(context).primaryColor, fontSize: 20, fontWeight: FontWeight.bold, ), @@ -84,7 +82,7 @@ class BugUnitPage extends StatelessWidget { .createShader(bounds); Widget buildPlan() { - return Positioned( + return const Positioned( bottom: 80, child: Text("Flutter Unit 异常/特色 征集方案(待完成)", @@ -102,7 +100,7 @@ class BugUnitPage extends StatelessWidget { } Widget buildPower() { - return Positioned( + return const Positioned( bottom: 30, right: 30, child: 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/views/components/project/overlay_tool_wrapper.dart b/modules/basic_system/app/lib/view/wrapper/overlay_tool_wrapper.dart similarity index 82% rename from lib/views/components/project/overlay_tool_wrapper.dart rename to modules/basic_system/app/lib/view/wrapper/overlay_tool_wrapper.dart index 3f6517189..0e3a3b106 100644 --- a/lib/views/components/project/overlay_tool_wrapper.dart +++ b/modules/basic_system/app/lib/view/wrapper/overlay_tool_wrapper.dart @@ -1,11 +1,6 @@ +import 'package:app/app.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/res/toly_icon.dart'; -import 'package:flutter_unit/app/router/unit_router.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; - -import '../permanent/burst_menu.dart'; -import '../permanent/color_wrapper.dart'; /// create by 张风捷特烈 on 2020/10/21 /// contact me by email 1981462002@qq.com @@ -14,18 +9,17 @@ import '../permanent/color_wrapper.dart'; class OverlayToolWrapper extends StatefulWidget { final Widget child; - OverlayToolWrapper({Key key, this.child}) : super(key: key); + const OverlayToolWrapper({Key? key,required this.child}) : super(key: key); @override OverlayToolWrapperState createState() => OverlayToolWrapperState(); static OverlayToolWrapperState of(BuildContext context, {bool nullOk = false}) { - assert(nullOk != null); - assert(context != null); - final OverlayToolWrapperState result = + + final OverlayToolWrapperState? result = context.findAncestorStateOfType(); - if (nullOk || result != null) return result; + if (result != null) return result; throw FlutterError.fromParts([ ErrorSummary( 'OverlayToolWrapper.of() called with a context that does not contain a OverlayToolWrapper.'), @@ -36,9 +30,9 @@ class OverlayToolWrapper extends StatefulWidget { class OverlayToolWrapperState extends State with SingleTickerProviderStateMixin { bool show = false; - Offset offset = Offset(200, 200); + late Offset offset; - AnimationController _ctrl; + late AnimationController _ctrl; final double width = 200; final double height = 30; @@ -46,7 +40,7 @@ class OverlayToolWrapperState extends State final double boxHeight = 110; final double radius = 60; - OverlayEntry entry; + OverlayEntry? entry; double showWidth = 0; bool out = false; @@ -56,13 +50,13 @@ class OverlayToolWrapperState extends State super.initState(); _ctrl = AnimationController( - duration: Duration(milliseconds: 400), + duration: const Duration(milliseconds: 400), vsync: this, )..addListener(_listenAnimate); WidgetsBinding.instance.addPostFrameCallback((callback) { var px = MediaQuery.of(context).size.width - 100; - var py = 250.0; + var py = MediaQuery.of(context).size.height*0.05; offset = Offset(px, py); entry = OverlayEntry( @@ -91,7 +85,7 @@ class OverlayToolWrapperState extends State alignment: Alignment.center, // color: Colors.orangeAccent, child: IconTheme( - data: IconTheme.of(context).copyWith(color: Colors.white, size: 18), + data: const IconThemeData(color: Colors.white, size: 18), child: BurstMenu( startAngle: !left ? 90.0 + 15 : -90 - 15.0 + 180, swapAngle: !left ? 180.0 - 15 * 2 : -(180.0 - 15 * 2), @@ -117,8 +111,8 @@ class OverlayToolWrapperState extends State child: Container( decoration: BoxDecoration( color: Colors.blue, - image: DecorationImage( - image: const AssetImage('assets/images/icon_head.webp')), + image: const DecorationImage( + image: AssetImage('assets/images/icon_head.webp')), borderRadius: BorderRadius.circular(menuSize / 2)), ), ), @@ -140,7 +134,6 @@ class OverlayToolWrapperState extends State case 0: _doClose(); return true; - break; case 1: _toPoint(); break; @@ -160,18 +153,18 @@ class OverlayToolWrapperState extends State // 处理 菜单 item 点击事件 void _toSetting() { - Navigator.of(context).pushNamed(UnitRouter.setting); + // Navigator.of(context).pushNamed(UnitRouter.setting); } void _toWidget() {} void _toGalley() { - Navigator.of(context).pushNamed(UnitRouter.galley); + } 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() { @@ -180,7 +173,7 @@ class OverlayToolWrapperState extends State } } - double endX; + double endX=0; void _onPanEnd(details) { endX = offset.dx; @@ -210,7 +203,7 @@ class OverlayToolWrapperState extends State } offset = Offset(px, offset.dy); - entry.markNeedsBuild(); + entry?.markNeedsBuild(); } void _updatePosition(DragUpdateDetails details) { @@ -232,19 +225,19 @@ class OverlayToolWrapperState extends State y = MediaQuery.of(context).size.height - menuSize / 2 - circleRadius; } offset = Offset(x, y); - entry.markNeedsBuild(); + entry?.markNeedsBuild(); } void showFloating() { - if (!show) { - Overlay.of(context).insert(entry); + if (!show&&entry!=null) { + Overlay.of(context)?.insert(entry!); show = true; } } void hideFloating() { if (show) { - entry.remove(); + entry?.remove(); show = false; } } 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/modules/basic_system/app/test/app_config_test.dart b/modules/basic_system/app/test/app_config_test.dart new file mode 100644 index 000000000..a6ff66095 --- /dev/null +++ b/modules/basic_system/app/test/app_config_test.dart @@ -0,0 +1,12 @@ +// import 'package:flutter_test/flutter_test.dart'; +// +// import 'package:app_config/app_config.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/basic_system/app_update/.gitignore b/modules/basic_system/app_update/.gitignore new file mode 100644 index 000000000..96486fd93 --- /dev/null +++ b/modules/basic_system/app_update/.gitignore @@ -0,0 +1,30 @@ +# 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/modules/basic_system/app_update/.metadata b/modules/basic_system/app_update/.metadata new file mode 100644 index 000000000..541c2f65c --- /dev/null +++ b/modules/basic_system/app_update/.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: 135454af32477f815a7525073027a3ff9eff1bfd + channel: stable + +project_type: package diff --git a/modules/basic_system/app_update/CHANGELOG.md b/modules/basic_system/app_update/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/basic_system/app_update/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/basic_system/app_update/LICENSE b/modules/basic_system/app_update/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/basic_system/app_update/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/basic_system/app_update/README.md b/modules/basic_system/app_update/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/basic_system/app_update/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/basic_system/app_update/analysis_options.yaml b/modules/basic_system/app_update/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/basic_system/app_update/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/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/modules/basic_system/app_update/lib/views/update_red_point.dart b/modules/basic_system/app_update/lib/views/update_red_point.dart new file mode 100644 index 000000000..607b7df14 --- /dev/null +++ b/modules/basic_system/app_update/lib/views/update_red_point.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../bloc/bloc.dart'; +import '../bloc/state.dart'; + + + +class UpdateRedPoint extends StatelessWidget { + const UpdateRedPoint({super.key}); + + @override + Widget build(BuildContext context) { + Widget radPoint = Container( + width: 8, + height: 8, + decoration: const BoxDecoration(color: Colors.red, shape: BoxShape.circle), + ); + return BlocBuilder( + builder: (BuildContext context, UpdateState state) { + if (state is ShouldUpdateState) { + return radPoint; + } else { + return const SizedBox.shrink(); + } + }, + ); + } +} 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/modules/basic_system/authentication/.gitignore b/modules/basic_system/authentication/.gitignore new file mode 100644 index 000000000..96486fd93 --- /dev/null +++ b/modules/basic_system/authentication/.gitignore @@ -0,0 +1,30 @@ +# 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/modules/basic_system/authentication/.metadata b/modules/basic_system/authentication/.metadata new file mode 100644 index 000000000..fbfa6dfba --- /dev/null +++ b/modules/basic_system/authentication/.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: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + channel: stable + +project_type: package diff --git a/modules/basic_system/authentication/CHANGELOG.md b/modules/basic_system/authentication/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/basic_system/authentication/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/basic_system/authentication/LICENSE b/modules/basic_system/authentication/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/basic_system/authentication/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/basic_system/authentication/README.md b/modules/basic_system/authentication/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/basic_system/authentication/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/basic_system/authentication/analysis_options.yaml b/modules/basic_system/authentication/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/basic_system/authentication/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/basic_system/authentication/lib/authentication.dart b/modules/basic_system/authentication/lib/authentication.dart new file mode 100644 index 000000000..0147e563a --- /dev/null +++ b/modules/basic_system/authentication/lib/authentication.dart @@ -0,0 +1,18 @@ +library authentication; + +export 'repository/auth_repository.dart'; +export 'repository/impl/http_auth_repository.dart'; +export 'blocs/authentic/state.dart'; +export 'blocs/authentic/bloc.dart'; +export 'blocs/authentic/event.dart'; +export 'blocs/register/event.dart'; +export 'blocs/register/state.dart'; +export 'blocs/register/bloc.dart'; +export 'blocs/user/bloc.dart'; +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/modules/basic_system/authentication/lib/blocs/authentic/bloc.dart b/modules/basic_system/authentication/lib/blocs/authentic/bloc.dart new file mode 100644 index 000000000..5ce6c466b --- /dev/null +++ b/modules/basic_system/authentication/lib/blocs/authentic/bloc.dart @@ -0,0 +1,112 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:app/app.dart'; +import 'package:authentication/authentication.dart'; +import 'package:authentication/models/user.dart'; +import 'package:bloc/bloc.dart'; + +import 'package:jwt_decoder/jwt_decoder.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:utils/utils.dart'; + +import 'event.dart'; +import 'state.dart'; + +class AuthBloc extends Bloc { + + final AuthRepository repository; + + AuthBloc({required this.repository}) : super(const AuthInitial()){ + on(_onAppStarted); + on(_onAuthByPassword); + on(_onAuthByRegister); + on(_onLoggedOut); + } + + void _onAppStarted(AuthEvent event, Emitter emit) async{ + SharedPreferences sp = await SharedPreferences.getInstance(); + + if (event is AppStarted) { + String? token = sp.getString(SpKey.tokenKey); + String? userJson = sp.getString(SpKey.userKey); + if (token != null && userJson != null) { + bool disable = JwtDecoder.isExpired(token); + if (!disable) { + HttpUtil.instance.setToken(token); + emit(AuthSuccess(User.fromJson(json.decode(userJson)))); + }else{ + // 说明 token 过期 + await _removeToken(sp); + await _removeUser(sp); + } + } + } + + if (event is Logout) { + + } + } + + // 持久化 token + Future _persistToken(String token,SharedPreferences sp) async { + await sp.setString(SpKey.tokenKey, token); + } + + + // 持久化 token + Future _removeToken(SharedPreferences sp) async { + await sp.remove(SpKey.tokenKey); + } + + // 持久化 token + Future _removeUser(SharedPreferences sp) async { + await sp.remove(SpKey.userKey); + } + + // 持久化 user + Future _persistUser(User user,SharedPreferences sp) async { + await sp.setString(SpKey.userKey, json.encode(user)); + } + + + + FutureOr _onAuthByPassword(AuthByPassword event, Emitter emit) async{ + emit (AuthLoading()); + await Future.delayed(const Duration(milliseconds: 500)); + TaskResult result = await repository.login(username: event.username, password: event.password); + + if (result.success&& result.data!=null) { + // 登录成功 + SharedPreferences sp = await SharedPreferences.getInstance(); + HttpUtil.instance.setToken(result.msg); + await _persistToken(result.msg,sp); + await _persistUser(result.data!,sp); + emit (AuthSuccess(result.data!)); + } else { + emit (const AuthFailure('用户名和密码不匹配')); + } + } + + FutureOr _onAuthByRegister(AuthByRegister event, Emitter emit) async{ + emit(AuthLoading()); + TaskResult result = await repository.register(email: event.email, code: event.code); + + // if(result.data == null){ + // emit(const RegisterError('注册失败')); + // }else{ + // if (result.data!=null&&result.data!) { + // // 注册成功 + // emit( RegisterSuccess(event.email)); + // }else{ + // emit( RegisterError(result.msg)); + // } + // } + } + + FutureOr _onLoggedOut(Logout event, Emitter emit) async{ + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.remove(SpKey.tokenKey); + emit(const AuthInitial()); + } +} diff --git a/modules/basic_system/authentication/lib/blocs/authentic/event.dart b/modules/basic_system/authentication/lib/blocs/authentic/event.dart new file mode 100644 index 000000000..1bfd237dd --- /dev/null +++ b/modules/basic_system/authentication/lib/blocs/authentic/event.dart @@ -0,0 +1,46 @@ +import 'package:equatable/equatable.dart'; + +///********************************验证行为******************************** + +abstract class AuthEvent extends Equatable { + const AuthEvent(); + + @override + List get props => []; +} + +class AppStarted extends AuthEvent { + const AppStarted(); +} + +// 发送 邮箱验证 +class AuthByPassword extends AuthEvent { + final String username; + final String password; + + const AuthByPassword({required this.username,required this.password}); + + @override + List get props => [username,password]; +} + +// 用户注册也是认证的一部分 +class AuthByRegister extends AuthEvent{ + final String email; + final String code; + + const AuthByRegister(this.email, this.code); +} + +class Logout extends AuthEvent { + + final bool clearUser; + final bool tokenDisable; + + const Logout({this.clearUser=true,this.tokenDisable=false}); +} + +class TokenDisabled extends AuthEvent { + + const TokenDisabled(); +} \ No newline at end of file diff --git a/modules/basic_system/authentication/lib/blocs/authentic/state.dart b/modules/basic_system/authentication/lib/blocs/authentic/state.dart new file mode 100644 index 000000000..13a074b76 --- /dev/null +++ b/modules/basic_system/authentication/lib/blocs/authentic/state.dart @@ -0,0 +1,53 @@ + +import 'package:authentication/models/user.dart'; +import 'package:equatable/equatable.dart'; + +enum AuthType{ + login, + register, + visitor +} + +///********************************校验状态******************************** +abstract class AuthState extends Equatable { + const AuthState(); + @override + List get props => []; +} + +class AuthInitial extends AuthState { +const AuthInitial(); +} + + +class AuthFailure extends AuthState { + final String error; + + const AuthFailure(this.error); + + @override + List get props => [error]; + + @override + String toString() { + return 'AuthFailure{message: $error}'; + } +} + + +class LogOuted extends AuthState {} + +class AuthSuccess extends AuthState { + final User user; + + const AuthSuccess(this.user); + + @override + String toString() { + return 'AuthSuccess{loginResult: $user}'; + } +} + +class AuthLoading extends AuthState { + +} \ No newline at end of file diff --git a/modules/basic_system/authentication/lib/blocs/register/bloc.dart b/modules/basic_system/authentication/lib/blocs/register/bloc.dart new file mode 100644 index 000000000..51c03b35f --- /dev/null +++ b/modules/basic_system/authentication/lib/blocs/register/bloc.dart @@ -0,0 +1,37 @@ +// import 'package:authentication/authentication.dart'; +// import 'package:flutter_bloc/flutter_bloc.dart'; +// import 'package:utils/algorithm.dart'; +// +// import 'event.dart'; +// import 'state.dart'; +// +// /// create by 张风捷特烈 on 2021/1/17 +// /// contact me by email 1981462002@qq.com +// /// 说明: +// +// class RegisterBloc extends Bloc { +// final AuthRepository repository; +// +// RegisterBloc({required this.repository}) : super(RegisterNone()){ +// on(_onRegisterEvent); +// } +// +// void _onRegisterEvent(RegisterEvent event,Emitter emit) async { +// if (event is DoRegister) { +// emit(RegisterLoading()); +// TaskResult result = +// await repository.register(email: event.email, code: event.code); +// +// if(result.data == null){ +// emit(const RegisterError('注册失败')); +// }else{ +// if (result.data!=null&&result.data!) { +// // 注册成功 +// emit( RegisterSuccess(event.email)); +// }else{ +// emit( RegisterError(result.msg)); +// } +// } +// } +// } +// } diff --git a/modules/basic_system/authentication/lib/blocs/register/event.dart b/modules/basic_system/authentication/lib/blocs/register/event.dart new file mode 100644 index 000000000..fbbd19bbf --- /dev/null +++ b/modules/basic_system/authentication/lib/blocs/register/event.dart @@ -0,0 +1,20 @@ +// import 'package:equatable/equatable.dart'; +// +// /// create by 张风捷特烈 on 2020-03-03 +// /// contact me by email 1981462002@qq.com +// /// 说明: +// +// abstract class RegisterEvent extends Equatable { +// const RegisterEvent(); +// +// @override +// List get props => []; +// } +// +// // 发送 邮箱验证 +// class DoRegister extends RegisterEvent { +// final String email; +// final String code; +// +// const DoRegister(this.email, this.code); +// } diff --git a/modules/basic_system/authentication/lib/blocs/register/state.dart b/modules/basic_system/authentication/lib/blocs/register/state.dart new file mode 100644 index 000000000..1389c43e0 --- /dev/null +++ b/modules/basic_system/authentication/lib/blocs/register/state.dart @@ -0,0 +1,46 @@ +// import 'package:equatable/equatable.dart'; +// +// /// create by 张风捷特烈 on 2020-03-03 +// /// contact me by email 1981462002@qq.com +// /// 说明: 主页 Widget 列表 状态类 +// +// abstract class RegisterState extends Equatable { +// const RegisterState(); +// +// @override +// List get props => []; +// } +// +// class RegisterLoading extends RegisterState { +// @override +// List get props => []; +// } +// +// class RegisterNone extends RegisterState { +// @override +// List get props => []; +// } +// +// +// class RegisterError extends RegisterState { +// final String message; +// +// const RegisterError(this.message); +// +// @override +// List get props => [message]; +// +// @override +// String toString() { +// return 'RegisterError{message: $message}'; +// } +// } +// +// class RegisterSuccess extends RegisterState { +// final String username; +// +// const RegisterSuccess(this.username); +// +// @override +// List get props => [username]; +// } 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/modules/basic_system/authentication/lib/blocs/user/state.dart b/modules/basic_system/authentication/lib/blocs/user/state.dart new file mode 100644 index 000000000..a21bd9892 --- /dev/null +++ b/modules/basic_system/authentication/lib/blocs/user/state.dart @@ -0,0 +1,82 @@ + + +class UserPerformance { + final String username; + final int userId; + final String phone; + final String email; + final int emailVerified; + final String weiChat; + final String avatar; + + + String get showEmail { + String prefix = email.split('@')[0]; + String tail = email.split('@')[1]; + String result = ''; + if (prefix.length > 8) { + result = + "${prefix.substring(0, 3)}***${prefix.substring(prefix.length - 2)}"; + } else { + result = prefix; + } + return '$result@$tail'; + } + + String get showPhone => + "${phone.substring(0, 3)}***${phone.substring(phone.length - 4)}"; + + UserPerformance({ + required this.username, + required this.userId, + required this.email, + required this.emailVerified, + required this.weiChat, + required this.phone, + required this.avatar, + }); + + UserPerformance copyWith({ + String? username, + int? userId, + String? phone, + String? avatar, + String? email, + int? emailVerified, + String? weiChat, + bool? hasPwd, + int? pollenCoin, + int? honeyCoin, + }) => + UserPerformance( + weiChat: weiChat ?? this.weiChat, + username: username ?? this.username, + userId: userId ?? this.userId, + phone: phone ?? this.phone, + avatar: avatar ?? this.avatar, + email: email ?? this.email, + emailVerified: emailVerified ?? this.emailVerified, + ); + + static UserPerformance fromJson(Map map) => UserPerformance( + username: map["username"] ?? '', + userId: map["userId"] ?? 0, + phone: map["phone"] ?? '', + avatar: map["avatar"] ?? '', + email: map["email"] ?? '', + weiChat: map["weiChat"] ?? '', + emailVerified: map["emailVerified"] ?? 0, + ); + + Map toJson() => { + "username": username, + "userId": userId, + "phone": phone, + "avatar": avatar, + "weiChat": weiChat, + "email": email, + "emailVerified": emailVerified, + }; + + +} \ No newline at end of file diff --git a/modules/basic_system/authentication/lib/models/user.dart b/modules/basic_system/authentication/lib/models/user.dart new file mode 100644 index 000000000..cbfa691bc --- /dev/null +++ b/modules/basic_system/authentication/lib/models/user.dart @@ -0,0 +1,52 @@ +import 'package:equatable/equatable.dart'; + +/// create by 张风捷特烈 on 2021/1/17 +/// contact me by email 1981462002@qq.com +/// 说明: + +// "userId": 1302422300380954625, +// "username": "toly", +// "email": "1981462001@qq.com", +// "activeCode": 1, +// "roles": "admin", +// "createAt": "2020-09-06T01:44:09.000+00:00", +// "updateAt": "2020-12-12T06:35:22.000+00:00" + +class User extends Equatable { + final String username; + final String userAvatar; + final String email; + final String roles; + final int userId; + + const User({ + required this.username, + required this.email, + required this.roles, + required this.userId, + required this.userAvatar, + }); + + factory User.fromJson(Map map) { + return User( + username: map['username'], + email: map['email'], + roles: map['roles'], + userId: map['userId'], + userAvatar: map['userAvatar'], + ); + } + + bool get isHonour => roles.contains('honour'); + + @override + List get props => [username, email, roles, userId, userAvatar]; + + Map toJson() => { + "username": username, + "email": email, + "roles": roles, + "userId": userId, + "userAvatar": userAvatar, + }; +} diff --git a/modules/basic_system/authentication/lib/repository/auth_repository.dart b/modules/basic_system/authentication/lib/repository/auth_repository.dart new file mode 100644 index 000000000..adcd817ee --- /dev/null +++ b/modules/basic_system/authentication/lib/repository/auth_repository.dart @@ -0,0 +1,24 @@ +import 'package:utils/utils.dart'; + +import '../models/user.dart'; + +abstract class AuthRepository { + + // 用户登录接口 + Future> login({ + required String username, + required String password, + }); + + // 用户注册接口 + // 邮箱注册 + Future> register({ + required String email, + required String code, + }); + + // 发送邮箱验证信息 + Future> sendEmail({ + required String email, + }); +} diff --git a/modules/basic_system/authentication/lib/repository/impl/http_auth_repository.dart b/modules/basic_system/authentication/lib/repository/impl/http_auth_repository.dart new file mode 100644 index 000000000..ccb37ac1b --- /dev/null +++ b/modules/basic_system/authentication/lib/repository/impl/http_auth_repository.dart @@ -0,0 +1,88 @@ +import 'package:authentication/models/user.dart'; + +import 'package:utils/src/http_utils/task_result.dart'; +import 'package:utils/utils.dart'; + +import '../auth_repository.dart'; + +const String kSendEmail = '/sendEmail/'; +const String kLogin = '/login'; +const String kRegister = '/register'; + +class HttpAuthRepository implements AuthRepository { + @override + Future> login({ + required String username, + required String password, + }) async { + String errorMsg = ""; + + try { + var result = await HttpUtil.instance.client.post( + kLogin, + data: { + "username": username, + "password": password, + }, + ); + + if (result.data != null) { + if (result.data['status']) { + return TaskResult( + msg: result.data['msg'], + data: User.fromJson(result.data['data']), + success: result.data['status'], + ); + } else { + return TaskResult( + msg: result.data['msg'], + data: null, + success: false, + ); + } + } + } catch (e) { + errorMsg = e.toString(); + } + + return TaskResult.error(msg: '请求错误: $errorMsg'); + } + + @override + Future> register({ + required String email, + required String code, + }) async { + String errorMsg = ""; + + try { + var result = await HttpUtil.instance.client + .post(kRegister, data: {"email": email, "activeCode": code}); + + if (result.data != null) { + return TaskResult.success(data: result.data); + } + } catch (e) { + errorMsg = e.toString(); + } + + return TaskResult.error(msg: '请求错误: $errorMsg'); + } + + @override + Future> sendEmail({required String email}) async { + try { + var result = await HttpUtil.instance.client.post(kSendEmail + email); + if (result.data != null) { + if(result.data['status']){ + return TaskResult.success(data: result.data); + }else{ + return TaskResult.error(msg: result.data['msg']); + } + } + } catch (e) { + print(e); + } + return const TaskResult.error(msg: '请求错误'); + } +} diff --git a/modules/basic_system/authentication/lib/views/authentic_widget.dart b/modules/basic_system/authentication/lib/views/authentic_widget.dart new file mode 100644 index 000000000..b5b8c96e1 --- /dev/null +++ b/modules/basic_system/authentication/lib/views/authentic_widget.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../authentication.dart'; + +/// create by 张风捷特烈 on 2021/2/24 +/// contact me by email 1981462002@qq.com +/// 说明: + +class AuthenticWidget extends StatelessWidget { + final Widget authentic; + final Widget noAuthentic; + + const AuthenticWidget( + {Key? key, required this.authentic, required this.noAuthentic}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocBuilder( + builder: (_, state) { + return state is AuthSuccess ? authentic : noAuthentic; + }); + } + + factory AuthenticWidget.just(Widget authentic){ + return AuthenticWidget( + authentic: authentic, + noAuthentic: const SizedBox(), + ); + } +} diff --git a/modules/basic_system/authentication/lib/views/mobile/login/login_form.dart b/modules/basic_system/authentication/lib/views/mobile/login/login_form.dart new file mode 100644 index 000000000..372f8f4e4 --- /dev/null +++ b/modules/basic_system/authentication/lib/views/mobile/login/login_form.dart @@ -0,0 +1,275 @@ +import 'package:app/app.dart'; +import 'package:flutter/cupertino.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 '../../../authentication.dart'; + + +class LoginFrom extends StatefulWidget { + const LoginFrom({Key? key}) : super(key: key); + + @override + _LoginFromState createState() => _LoginFromState(); +} + +class _LoginFromState extends State { + final _usernameController = TextEditingController(text: '张风捷特烈'); + final _passwordController = TextEditingController(text: '111111'); + + bool _showPwd = false; + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + "Flutter Unit 登录", + style: TextStyle(fontSize: 25), + ), + const SizedBox( + height: 5, + ), + const Text( + "登录账号,更多精彩,更多体验 ~", + style: TextStyle(color: Colors.grey), + ), + const SizedBox( + height: 20, + ), + buildUsernameInput(), + Stack( + alignment: const Alignment(.8, 0), + children: [ + buildPasswordInput(), + FeedbackWidget( + onPressed: () => setState(() => _showPwd = !_showPwd), + child: Icon(_showPwd ? TolyIcon.icon_show : TolyIcon.icon_hide)) + ], + ), + // Row( + // children: [ + // Checkbox(value: true, onChanged: (e) => {}), + // const Text( + // "自动登录", + // style: TextStyle(color: Color(0xff444444), fontSize: 14), + // ), + // const Spacer(), + // + // ], + // ), + BlocConsumer( + listener: _listenLoginState, + builder: _buildBtnByState, + ), + GestureDetector( + onTap: () { + // Navigator.of(context).pushReplacementNamed(UnitRouter.register); + }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: const Text( + "没有账号,立即注册", + style: TextStyle( + color: Colors.blue, + fontSize: 14, + decoration: TextDecoration.underline,decorationColor: Colors.blue), + ), + ), + ), + // buildOtherLogin(), + const Spacer(flex: 4), + ], + ); + } + + void _doLogIn() { + print('---用户名:${_usernameController.text}------密码:${_passwordController.text}---'); + String username = _usernameController.text; + String password = _passwordController.text; + if (!_preValidate(username, password)) return; + BlocProvider.of(context).add( + AuthByPassword(username: username, password: password), + ); + } + + Widget buildUsernameInput() { + return Column( + children: [ + Container( + decoration: BoxDecoration( + border: Border.all( + color: Colors.grey.withOpacity(0.5), + width: 1.0, + ), + borderRadius: BorderRadius.circular(15.0), + ), + margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0), + child: Row( + children: [ + const Padding( + padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), + child: Icon( + Icons.person_outline, + color: Colors.grey, + ), + ), + Container( + height: 20.0, + width: 1.0, + color: Colors.grey.withOpacity(0.5), + margin: const EdgeInsets.only(left: 00.0, right: 10.0), + ), + Expanded( + child: TextField( + controller: _usernameController, + decoration: const InputDecoration( + border: InputBorder.none, + hintText: '请输入用户名...', + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ) + ], + ), + ) + ], + ); + } + + Widget buildPasswordInput() { + return Column( + children: [ + Container( + decoration: BoxDecoration( + border: Border.all( + color: Colors.grey.withOpacity(0.5), + width: 1.0, + ), + borderRadius: BorderRadius.circular(15.0), + ), + margin: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0), + child: Row( + children: [ + const Padding( + padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), + child: Icon( + Icons.lock_outline, + color: Colors.grey, + ), + ), + Container( + height: 30.0, + width: 1.0, + color: Colors.grey.withOpacity(0.5), + margin: const EdgeInsets.only(left: 00.0, right: 10.0), + ), + Expanded( + child: TextField( + obscureText: !_showPwd, + controller: _passwordController, + decoration: const InputDecoration( + border: InputBorder.none, + hintText: '请输入密码...', + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ) + ], + ), + ) + ], + ); + } + + Widget buildOtherLogin() { + return Wrap( + alignment: WrapAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.only(top: 30.0), + child: Row( + children: const [ + Expanded( + child: Divider( + height: 20, + )), + Padding( + padding: EdgeInsets.all(8.0), + child: Text( + '第三方登录', + style: TextStyle(color: Colors.grey), + ), + ), + Expanded( + child: Divider( + height: 20, + )), + ], + ), + ), + const Icon( + TolyIcon.icon_github, + color: Colors.black, + size: 30, + ) + ], + ); + } + + Widget _buildBtnByState(BuildContext context, AuthState state) { + if (state is AuthLoading) { + return Container( + margin: const EdgeInsets.only(top: 10, bottom: 0), + height: 40, + width: 40, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20))), + backgroundColor: Colors.blue.withOpacity(0.4), + ), + onPressed: _doLogIn, + child: const CupertinoActivityIndicator(), + )); + } + return Container( + margin: const EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 0), + height: 40, + width: MediaQuery.of(context).size.width, + child: ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20))), + backgroundColor: Colors.blue, + ), + onPressed: _doLogIn, + child: const Text("进入 Unit 世界", + style: TextStyle(color: Colors.white, fontSize: 14,fontWeight: FontWeight.bold)), + )); + } + + void _listenLoginState(BuildContext context, AuthState state) { + if (state is AuthSuccess) { + Navigator.of(context).pop(); + } + if (state is AuthFailure) { + Toast.toast(context, '登录失败 : ${state.error}!', + color: Colors.red, duration: const Duration(seconds: 2)); + } + } + + bool _preValidate(String username, String password) { + if (username.isEmpty || password.isEmpty) { + Toast.toast(context, '登录失败 : 用户名和密码不能为空!', + color: Colors.orange, duration: const Duration(seconds: 2)); + return false; + } + return true; + } +} diff --git a/modules/basic_system/authentication/lib/views/mobile/login/login_page.dart b/modules/basic_system/authentication/lib/views/mobile/login/login_page.dart new file mode 100644 index 000000000..ef87276c6 --- /dev/null +++ b/modules/basic_system/authentication/lib/views/mobile/login/login_page.dart @@ -0,0 +1,52 @@ +import 'package:authentication/authentication.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import '../register/arc_clipper.dart'; +import 'login_form.dart'; + +/// create by 张风捷特烈 on 2020/4/24 +/// contact me by email 1981462002@qq.com +/// 说明: + +// class AuthenticScope extends StatelessWidget { +// const AuthenticScope({Key? key}) : super(key: key); +// +// @override +// Widget build(BuildContext context) { +// final AuthRepository repository = context.read().repository; +// return MultiBlocProvider(providers: [ +// BlocProvider(create: (_) => RegisterBloc(repository: repository)), +// ], child: const LoginPage()); +// } +// } + +class LoginPage extends StatelessWidget { + const LoginPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + Size winSize = MediaQuery.of(context).size; + + return Scaffold( + body: SingleChildScrollView( + child: Wrap(children: [ + Stack(children: [ + UnitArcBackground(height: winSize.height * 0.3), + const Positioned(top: 24, child: BackButton(color: Colors.white)), + ]), + Container( + // color: Colors.green, + height: winSize.height * 0.68, + width: MediaQuery.of(context).size.width, + padding: const EdgeInsets.only(left: 20.0, right: 20, top: 10), + child: Stack( + alignment: Alignment.center, + children: const [ + LoginFrom(), + ], + )) + ]), + )); + } +} diff --git a/lib/user_system/pages/register/arc_clipper.dart b/modules/basic_system/authentication/lib/views/mobile/register/arc_clipper.dart similarity index 83% rename from lib/user_system/pages/register/arc_clipper.dart rename to modules/basic_system/authentication/lib/views/mobile/register/arc_clipper.dart index 4fbf5a8dc..3f1a66898 100644 --- a/lib/user_system/pages/register/arc_clipper.dart +++ b/modules/basic_system/authentication/lib/views/mobile/register/arc_clipper.dart @@ -1,5 +1,6 @@ +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/permanent/circle_image.dart'; + /// create by 张风捷特烈 on 2020/4/27 /// contact me by email 1981462002@qq.com @@ -27,10 +28,11 @@ class ArcClipper extends CustomClipper { } class ArcBackground extends StatelessWidget { - final Widget child; + final Widget? child; final ImageProvider image; - ArcBackground({this.child, this.image}); + const ArcBackground({Key? key, this.child, required this.image}) + : super(key: key); @override Widget build(BuildContext context) { @@ -52,19 +54,19 @@ class ArcBackground extends StatelessWidget { class UnitArcBackground extends StatelessWidget { final double height; - UnitArcBackground({Key key,this.height}) : super(key: key); + const UnitArcBackground({Key? key,required this.height}) : super(key: key); @override Widget build(BuildContext context) { return SizedBox( height: height, child: ArcBackground( - image: AssetImage("assets/images/caver.webp"), + image: const AssetImage("assets/images/caver.webp"), child: Container( padding: const EdgeInsets.all(30), decoration: BoxDecoration( color: Colors.blue.withAlpha(88), shape: BoxShape.circle), - child: CircleImage( + child: const CircleImage( size: 100, roundColor: Colors.blue, image: AssetImage( diff --git a/modules/basic_system/authentication/lib/views/mobile/register/register_page.dart b/modules/basic_system/authentication/lib/views/mobile/register/register_page.dart new file mode 100644 index 000000000..a4210538e --- /dev/null +++ b/modules/basic_system/authentication/lib/views/mobile/register/register_page.dart @@ -0,0 +1,199 @@ +import 'package:authentication/authentication.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'; + +import 'arc_clipper.dart'; +import 'send_code.dart'; + +/// create by 张风捷特烈 on 2020/4/24 +/// contact me by email 1981462002@qq.com +/// 说明: + +class RegisterPage extends StatefulWidget { + const RegisterPage({Key? key}) : super(key: key); + + @override + _RegisterPageState createState() => _RegisterPageState(); +} + +class _RegisterPageState extends State { + final _emailCtrl = TextEditingController(text: '1981462002@qq.com'); + final _codeCtrl = TextEditingController(text: ''); + + final ValueNotifier _enableRegister = ValueNotifier(false); + + bool get enable => _emailCtrl.text.isNotEmpty && _codeCtrl.text.isNotEmpty; + + @override + void initState() { + super.initState(); + _emailCtrl.addListener(() { + _enableRegister.value = enable; + }); + + _codeCtrl.addListener(() { + _enableRegister.value = enable; + }); + } + + @override + Widget build(BuildContext context) { + Size winSize = MediaQuery.of(context).size; + + return Scaffold( + body: SingleChildScrollView( + child: Wrap(children: [ + Stack(children: [ + UnitArcBackground(height: winSize.height * 0.30), + const Positioned(top: 20, child: BackButton(color: Colors.white)), + ]), + Container( + width: winSize.width, + height: winSize.height * 0.68, + padding: const EdgeInsets.only(left: 20.0, right: 20, top: 10), + child: Column( + children: [ + const Text( + "Flutter Unit 注册", + style: TextStyle(fontSize: 25), + ), + const SizedBox( + height: 5, + ), + const Text( + "登录账号,更多精彩,更多体验 ~", + style: TextStyle(color: Colors.grey), + ), + const Spacer( + flex: 1, + ), + IconInput( + icon: Icons.person_outline, + textFiled: TextField( + controller: _emailCtrl, + decoration: const InputDecoration( + border: InputBorder.none, + hintText: '请输入邮箱', + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ), + const SizedBox(height: 10), + buildInputWithSend(), + const Spacer(flex: 1), + _buildBtn(), + const Spacer(flex: 6), + ], + )) + ]), + )); + } + + Stack buildInputWithSend() { + return Stack( + alignment: const Alignment(.8, 0), + children: [ + IconInput( + icon: Icons.code_outlined, + textFiled: TextField( + controller: _codeCtrl, + decoration: const InputDecoration( + border: InputBorder.none, + hintText: '请输入验证码', + hintStyle: TextStyle(color: Colors.grey), + ), + ), + ), + CountDownWidget( + onPress: _sendEmail, + ) + ], + ); + } + + _sendEmail(BuildContext context) async { + if (!_checkEmail(_emailCtrl.text)) { + Toast.toast(context, '邮箱格式校验错误,请重试!', + color: Colors.orange, duration: const Duration(seconds: 2)); + return; + } + AuthRepository repository = context.read().repository; + TaskResult result = await repository.sendEmail(email: _emailCtrl.text); + print(result); + if (result.success) { + Toast.toast(context, '验证码发送成功,请注意邮箱查收!', + duration: const Duration(seconds: 2)); + } else { + Toast.toast(context, '验证码发送失败: ${result.msg}!', + color: Colors.red, duration: const Duration(seconds: 2)); + } + } + + bool _checkEmail(String email) { + RegExp exp = + RegExp(r'^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$'); + return exp.hasMatch(email); + } + + Widget _buildBtn() => Container( + margin: const EdgeInsets.only(top: 10, left: 10, right: 10, bottom: 0), + height: 40, + width: MediaQuery.of(context).size.width, + child: BlocConsumer( + builder: _build, + listener: _listener, + ), + ); + + void _doRegister() { + BlocProvider.of(context) + .add(AuthByRegister(_emailCtrl.text, _codeCtrl.text)); + } + + Widget _build(BuildContext context, state) { + bool enable = state is AuthLoading || state is AuthSuccess; + String info = enable ? '注册中...' : '开启 Unit 新世界'; + return BlocListener( + listener: _listenerLogin, + child: ValueListenableBuilder( + valueListenable: _enableRegister, + builder: (ctx, bool value, child) { + return ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20))), + backgroundColor: Colors.blue, + disabledBackgroundColor: Colors.blue.withOpacity(0.6), + ), + onPressed: (enable || !value) ? null : _doRegister, + child: Text(info, + style: TextStyle(color: Colors.white, fontSize: 14,fontWeight: FontWeight.bold)), + + ); + }, + )); + } + + void _listener(BuildContext context, AuthState state) { + if (state is AuthFailure) { + Toast.toast(context, '注册失败 : ${state.error}!', + color: Colors.red, duration: const Duration(seconds: 2)); + } + + if (state is AuthSuccess) { + // BlocProvider.of(context).add(AuthByPassword( + // username: _emailCtrl.text, + // password: _codeCtrl.text, + // )); + } + } + + void _listenerLogin(BuildContext context, AuthState state) { + if (state is AuthSuccess) { + Navigator.pop(context); + } + } +} diff --git a/lib/user_system/pages/register/send_code.dart b/modules/basic_system/authentication/lib/views/mobile/register/send_code.dart similarity index 79% rename from lib/user_system/pages/register/send_code.dart rename to modules/basic_system/authentication/lib/views/mobile/register/send_code.dart index 76b60e956..59b656677 100644 --- a/lib/user_system/pages/register/send_code.dart +++ b/modules/basic_system/authentication/lib/views/mobile/register/send_code.dart @@ -7,16 +7,16 @@ import 'package:flutter/material.dart'; /// 说明: class CountDownWidget extends StatefulWidget { - final Function(BuildContext context) onPress; + final Function(BuildContext context)? onPress; - CountDownWidget({Key key, this.onPress}) : super(key: key); + const CountDownWidget({Key? key, this.onPress}) : super(key: key); @override _CountDownWidgetState createState() => _CountDownWidgetState(); } class _CountDownWidgetState extends State { - Timer timer; + Timer? timer; int count = 60; bool startTimer = false; @@ -37,11 +37,11 @@ class _CountDownWidgetState extends State { onPressed: startTimer ? null : () { - timer = Timer.periodic(Duration(seconds: 1), _update); + timer = Timer.periodic(const Duration(seconds: 1), _update); setState(() { startTimer = true; }); - widget.onPress(context); + widget.onPress?.call(context); }, child: Text(startTimer ? '$count 秒后重试' : '获取验证码')); } 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/modules/basic_system/authentication/lib/views/mobile/user/unit_drawer_header.dart b/modules/basic_system/authentication/lib/views/mobile/user/unit_drawer_header.dart new file mode 100644 index 000000000..72d617747 --- /dev/null +++ b/modules/basic_system/authentication/lib/views/mobile/user/unit_drawer_header.dart @@ -0,0 +1,88 @@ +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/login_bg.png'), + fit: BoxFit.cover), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Wrap( + spacing: 10, + crossAxisAlignment: WrapCrossAlignment.center, + children: const [ + FlutterLogo( + // colors: Colors.orange, + 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/modules/basic_system/authentication/lib/views/mobile/user/user_account.dart b/modules/basic_system/authentication/lib/views/mobile/user/user_account.dart new file mode 100644 index 000000000..c60c09ddd --- /dev/null +++ b/modules/basic_system/authentication/lib/views/mobile/user/user_account.dart @@ -0,0 +1,415 @@ +import 'dart:io'; +import 'dart:ui'; + +import 'package:authentication/blocs/authentic/bloc.dart'; +import 'package:authentication/blocs/user/bloc.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 '../../../blocs/authentic/event.dart'; +import '../../../blocs/user/state.dart'; + +class UserAccountPage extends StatelessWidget { + const UserAccountPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + Icon trailing = const Icon( + Icons.navigate_next, + color: Color(0xffD9D9D9), + ); + Color? color = Theme.of(context).listTileTheme.tileColor; + Color? sbgColor = Theme.of(context).appBarTheme.backgroundColor; + Color? bgColor = Theme.of(context).scaffoldBackgroundColor; + bool isDark = Theme.of(context).brightness == Brightness.dark; + + UserPerformance performance = context.select( + (bloc) => bloc.state, + ); + return Scaffold( + backgroundColor: isDark ? null : bgColor, + appBar: AppBar( + backgroundColor: isDark ? null : sbgColor, + title: const Text( + '账号资料', + ), + ), + body: Column( + children: [ + const SizedBox( + height: 10, + ), + GestureDetector( + onTap: () => _showPicker(context), + child: Container( + color: color, + height: 64, + child: Row( + children: [ + Container( + padding: const EdgeInsets.only(left: 15), + width: 120, + child: const Text('头像')), + const Spacer(), + // AuthUserAvatar( + // size: 50, + // borderSize: 2, + // ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: trailing, + ) + ], + ), + ), + ), + Divider( + height: 1 / window.devicePixelRatio, + thickness: 1 / window.devicePixelRatio, + ), + GestureDetector( + onTap: () { + // LoggingUploader.onEvent(kSetNameAction, kMyInfoPageName); + // Navigator.push( + // context, + // Right2LeftRouter( + // duration: Duration(milliseconds: 200), + // child: UserChangeNamePage( + // name: performance.username, + // ))); + }, + child: Container( + color: color, + height: 54, + child: Row( + children: [ + Container( + padding: const EdgeInsets.only(left: 15), + width: 120, + child: const Text('昵称')), + const Spacer(), + Text( + performance.username ?? '', + style: const TextStyle(color: Colors.grey), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: trailing, + ) + ], + ), + ), + ), + Divider( + height: 1 / window.devicePixelRatio, + thickness: 1 / window.devicePixelRatio, + ), + Container( + color: color, + height: 54, + child: Row( + children: [ + Container( + padding: const EdgeInsets.only(left: 15), + width: 120, + child: const Text('箴言')), + const Spacer(), + const Text( + // '${performance.userId}', + '海的彼岸,有我未曾见证的风采。', style: TextStyle(color: Colors.grey,fontSize: 12), + ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 12), + child: Icon( + Icons.arrow_forward_ios_sharp, + size: 20, + color: Colors.transparent, + ), + ) + ], + ), + ), + + // const SizedBox( + // height: 10, + // ), + + // Container( + // color: color, + // height: 54, + // child: Row( + // children: [ + // Container( + // padding: EdgeInsets.only(left: 15), + // width: 120, + // child: Text('免费蜂蜜/日')), + // Spacer(), + // Text( + // '${ 0}', + // style: TextStyle(color: Colors.grey), + // ), + // Padding( + // padding: const EdgeInsets.symmetric(horizontal: 12), + // child: Icon( + // Icons.arrow_forward_ios_sharp, + // size: 20, + // color: Colors.transparent, + // ), + // ) + // ], + // ), + // ), + // Divider( + // height: 1 / window.devicePixelRatio, + // thickness: 1 / window.devicePixelRatio, + // ), + // Container( + // color: color, + // height: 54, + // child: Row( + // children: [ + // Container( + // padding: EdgeInsets.only(left: 15), + // width: 120, + // child: Text('蜂蜜')), + // Spacer(), + // Text( + // '${0}', + // style: TextStyle(color: Colors.grey), + // ), + // Padding( + // padding: const EdgeInsets.symmetric(horizontal: 12), + // child: Icon( + // Icons.arrow_forward_ios_sharp, + // size: 20, + // color: Colors.transparent, + // ), + // ) + // ], + // ), + // ), + + const SizedBox( + height: 10, + ), + Container( + color: color, + height: 54, + child: Row( + children: [ + Container( + padding: const EdgeInsets.only(left: 15), + width: 120, + child: const Text('账号')), + const Spacer(), + const Text( + // '${performance.userId}', + '******', style: TextStyle(color: Colors.grey,fontSize: 12), + ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 12), + child: Icon( + Icons.arrow_forward_ios_sharp, + size: 20, + color: Colors.transparent, + ), + ) + ], + ), + ), + const SizedBox( + height: 10, + ), + ListTile( + title: const Center( + child: Text( + '退出登录', + style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold), + )), + + // trailing: _nextIcon(context), + onTap: () { + showDialog( + context: context, + builder: (ctx) => AlertConformDialog( + title: "登出提示", + content: "退出后将无法使用用户相关的功能,确定退出登录吗?", + conformText: '确定', + onConform: () async { + context.read().add(const Logout()); + // Navigator.of(context).pushAndRemoveUntil( + // NoAnimRouter(AuthRelation( + // pushLogin: false, + // child: MoAILoginPage( + // model: LoginModel( + // appName: "蜜蜂AI智能助手", + // appNameEn: + // "Bee Chat AI Intelligence Assistant", + // appIcon: Icons.widgets_outlined, + // loginBgAssets: "assets/images/login_bg.png"), + // ), + // )), + // ModalRoute.withName('/'), + // ); + return true; + })); + }, + ), + Divider( + height: 1 / window.devicePixelRatio, + thickness: 1 / window.devicePixelRatio, + ), + // const SizedBox( + // height: 10, + // ), + // if(false) + ListTile( + title: const Center( + child: Text( + '删除账号', + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + color: Colors.redAccent), + )), + + // trailing: _nextIcon(context), + onTap: () { + String msg = + "确定删除账号吗?删除后你将无法再访问蜜蜂 ai 提供的智能服务,并清空你的所有账号资料,点击确定删除。"; + showDialog( + context: context, + builder: (ctx) => Dialog( + child: MobileMessagePanel( + title: '清空提示', + conformText: '确定', + msg: msg, + task: (_) async { + // await Future.delayed(Duration(seconds: 3)); + // await context.read().repo.unregister(); + // + context.read().add(const Logout()); + + // Navigator.of(context).pushAndRemoveUntil( + // NoAnimRouter(AuthRelation( + // pushLogin: false, + // child: MoAILoginPage( + // model: LoginModel( + // appName: "蜜蜂AI智能助手", + // appNameEn: + // "Bee Chat AI Intelligence Assistant", + // appIcon: Icons.widgets_outlined, + // loginBgAssets: + // "assets/images/login_bg.png"), + // ), + // )), + // ModalRoute.withName('/'), + // ); + }, + ), + )); + }, + ), + ], + ), + ); + } + + void _showPicker(BuildContext context) async { + // FilePickerResult? result = + // await FilePicker.platform.pickFiles(type: FileType.image); + // if (result != null) { + // String? p = result.files.single.path; + // if (p != null) { + // // File file = File(p); + // // Share.shareXFiles([XFile(file.path)], text: 'Great picture'); + // Navigator.push( + // context, + // NoAnimRouter(ClipImagePage( + // image: FileImage(File(p)), + // ))); + // } + // } + // // showCupertinoModalPopup( + // // context: context, + // // builder: (ctx) => ClipRRect( + // // borderRadius: BorderRadius.only( + // // topLeft: Radius.circular(10), + // // topRight: Radius.circular(10), + // // ), + // // child: SizedBox( + // // width: 500, + // // child: AsyncPopPicker( + // // title: Text( + // // '更换头像', + // // style: TextStyle(color: Colors.grey), + // // ), + // // tasks: [ + // // AsyncPopItem( + // // task: () async { + // // try { + // // FilePickerResult? result = await FilePicker.platform + // // .pickFiles(type: FileType.image); + // // if (result != null) { + // // String? p = result.files.single.path; + // // if (p != null) { + // // // File file = File(p); + // // // Share.shareXFiles([XFile(file.path)], text: 'Great picture'); + // // Navigator.push( + // // context, + // // NoAnimRouter(ClipImagePage( + // // image: FileImage(File(p)), + // // ))); + // // } + // // } + // // } catch (e) { + // // Toast.warning("当前应用没有文件读写权限,请先在权限管理中允许!"); + // // } + // // }, + // // info: '从相册选取', + // // ), + // // ], + // // ), + // // ), + // // )); + } +} + +class UserItemPanel extends StatelessWidget { + final String label; + final String value; + final Color? color; + const UserItemPanel( + {Key? key, required this.label, required this.value, required this.color}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + color: color, + height: 54, + child: Row( + children: [ + Expanded( + child: Container( + padding: const EdgeInsets.only(left: 15), child: Text(label)), + ), + Text( + value, + style: const TextStyle(color: Colors.grey), + ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 12), + child: Icon( + Icons.arrow_forward_ios_sharp, + size: 20, + color: Colors.transparent, + ), + ) + ], + ), + ); + } +} diff --git a/modules/basic_system/authentication/lib/views/mobile/user/user_page.dart b/modules/basic_system/authentication/lib/views/mobile/user/user_page.dart new file mode 100644 index 000000000..6cbe82fc2 --- /dev/null +++ b/modules/basic_system/authentication/lib/views/mobile/user/user_page.dart @@ -0,0 +1,121 @@ +import 'package:app/app.dart'; +import 'package:authentication/views/mobile/user/user_account.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:toly_ui/toly_ui.dart'; + + +import '../../../authentication.dart'; +import 'page_item.dart'; + +/// create by 张风捷特烈 on 2020/4/26 +/// contact me by email 1981462002@qq.com +/// 说明: + +class UserPage extends StatelessWidget { + const UserPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + String image = isDark?'anim_draw.webp':'base_draw.webp'; + return Scaffold( + body: AnnotatedRegion( + value:Theme.of(context).appBarTheme.systemOverlayStyle!, + child: Column( + children: [ + Stack( + children: [ + Container( + height: 180, + width: MediaQuery.of(context).size.width, + margin: const EdgeInsets.only(bottom: 40), + child: Image.asset( + 'assets/images/$image', + fit: BoxFit.cover, + ), + ), + Positioned( + top: 50, + right: 20, + child: Container( + padding: const EdgeInsets.all(6), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.5), + borderRadius: BorderRadius.circular(4)), + child: const Icon( + Icons.color_lens_outlined, + color: Colors.white, + size: 20, + )), + ), + Positioned( + bottom: 0, + left: 40, + child: BlocBuilder( + builder: _buildAvatarByState, + ), + ), + Positioned( + bottom: 5, + right: 30, + child: BlocBuilder( + builder: _buildByState, + )) + ], + ), + const Expanded(child: MePageItem()) + ], + ), + )); + } + + Widget _buildByState(BuildContext context, AuthState state) { + if (state is AuthSuccess) { + if (state.user.isHonour) { + return HonourWrapper(username: state.user.username); + } + + return Text( + state.user.username, + style: TextStyle(fontSize: 18, color: Theme.of(context).primaryColor), + ); + } + return Text( + '张风捷特烈', + style: TextStyle( + fontSize: 18, + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold), + ); + } + + Widget _buildAvatarByState(BuildContext context, AuthState state) { + if (state is AuthSuccess) { + return FeedbackWidget( + onEnd: () { + Navigator.of(context).push(SlidePageRoute(child: UserAccountPage())); + }, + child: CircleImage( + size: 80, + shadowColor: Theme.of(context).primaryColor.withAlpha(33), + // image: NetworkImage(state.user.userAvatar), + image: const AssetImage("assets/images/icon_head.webp"), + ), + ); + } + return FeedbackWidget( + onEnd: () { + // Navigator.of(context).pushNamed(UnitRouter.login); + }, + child: CircleImage( + size: 80, + shadowColor: Theme.of(context).primaryColor.withAlpha(33), + image: const AssetImage("assets/images/icon_head.webp"), + ), + ); + } +} 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/modules/basic_system/authentication/test/authentication_test.dart b/modules/basic_system/authentication/test/authentication_test.dart new file mode 100644 index 000000000..bd5733747 --- /dev/null +++ b/modules/basic_system/authentication/test/authentication_test.dart @@ -0,0 +1,12 @@ +// import 'package:flutter_test/flutter_test.dart'; +// +// import 'package:authentication/authentication.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/basic_system/components/.gitignore b/modules/basic_system/components/.gitignore new file mode 100644 index 000000000..96486fd93 --- /dev/null +++ b/modules/basic_system/components/.gitignore @@ -0,0 +1,30 @@ +# 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/modules/basic_system/components/.metadata b/modules/basic_system/components/.metadata new file mode 100644 index 000000000..fbfa6dfba --- /dev/null +++ b/modules/basic_system/components/.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: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + channel: stable + +project_type: package diff --git a/modules/basic_system/components/CHANGELOG.md b/modules/basic_system/components/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/basic_system/components/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/basic_system/components/LICENSE b/modules/basic_system/components/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/basic_system/components/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/basic_system/components/README.md b/modules/basic_system/components/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/basic_system/components/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/basic_system/components/analysis_options.yaml b/modules/basic_system/components/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/basic_system/components/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/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/modules/basic_system/components/lib/flutter_ui/diy_flexible_space_bar.dart b/modules/basic_system/components/lib/flutter_ui/diy_flexible_space_bar.dart new file mode 100644 index 000000000..34075ef01 --- /dev/null +++ b/modules/basic_system/components/lib/flutter_ui/diy_flexible_space_bar.dart @@ -0,0 +1,338 @@ +// 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 'dart:math'; +import 'dart:ui' as ui; + + +import 'package:flutter/foundation.dart' show clampDouble; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + + +/// The part of a Material Design [AppBar] that expands, collapses, and +/// stretches. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=mSc7qFzxHDw} +/// +/// Most commonly used in the [SliverAppBar.flexibleSpace] field, a flexible +/// space bar expands and contracts as the app scrolls so that the [AppBar] +/// reaches from the top of the app to the top of the scrolling contents of the +/// app. When using [SliverAppBar.flexibleSpace], the [SliverAppBar.expandedHeight] +/// must be large enough to accommodate the [SliverAppBar.flexibleSpace] widget. +/// +/// Furthermore is included functionality for stretch behavior. When +/// [SliverAppBar.stretch] is true, and your [ScrollPhysics] allow for +/// overscroll, this space will stretch with the overscroll. +/// +/// The widget that sizes the [AppBar] must wrap it in the widget returned by +/// [DiyFlexibleSpaceBar.createSettings], to convey sizing information down to the +/// [DiyFlexibleSpaceBar]. +/// +/// {@tool dartpad} +/// This sample application demonstrates the different features of the +/// [DiyFlexibleSpaceBar] when used in a [SliverAppBar]. This app bar is configured +/// to stretch into the overscroll space, and uses the +/// [DiyFlexibleSpaceBar.stretchModes] to apply `fadeTitle`, `blurBackground` and +/// `zoomBackground`. The app bar also makes use of [CollapseMode.parallax] by +/// default. +/// +/// ** See code in examples/api/lib/material/flexible_space_bar/flexible_space_bar.0.dart ** +/// {@end-tool} +/// +/// See also: +/// +/// * [SliverAppBar], which implements the expanding and contracting. +/// * [AppBar], which is used by [SliverAppBar]. +/// * + +typedef FractionalBuilder = Widget Function(double t); + +class DiyFlexibleSpaceBar extends StatefulWidget { + /// Creates a flexible space bar. + /// + /// Most commonly used in the [AppBar.flexibleSpace] field. + const DiyFlexibleSpaceBar({ + super.key, + this.title, + this.fixedSubtitle, + this.titleIconBuilder, + this.background, + this.centerTitle, + this.titlePadding, + this.collapseMode = CollapseMode.parallax, + this.stretchModes = const [StretchMode.zoomBackground], + this.expandedTitleScale = 1.5, + }) : assert(collapseMode != null), + assert(expandedTitleScale >= 1); + + /// The primary contents of the flexible space bar when expanded. + /// + /// Typically a [Text] widget. + final Widget? title; + final Widget? fixedSubtitle; + final FractionalBuilder? titleIconBuilder; + + /// Shown behind the [title] when expanded. + /// + /// Typically an [Image] widget with [Image.fit] set to [BoxFit.cover]. + final Widget? background; + + /// Whether the title should be centered. + /// + /// By default this property is true if the current target platform + /// is [TargetPlatform.iOS] or [TargetPlatform.macOS], false otherwise. + final bool? centerTitle; + + /// Collapse effect while scrolling. + /// + /// Defaults to [CollapseMode.parallax]. + final CollapseMode collapseMode; + + /// Stretch effect while over-scrolling. + /// + /// Defaults to include [StretchMode.zoomBackground]. + final List stretchModes; + + /// Defines how far the [title] is inset from either the widget's + /// bottom-left or its center. + /// + /// Typically this property is used to adjust how far the title is + /// is inset from the bottom-left and it is specified along with + /// [centerTitle] false. + /// + /// By default the value of this property is + /// `EdgeInsetsDirectional.only(start: 72, bottom: 16)` if the title is + /// not centered, `EdgeInsetsDirectional.only(start: 0, bottom: 16)` otherwise. + final EdgeInsetsGeometry? titlePadding; + + /// Defines how much the title is scaled when the FlexibleSpaceBar is expanded + /// due to the user scrolling downwards. The title is scaled uniformly on the + /// x and y axes while maintaining its bottom-left position (bottom-center if + /// [centerTitle] is true). + /// + /// Defaults to 1.5 and must be greater than 1. + final double expandedTitleScale; + + + @override + State createState() => _DiyFlexibleSpaceBarState(); +} + +class _DiyFlexibleSpaceBarState extends State { + bool _getEffectiveCenterTitle(ThemeData theme) { + if (widget.centerTitle != null) { + return widget.centerTitle!; + } + assert(theme.platform != null); + switch (theme.platform) { + case TargetPlatform.android: + case TargetPlatform.fuchsia: + case TargetPlatform.linux: + case TargetPlatform.windows: + return false; + case TargetPlatform.iOS: + case TargetPlatform.macOS: + return true; + } + } + + Alignment _getTitleAlignment(bool effectiveCenterTitle) { + if (effectiveCenterTitle) { + return Alignment.bottomCenter; + } + final TextDirection textDirection = Directionality.of(context); + assert(textDirection != null); + switch (textDirection) { + case TextDirection.rtl: + return Alignment.bottomRight; + case TextDirection.ltr: + return Alignment.bottomLeft; + } + } + + double _getCollapsePadding(double t, FlexibleSpaceBarSettings settings) { + switch (widget.collapseMode) { + case CollapseMode.pin: + return -(settings.maxExtent - settings.currentExtent); + case CollapseMode.none: + return 0.0; + case CollapseMode.parallax: + final double deltaExtent = settings.maxExtent - settings.minExtent; + return -Tween(begin: 0.0, end: deltaExtent / 4.0).transform(t); + } + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + final FlexibleSpaceBarSettings settings = context.dependOnInheritedWidgetOfExactType()!; + assert( + settings != null, + 'A FlexibleSpaceBar must be wrapped in the widget returned by FlexibleSpaceBar.createSettings().', + ); + + final List children = []; + + final double deltaExtent = settings.maxExtent - settings.minExtent; + + // 0.0 -> Expanded + // 1.0 -> Collapsed to toolbar + final double t = clampDouble(1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent, 0.0, 1.0); + // print("=======build=======$t========"); + + // background + if (widget.background != null) { + final double fadeStart = math.max(0.0, 1.0 - kToolbarHeight / deltaExtent); + const double fadeEnd = 1.0; + assert(fadeStart <= fadeEnd); + // If the min and max extent are the same, the app bar cannot collapse + // and the content should be visible, so opacity = 1. + final double opacity = settings.maxExtent == settings.minExtent + ? 1.0 + : 1.0 - Interval(fadeStart, fadeEnd).transform(t); + double height = settings.maxExtent; + + // StretchMode.zoomBackground + if (widget.stretchModes.contains(StretchMode.zoomBackground) && + constraints.maxHeight > height) { + height = constraints.maxHeight; + } + children.add(Positioned( + top: _getCollapsePadding(t, settings), + left: 0.0, + right: 0.0, + height: height, + child: Opacity( + // IOS is relying on this semantics node to correctly traverse + // through the app bar when it is collapsed. + alwaysIncludeSemantics: true, + opacity: opacity, + child: widget.background, + ), + )); + + // StretchMode.blurBackground + if (widget.stretchModes.contains(StretchMode.blurBackground) && + constraints.maxHeight > settings.maxExtent) { + final double blurAmount = (constraints.maxHeight - settings.maxExtent) / 10; + children.add(Positioned.fill( + child: BackdropFilter( + filter: ui.ImageFilter.blur( + sigmaX: blurAmount, + sigmaY: blurAmount, + ), + child: Container( + color: Colors.transparent, + ), + ), + )); + } + } + + // title + if (widget.title != null) { + final ThemeData theme = Theme.of(context); + + Widget? title; + switch (theme.platform) { + case TargetPlatform.iOS: + case TargetPlatform.macOS: + title = widget.title; + break; + case TargetPlatform.android: + case TargetPlatform.fuchsia: + case TargetPlatform.linux: + case TargetPlatform.windows: + title = Semantics( + namesRoute: true, + child: widget.title, + ); + break; + } + + // StretchMode.fadeTitle + if (widget.stretchModes.contains(StretchMode.fadeTitle) && + constraints.maxHeight > settings.maxExtent) { + final double stretchOpacity = 1 - + clampDouble( + (constraints.maxHeight - settings.maxExtent) / 100, + 0.0, + 1.0); + title = Opacity( + opacity: stretchOpacity, + child: title, + ); + } + + final double opacity = settings.toolbarOpacity; + if (opacity > 0.0) { + TextStyle titleStyle = theme.primaryTextTheme.headlineSmall!; + titleStyle = titleStyle.copyWith( + color: titleStyle.color!.withOpacity(opacity), + ); + final bool effectiveCenterTitle = _getEffectiveCenterTitle(theme); + final EdgeInsetsGeometry padding = widget.titlePadding ?? + EdgeInsetsDirectional.only( + start: effectiveCenterTitle ? 0.0 : 72.0, + bottom: 16.0, + ); + final double scaleValue = Tween(begin: widget.expandedTitleScale, end: 1.0).transform(t); + final Matrix4 scaleTransform = Matrix4.identity() + ..scale(scaleValue, scaleValue, 1.0)..translate(t*30); + final Matrix4 translateTransform = Matrix4.identity() + ..translate(t*30); + final Alignment titleAlignment = _getTitleAlignment(effectiveCenterTitle); + children.add(Container( + padding: padding, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Transform( + alignment: titleAlignment, + transform: scaleTransform, + child: Align( + alignment: titleAlignment, + child: DefaultTextStyle( + style: titleStyle, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Container( + width: constraints.maxWidth / scaleValue, + alignment: titleAlignment, + child: Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 5, + children: [ + title!, + if(widget.titleIconBuilder!=null) + widget.titleIconBuilder!(t) + ], + ), + ); + }, + ), + ), + ), + ), + if(widget.fixedSubtitle!=null) + Transform( + alignment: titleAlignment, + transform: translateTransform, + child: widget.fixedSubtitle!) + ], + ), + ),); + } + } + + return ClipRect(child: Stack(children: children)); + }, + ); + } +} + diff --git a/modules/basic_system/components/lib/flutter_ui/flutter_ui.dart b/modules/basic_system/components/lib/flutter_ui/flutter_ui.dart new file mode 100644 index 000000000..a2875c353 --- /dev/null +++ b/modules/basic_system/components/lib/flutter_ui/flutter_ui.dart @@ -0,0 +1,3 @@ +export 'diy_flexible_space_bar.dart'; +export 'no_div_expansion_tile.dart'; +export 'toly_date_picker.dart'; diff --git a/lib/views/components/flutter/no_div_expansion_tile.dart b/modules/basic_system/components/lib/flutter_ui/no_div_expansion_tile.dart similarity index 83% rename from lib/views/components/flutter/no_div_expansion_tile.dart rename to modules/basic_system/components/lib/flutter_ui/no_div_expansion_tile.dart index 6dcbbe53d..a43a0a8f4 100644 --- a/lib/views/components/flutter/no_div_expansion_tile.dart +++ b/modules/basic_system/components/lib/flutter_ui/no_div_expansion_tile.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; const Duration _kExpand = Duration(milliseconds: 200); @@ -12,10 +11,10 @@ const Duration _kExpand = Duration(milliseconds: 200); /// the tile to reveal or hide the [children]. /// /// This widget is typically used with [ListView] to create an -/// "expand / collapse" list entry. When used with scrolling widgets like +/// "expand / collapse" list entry. When used with scrolling widgets liked_widget_bloc /// [ListView], a unique [PageStorageKey] must be specified to enable the /// [NoBorderExpansionTile] to save and restore its expanded state when it is scrolled -/// in and out of view. +/// in and out of category_view. /// /// See also: /// @@ -28,22 +27,21 @@ class NoBorderExpansionTile extends StatefulWidget { /// the tile to reveal or hide the [children]. The [initiallyExpanded] property must /// be non-null. const NoBorderExpansionTile({ - Key key, + Key? key, this.leading, - @required this.title, + required this.title, this.subtitle, this.backgroundColor, this.onExpansionChanged, this.children = const [], this.trailing, this.initiallyExpanded = false, - }) : assert(initiallyExpanded != null), - super(key: key); + }) : super(key: key); /// A widget to display before the title. /// /// Typically a [CircleAvatar] widget. - final Widget leading; + final Widget? leading; /// The primary content of the list item. /// @@ -53,14 +51,14 @@ class NoBorderExpansionTile extends StatefulWidget { /// Additional content displayed below the title. /// /// Typically a [Text] widget. - final Widget subtitle; + final Widget? subtitle; /// Called when the tile expands or collapses. /// /// When the tile starts expanding, this function is called with the value /// true. When the tile starts collapsing, this function is called with /// the value false. - final ValueChanged onExpansionChanged; + final ValueChanged? onExpansionChanged; /// The widgets that are displayed when the tile expands. /// @@ -68,10 +66,10 @@ class NoBorderExpansionTile extends StatefulWidget { final List children; /// The color to display behind the sublist when expanded. - final Color backgroundColor; + final Color? backgroundColor; /// A widget to display instead of a rotating arrow icon. - final Widget trailing; + final Widget? trailing; /// Specifies if the list tile is initially expanded (true) or collapsed (false, the default). final bool initiallyExpanded; @@ -90,12 +88,12 @@ class _NoBorderExpansionTileState extends State with Sing final ColorTween _iconColorTween = ColorTween(); final ColorTween _backgroundColorTween = ColorTween(); - AnimationController _controller; - Animation _iconTurns; - Animation _heightFactor; - Animation _headerColor; - Animation _iconColor; - Animation _backgroundColor; + late AnimationController _controller; + late Animation _iconTurns; + late Animation _heightFactor; + late Animation _headerColor; + late Animation _iconColor; + late Animation _backgroundColor; bool _isExpanded = false; @@ -109,9 +107,10 @@ class _NoBorderExpansionTileState extends State with Sing _iconColor = _controller.drive(_iconColorTween.chain(_easeInTween)); _backgroundColor = _controller.drive(_backgroundColorTween.chain(_easeOutTween)); - _isExpanded = PageStorage.of(context)?.readState(context) ?? widget.initiallyExpanded; - if (_isExpanded) + _isExpanded = PageStorage.of(context).readState(context) ?? widget.initiallyExpanded; + if (_isExpanded) { _controller.value = 1.0; + } } @@ -128,20 +127,20 @@ class _NoBorderExpansionTileState extends State with Sing _controller.forward(); } else { _controller.reverse().then((void value) { - if (!mounted) + if (!mounted) { return; + } setState(() { // Rebuild without widget.children. }); }); } - PageStorage.of(context)?.writeState(context, _isExpanded); + PageStorage.of(context).writeState(context, _isExpanded); }); - if (widget.onExpansionChanged != null) - widget.onExpansionChanged(_isExpanded); + widget.onExpansionChanged?.call(_isExpanded); } - Widget _buildChildren(BuildContext context, Widget child) { + Widget _buildChildren(BuildContext context, Widget? child) { return Container( color: _backgroundColor.value ?? Colors.transparent, @@ -178,15 +177,15 @@ class _NoBorderExpansionTileState extends State with Sing void didChangeDependencies() { final ThemeData theme = Theme.of(context); _borderColorTween - ..end = theme.dividerColor; + .end = theme.dividerColor; _headerColorTween - ..begin = theme.textTheme.subhead.color - ..end = theme.accentColor; + ..begin = theme.textTheme.bodyMedium?.color + ..end = theme.primaryColor; _iconColorTween ..begin = theme.unselectedWidgetColor - ..end = theme.accentColor; + ..end = theme.primaryColor; _backgroundColorTween - ..end = widget.backgroundColor; + .end = widget.backgroundColor; super.didChangeDependencies(); } 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/modules/basic_system/components/lib/project_ui/default/empty_search_page.dart b/modules/basic_system/components/lib/project_ui/default/empty_search_page.dart new file mode 100644 index 000000000..de90b8d74 --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/default/empty_search_page.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; + +class NotSearchPage extends StatelessWidget { + final String tips; + const NotSearchPage({Key? key, required this.tips}):super(key: key); + + @override + Widget build(BuildContext context) { + final Color color = Theme.of(context).primaryColor; + return Container( + height: 300, + alignment: Alignment.center, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon(Icons.search, color:color, size: 120.0), + Container( + padding: const EdgeInsets.only(top: 16.0), + child: Text( + tips, + style: TextStyle( + fontSize: 20, + color: color, + ), + ), + ) + ], + ), + ); + } +} diff --git a/lib/views/components/project/default/empty_shower.dart b/modules/basic_system/components/lib/project_ui/default/empty_shower.dart similarity index 76% rename from lib/views/components/project/default/empty_shower.dart rename to modules/basic_system/components/lib/project_ui/default/empty_shower.dart index 8dcc0b071..99513800a 100644 --- a/lib/views/components/project/default/empty_shower.dart +++ b/modules/basic_system/components/lib/project_ui/default/empty_shower.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter_unit/app/res/style_unit.dart'; /// create by 张风捷特烈 on 2020/11/17 /// contact me by email 1981462002@qq.com @@ -8,7 +7,7 @@ import 'package:flutter_unit/app/res/style_unit.dart'; class EmptyShower extends StatelessWidget { final String message; - const EmptyShower({Key key, this.message = "数据为空"}) : super(key: key); + const EmptyShower({Key? key, this.message = "数据为空"}) : super(key: key); @override Widget build(BuildContext context) { @@ -23,11 +22,13 @@ class EmptyShower extends StatelessWidget { children: [ Icon(Icons.style, color: color, size: 120.0), Container( - padding: EdgeInsets.only(top: 10.0), + padding: const EdgeInsets.only(top: 10.0), child: Text( message, textAlign: TextAlign.center, - style: TextStyleUnit.headTextStyle.copyWith(color: color), + style: TextStyle( + color: color, fontSize: 18, + ), ), ) ], diff --git a/lib/views/pages/search/error_page.dart b/modules/basic_system/components/lib/project_ui/default/error_page.dart similarity index 87% rename from lib/views/pages/search/error_page.dart rename to modules/basic_system/components/lib/project_ui/default/error_page.dart index b90fd9496..ceaa62137 100644 --- a/lib/views/pages/search/error_page.dart +++ b/modules/basic_system/components/lib/project_ui/default/error_page.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; - class ErrorPage extends StatelessWidget { + const ErrorPage({Key? key}):super(key: key); @override Widget build(BuildContext context) { @@ -14,7 +14,7 @@ class ErrorPage extends StatelessWidget { children: [ Icon(Icons.error_outline, color: Colors.red[300], size: 120.0), Container( - padding: EdgeInsets.only(top: 16.0), + padding: const EdgeInsets.only(top: 16.0), child: Text( "好像有些小错误,ε=(#>д<)ノ", style: TextStyle( diff --git a/modules/basic_system/components/lib/project_ui/default/error_shower.dart b/modules/basic_system/components/lib/project_ui/default/error_shower.dart new file mode 100644 index 000000000..a13760c0e --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/default/error_shower.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; + + +/// create by 张风捷特烈 on 2020/11/17 +/// contact me by email 1981462002@qq.com +/// 说明: 默认 错误视图 + +class ErrorShower extends StatelessWidget { + final String error; + + const ErrorShower({Key? key, this.error = "出现异常"}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Center( + child: Wrap( + spacing: 10, + direction: Axis.vertical, + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const SizedBox( + width: 80, + height: 80, + child: Icon( + Icons.error, + size: 80, + color: Colors.red, + )), + Text( + error, + style:const TextStyle( + color: Colors.red, fontSize: 18, + ), + textAlign: TextAlign.center, + ) + ], + ), + ); + } +} diff --git a/lib/views/components/project/default/loading_shower.dart b/modules/basic_system/components/lib/project_ui/default/loading_shower.dart similarity index 77% rename from lib/views/components/project/default/loading_shower.dart rename to modules/basic_system/components/lib/project_ui/default/loading_shower.dart index bba59bd44..054b5e230 100644 --- a/lib/views/components/project/default/loading_shower.dart +++ b/modules/basic_system/components/lib/project_ui/default/loading_shower.dart @@ -1,14 +1,14 @@ + import 'package:flutter/material.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; -import 'package:flutter_unit/app/res/style_unit.dart'; /// create by 张风捷特烈 on 2020/10/24 /// contact me by email 1981462002@qq.com /// 说明: 默认 加载视图 class LoadingShower extends StatelessWidget { - const LoadingShower({Key key}) : super(key: key); + const LoadingShower({Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -19,11 +19,13 @@ class LoadingShower extends StatelessWidget { alignment: WrapAlignment.center, crossAxisAlignment: WrapCrossAlignment.center, children: [ - Container( + SizedBox( width: 80, height: 80, child: SpinKitFadingCube(color: Theme.of(context).primaryColor)), - Text("loading ...",style: TextStyleUnit.hintStyle,) + const Text("loading ...",style: TextStyle( + color: Color(0xff939EA7), + fontSize: 13),) ], ), ); diff --git a/modules/basic_system/components/lib/project_ui/default/no_more_widget.dart b/modules/basic_system/components/lib/project_ui/default/no_more_widget.dart new file mode 100644 index 000000000..f7a3e038a --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/default/no_more_widget.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +/// create by 张风捷特烈 on 2020/11/25 +/// contact me by email 1981462002@qq.com +/// 说明: + +class NoMoreWidget extends StatelessWidget { + const NoMoreWidget({Key? key}) : super(key: key); + + + + @override + Widget build(BuildContext context) { + double bottom = MediaQuery.of(context).padding.bottom; + + return SizedBox(height: bottom); + } + +} 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/modules/basic_system/components/lib/project_ui/refresh/toly_refresh_indicator.dart b/modules/basic_system/components/lib/project_ui/refresh/toly_refresh_indicator.dart new file mode 100644 index 000000000..cd239d439 --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/refresh/toly_refresh_indicator.dart @@ -0,0 +1,97 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart' + hide RefreshIndicator, RefreshIndicatorState; +import 'package:flutter/services.dart'; +import 'package:tolyui_refresh/tolyui_refresh.dart'; + +class TolyRefreshIndicator extends RefreshIndicator { + const TolyRefreshIndicator({super.key}); + + @override + State createState() => _TolyRefreshIndicatorState(); +} + +class _TolyRefreshIndicatorState + extends RefreshIndicatorState + with TickerProviderStateMixin { + late AnimationController _iconRotateCtrl; + late Animation rotateAnima; + + @override + void initState() { + super.initState(); + _iconRotateCtrl = AnimationController( + vsync: this, duration: const Duration(milliseconds: 250)); + rotateAnima = Tween(begin: 0.0, end: -0.5).animate(_iconRotateCtrl); + } + + @override + void onModeChange(RefreshStatus? mode) { + if (mode == RefreshStatus.canRefresh) { + HapticFeedback.lightImpact(); + _iconRotateCtrl.forward(); + } + + if (mode == RefreshStatus.idle) { + if (_iconRotateCtrl.isCompleted) { + _iconRotateCtrl.reverse(); + } + } + } + + @override + Future readyToRefresh() { + // TODO: implement readyToRefresh + // final Simulation simulation = SpringSimulation( + // SpringDescription( + // mass: 3.4, + // stiffness: 10000.5, + // damping: 6, + // ), + // _beizerBounceCtl.value, + // 0, + // 1000); + // _beizerBounceCtl.animateWith(simulation); + // if (widget.readyRefresh != null) { + // return widget.readyRefresh!(); + // } + return super.readyToRefresh(); + } + + @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.idle || RefreshStatus.canRefresh => RotationTransition( + turns: rotateAnima, + child: Icon( + CupertinoIcons.arrow_down, + color: Color(0xff666668), + ), + ), + _ => SizedBox() + }; + + return Container( + // color: Colors.blue, + child: Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: child, + )), + ); + } + + @override + void onOffsetChange(double offset) { + // print(offset); + } +} diff --git a/modules/basic_system/components/lib/project_ui/time_line/flutter_unit_time_line.dart b/modules/basic_system/components/lib/project_ui/time_line/flutter_unit_time_line.dart new file mode 100644 index 000000000..eedef00a7 --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/time_line/flutter_unit_time_line.dart @@ -0,0 +1,330 @@ +import 'package:app/app.dart'; +import 'package:dash_painter/dash_painter.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:wrapper/wrapper.dart'; + +import 'model/time_node.dart'; + +class FlutterUnitTimeLine extends StatelessWidget { + const FlutterUnitTimeLine({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.black, + appBar: AppBar( + backgroundColor: Colors.black, + title: const Text('FlutterUnit 时光轴'), + ), + body: Container( + margin: const EdgeInsets.only(top: 0), + child: ListView( + padding: const EdgeInsets.only(top: 0, bottom: 20), + children: [ + TimeLineNode( + timeNode: TimeNode( + title: 'FlutterUnit 正式开源', + year: '2020', + content: 'V1.0 版本: 核心功能是组件集录,此时收录 213 组件的基本使用方式。', + imageUrl: 'https://gitee.com/toly1994/res/raw/master/img3.webp', + time: '2020-04-15', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: '收藏夹功能完成', + content: '通过收藏夹功能,使用者可以自由创建收藏夹,对组件进行自己的分类。', + time: '2020-04-23', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: 'FlutterUnit 支持 MacOS', + content: '优化相关界面布局,在新分支中支持 MacOS 系统运行 FlutterUnit 程序。', + imageUrl: + 'https://gitee.com/toly1994/toly_blog_pic/raw/master/node4.webp', + time: '2020-05-05', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: 'FlutterUnit 支持 Windows', + content: '优化相关界面布局,在新分支中支持 Windows 系统运行 FlutterUnit 程序。', + time: '2020-07-09', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: 'FlutterUnit 支持 web', + content: '优化相关界面布局,在新分支中支持 Windows 系统运行 FlutterUnit 程序。', + time: '2020-08-12', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: ' Flutter 要点集录', + imageUrl: 'https://gitee.com/toly1994/res/raw/master/img2.webp', + content: + '要点集录会收集 FlutterUnit 中 point 标签的 issues,在应用中进行展示,从而达到对要点知识的收集。', + time: '2020-09-04', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: '组件集录收集数', + content: + '收录组件数量从最初的 213 个增加到 306 个,已经涵盖了绝大多数 Flutter 框架中的原生组件。', + time: '2020-12-22', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: '个人页面的加入', + content: '相比于侧滑菜单,通过个人页面,更方便进行一些应用的设置操作。', + time: '2020-12-22', + ), + ), + TimeLineNode( + timeNode: TimeNode( + year: '2021', + title: '绘制集录正式加入', + imageUrl: + 'https://gitee.com/toly1994/res/raw/master/%E7%BB%98%E5%88%B6%E9%9B%86%E5%BD%95%E7%9A%84%E5%89%AF%E6%9C%AC.webp', + content: + '绘制集录目的在于收录一些有意思的绘制案例,帮助对绘制感兴趣的朋友更好地学习,另一方面也可以反映出 Flutter 在绘制方面的强大能力。', + time: '2021-01-22', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: '后端服务计划启动', + content: + '基于 SpringBoot 框架实现 Flutter Unit Server,打算实现用户系统,支持组件收藏的用户化及同步操作,以及应用中组件数据的线上化。(封测中)', + time: '2021-03-28', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: '用户登录测试', + content: '后端完成基本的用户系统,基于邮箱校验,前端完成用户注册、登录等界面及交互逻辑。(封测中)', + time: '2021-05-01', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: '全面支持空安全', + imageUrl: + 'https://gitee.com/toly1994/res/raw/master/nullsafe.webp', + content: '由于 FlutterUnit 中存在大量的组件示例,支持空安全是一个非常大的挑战。', + time: '2021-08-29', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: '绘制集录持续收集', + content: + '根据绘制、手势、动画等小册中的案例,进行收录到 FlutterUnit 绘制集录中,桌面版本也同步更新中。', + time: '2020-10-01', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: '实现应用内升级', + imageUrl: 'https://gitee.com/toly1994/res/raw/master/img.webp', + content: '服务端完成应用版本检测接口,应用内升级的实现,方便大家更新版本,及时体验最新版功能。', + time: '2021-12-18', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: '项目及案例全面优化', + year: '2022', + imageUrl: 'https://gitee.com/toly1994/res/raw/master/img4.webp', + content: + '由于代码案例代码比较老旧,为了给新手朋友一个规范的示例。针对 Dart Analysis 中代码的规范性问题进行优化。由于案例数量太多,这又是一个非常巨大的挑战。', + time: '2021-03-26', + ), + ), + TimeLineNode( + timeNode: TimeNode( + title: 'FlutterUnit 2.0 发布', + content: '鉴于案例的优化完成、[绘制/组件]集录的收集情况、以及应用内更新的支持,FlutterUnit 正式进入 2.0 版本。', + imageUrl: 'https://gitee.com/toly1994/res/raw/master/img3.webp', + time: '2022-03-28', + ), + ), + ], + ), + ), + ); + } +} + +class TimeLineNode extends StatelessWidget { + final TimeNode timeNode; + + const TimeLineNode({Key? key, required this.timeNode}) : super(key: key); + + final double dashLineWith = 20; + final double marginTop = 10; + final double offset = 20 + 10; + final double lineWidth = 2; + final double circleRadius = 5; + + @override + Widget build(BuildContext context) { + Color themeColor = BlocProvider.of(context).state.themeColor.color; + + return IntrinsicHeight( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 65, + child: Padding( + padding: const EdgeInsets.only(top: 20 + 5, left: 10), + child: timeNode.year != null + ? Text( + '${timeNode.year} 年', + style: const TextStyle( + color: Colors.white, fontWeight: FontWeight.bold), + ) + : const SizedBox.shrink()), + ), + _buildDecoration(themeColor), + Expanded(flex: 8, child: _buildCenterWidget(themeColor)), + const Spacer(flex: 1) + ], + ), + ); + } + + Widget _buildDecoration(Color themeColor) => Container( + margin: const EdgeInsets.only(left: 10), + width: dashLineWith, + decoration: DashDecoration( + circleColor: themeColor, + lineColor: Colors.white, + circleRadius: circleRadius, + color: Colors.white, + circleOffset: Offset(lineWidth / 2, offset + 10 / 2)), + ); + + Widget _buildCenterWidget(Color themeColor) { + return Padding( + padding: const EdgeInsets.only(top: 10), + child: Wrapper( + color: Colors.white, + offset: 20 + 10 / 2 - 6, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + timeNode.title, + style: TextStyle(fontWeight: FontWeight.bold, color: themeColor), + ), + Container( + margin: const EdgeInsets.symmetric(vertical: 5), + child: Text( + timeNode.content, + style: const TextStyle( + color: Colors.grey, + fontSize: 12, + shadows: [ + Shadow(color: Colors.blueAccent, blurRadius: .1) + ]), + ), + ), + if (timeNode.imageUrl != null) + Container( + width: double.infinity, + margin: const EdgeInsets.symmetric(vertical: 5), + child: Image( + image: NetworkImage( + timeNode.imageUrl!, + ), + height: 100, + fit: BoxFit.cover, + ), + ), + Align( + alignment: Alignment.centerRight, + child: Text(timeNode.time, + style: TextStyle( + color: themeColor, + fontSize: 14, + fontWeight: FontWeight.bold)), + ) + ], + ), + ), + ); + } +} + +class DashDecoration extends Decoration { + final Color color; + final Color circleColor; + final Color lineColor; + final Offset circleOffset; + final double circleRadius; + + const DashDecoration( + {required this.color, + required this.circleColor, + required this.lineColor, + required this.circleOffset, + required this.circleRadius}); + + @override + BoxPainter createBoxPainter([VoidCallback? onChanged]) => + DashBoxPainter(decoration: this); +} + +class DashBoxPainter extends BoxPainter { + final DashDecoration decoration; + + const DashBoxPainter({required this.decoration}); + + @override + void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { + canvas.save(); + final Paint paint = Paint()..style = PaintingStyle.stroke; + final Path path = Path(); + canvas.translate( + offset.dx, + offset.dy, + ); + // 绘制直线 + canvas.drawLine( + Offset(-decoration.circleOffset.dx, 0), + Offset(-decoration.circleOffset.dx, configuration.size!.height), + paint + ..color = decoration.lineColor + ..strokeWidth = 2); + + // 绘制虚线 + path + ..moveTo(0, decoration.circleOffset.dy) + ..relativeLineTo(configuration.size!.width, 0); + const DashPainter(span: 2, step: 3).paint( + canvas, + path, + paint + ..color = decoration.color + ..strokeWidth = 1); + + //绘制圆点 + final Paint paint2 = Paint()..color = Colors.white; + canvas.drawCircle( + Offset(-decoration.circleOffset.dx, decoration.circleOffset.dy), + decoration.circleRadius, + paint2); + canvas.drawCircle( + Offset(-decoration.circleOffset.dx, decoration.circleOffset.dy), + decoration.circleRadius * 0.6, + paint2..color = decoration.circleColor); + canvas.restore(); + } +} diff --git a/modules/basic_system/components/lib/project_ui/time_line/model/time_node.dart b/modules/basic_system/components/lib/project_ui/time_line/model/time_node.dart new file mode 100644 index 000000000..bb8006543 --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/time_line/model/time_node.dart @@ -0,0 +1,19 @@ +class TimeNode { + final String title; + final String time; + final String? year; + final String content; + final String? imageUrl; + + TimeNode( + {required this.title, + required this.time, + this.year, + required this.content, + this.imageUrl}); + + @override + String toString() { + return 'TimeNode{title: $title, time: $time, content: $content, imageUrl: $imageUrl}'; + } +} 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/modules/basic_system/components/lib/project_ui/top_bar/desk_tab_top_bar.dart b/modules/basic_system/components/lib/project_ui/top_bar/desk_tab_top_bar.dart new file mode 100644 index 000000000..8c05408f6 --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/top_bar/desk_tab_top_bar.dart @@ -0,0 +1,79 @@ +import 'package:app/app.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; + + +class DeskTabTopBar extends StatefulWidget { + final List tabs; + final ValueChanged onTabPressed; + + + const DeskTabTopBar({Key? key,required this.onTabPressed, required this.tabs}) : super(key: key); + + @override + State createState() => _DeskTabTopBarState(); +} + +class _DeskTabTopBarState extends State with TickerProviderStateMixin { + late TabController tabController; + + @override + void initState() { + super.initState(); + tabController = TabController(length: widget.tabs.length, vsync: this); + } + + @override + void didUpdateWidget(covariant DeskTabTopBar 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: Container( + height: 64, + color: isDark?const Color(0xff2C3036):Colors.white, + child: Row( + children: [ + const SizedBox(width: 12,), + const BackButton(), + 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: widget.tabs.map((String name) => Tab(text: name)).toList(), + ), + ), + const Spacer(), + const SizedBox(width: 20,), + const WindowButtons(), + ], + ), + ), + ); + } +} diff --git a/modules/basic_system/components/lib/project_ui/unit_app_bar.dart b/modules/basic_system/components/lib/project_ui/unit_app_bar.dart new file mode 100644 index 000000000..08f4303ba --- /dev/null +++ b/modules/basic_system/components/lib/project_ui/unit_app_bar.dart @@ -0,0 +1,27 @@ + + +import 'package:flutter/material.dart'; + +class UnitAppbar extends StatelessWidget implements PreferredSizeWidget{ + final String title; + final Widget? leading; + final List? actions; + + const UnitAppbar({Key? key,required this.title, this.leading,this.actions}) : super(key: key); + + @override + Widget build(BuildContext context) { + + return AppBar( + actions: actions, + elevation: 0, + centerTitle: true, + backgroundColor: Colors.white, + leading: const BackButton(color: Colors.black,), + title: Text(title,style: const TextStyle(color: Colors.black,fontSize: 16),), + ); + } + + @override + Size get preferredSize => AppBar().preferredSize; +} diff --git a/lib/views/components/project/wrapper/honour_wrapper.dart b/modules/basic_system/components/lib/project_ui/wrapper/honour_wrapper.dart similarity index 85% rename from lib/views/components/project/wrapper/honour_wrapper.dart rename to modules/basic_system/components/lib/project_ui/wrapper/honour_wrapper.dart index 40cc07269..8bc80d435 100644 --- a/lib/views/components/project/wrapper/honour_wrapper.dart +++ b/modules/basic_system/components/lib/project_ui/wrapper/honour_wrapper.dart @@ -9,7 +9,7 @@ import 'package:flutter/material.dart'; class HonourWrapper extends StatefulWidget { final String username; - HonourWrapper({this.username = '张风捷特烈'}); + const HonourWrapper({Key? key, this.username = '张风捷特烈'}) : super(key: key); @override _HonourWrapperState createState() => _HonourWrapperState(); @@ -17,12 +17,12 @@ class HonourWrapper extends StatefulWidget { class _HonourWrapperState extends State with SingleTickerProviderStateMixin { - AnimationController _ctrl; + late AnimationController _ctrl; @override void initState() { super.initState(); - _ctrl = AnimationController(vsync: this, duration: Duration(seconds: 5)) + _ctrl = AnimationController(vsync: this, duration: const Duration(seconds: 5)) ..repeat(reverse: true); } @@ -53,14 +53,14 @@ class _HonourWrapperState extends State class HonourPainter extends CustomPainter { final Animation factor; final TextPainter _textPainter = TextPainter( - text: TextSpan( + text: const TextSpan( text: '尊享', style: TextStyle(fontSize: 10, color: Colors.white, height: 1), ), textAlign: TextAlign.center, textDirection: TextDirection.ltr); - HonourPainter({this.factor}) : super(repaint: factor); + HonourPainter({required this.factor}) : super(repaint: factor); static const List colors = [ Color(0xffFFAB40), @@ -77,7 +77,7 @@ class HonourPainter extends CustomPainter { ..color = const Color(0xaaFFAB40) ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 2) ..shader = ui.Gradient.linear( - Offset(0, 0), Offset(size.width, 0), colors, [0, 0.3, 0.6, 1.0])); + const Offset(0, 0), Offset(size.width, 0), colors, [0, 0.3, 0.6, 1.0])); canvas.drawLine( Offset.zero.translate(size.width * factor.value, 0), Offset.zero.translate(size.width * factor.value / 2, size.height), @@ -92,7 +92,7 @@ class HonourPainter extends CustomPainter { Offset.zero .translate(size.width * factor.value - 3, -15 - 3.0) .translate(-_textPainter.size.width / 2, 0) & - (_textPainter.size + Offset(6, 6)), + (_textPainter.size + const Offset(6, 6)), 5, 5), Paint()..color = Colors.green); 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/modules/basic_system/components/test/components_test.dart b/modules/basic_system/components/test/components_test.dart new file mode 100644 index 000000000..ada8e4169 --- /dev/null +++ b/modules/basic_system/components/test/components_test.dart @@ -0,0 +1,12 @@ +// import 'package:flutter_test/flutter_test.dart'; +// +// import 'package:components/components.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/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/modules/basic_system/l10n/CHANGELOG.md b/modules/basic_system/l10n/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/basic_system/l10n/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/basic_system/l10n/LICENSE b/modules/basic_system/l10n/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/basic_system/l10n/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/basic_system/l10n/README.md b/modules/basic_system/l10n/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/basic_system/l10n/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/basic_system/l10n/analysis_options.yaml b/modules/basic_system/l10n/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/basic_system/l10n/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/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/modules/basic_system/storage/.gitignore b/modules/basic_system/storage/.gitignore new file mode 100644 index 000000000..96486fd93 --- /dev/null +++ b/modules/basic_system/storage/.gitignore @@ -0,0 +1,30 @@ +# 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/modules/basic_system/storage/.metadata b/modules/basic_system/storage/.metadata new file mode 100644 index 000000000..6c08927eb --- /dev/null +++ b/modules/basic_system/storage/.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: e3c29ec00c9c825c891d75054c63fcc46454dca1 + channel: stable + +project_type: package diff --git a/modules/basic_system/storage/CHANGELOG.md b/modules/basic_system/storage/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/basic_system/storage/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/basic_system/storage/LICENSE b/modules/basic_system/storage/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/basic_system/storage/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/basic_system/storage/README.md b/modules/basic_system/storage/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/basic_system/storage/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/basic_system/storage/analysis_options.yaml b/modules/basic_system/storage/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/basic_system/storage/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/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/modules/basic_system/storage/lib/src/db_storage/flutter_unit/model/cache_po.dart b/modules/basic_system/storage/lib/src/db_storage/flutter_unit/model/cache_po.dart new file mode 100644 index 000000000..81e3b85ed --- /dev/null +++ b/modules/basic_system/storage/lib/src/db_storage/flutter_unit/model/cache_po.dart @@ -0,0 +1,40 @@ +class CachePo { + // 0 专栏数据缓存 + // 1 文章列表数据缓存 + final int id; + final String filter; + final String content; + final int type; + final int create; + final int update; + + CachePo({ + required this.id, + required this.filter, + required this.content, + required this.type, + required this.create, + required this.update, + }); + + Map toJson() => { + "id": id == -1 ? null : id, + "filter": filter, + "content": content, + "type": type, + "create": create, + "update": update, + }; + + factory CachePo.fromJson(Map map) { + return CachePo( + id: map['id'], + filter: map['filter'], + content: map["content"], + create: map["create"], + type: map["type"], + update: map["update"], + ); + } + +} 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/modules/basic_system/storage/lib/src/sp_storage/cao/app_config_cao.dart b/modules/basic_system/storage/lib/src/sp_storage/cao/app_config_cao.dart new file mode 100644 index 000000000..c0cf9c38b --- /dev/null +++ b/modules/basic_system/storage/lib/src/sp_storage/cao/app_config_cao.dart @@ -0,0 +1,20 @@ +import 'dart:convert'; + +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:storage/storage.dart'; +const String kAppSpKey = 'app-config'; + +class AppConfigCao{ + final SharedPreferences sp; + AppConfigCao(this.sp); + + Future write(AppConfigPo appConfigPo) async { + String config = json.encode(appConfigPo); + return sp.setString(kAppSpKey, config); + } + + Future read() async { + String content = sp.getString(kAppSpKey) ?? "{}"; + return AppConfigPo.fromPo(json.decode(content)); + } +} \ No newline at end of file diff --git a/modules/basic_system/storage/lib/src/sp_storage/exp.dart b/modules/basic_system/storage/lib/src/sp_storage/exp.dart new file mode 100644 index 000000000..c07a16627 --- /dev/null +++ b/modules/basic_system/storage/lib/src/sp_storage/exp.dart @@ -0,0 +1,3 @@ +export 'cao/app_config_cao.dart'; +export 'models/app_config_po.dart'; +export 'sp_storage.dart'; \ No newline at end of file diff --git a/modules/basic_system/storage/lib/src/sp_storage/models/app_config_po.dart b/modules/basic_system/storage/lib/src/sp_storage/models/app_config_po.dart new file mode 100644 index 000000000..3f5399fd4 --- /dev/null +++ b/modules/basic_system/storage/lib/src/sp_storage/models/app_config_po.dart @@ -0,0 +1,51 @@ +// 用于维护 App 配置信息的存储类 +// 配置信息将通过 sp 存储在 xml 中 +class AppConfigPo { + final bool showBackGround; + final bool showOverlayTool; + final bool showPerformanceOverlay; + final int fontFamilyIndex; + final int languageIndex; + final int codeStyleIndex; + final int themeModeIndex; + final int itemStyleIndex; + final int themeColorIndex; + + AppConfigPo({ + this.showBackGround = false, + this.showOverlayTool = false, + this.showPerformanceOverlay = false, + this.fontFamilyIndex = 1, + this.languageIndex = 0, + this.themeColorIndex = 4, + this.codeStyleIndex = 0, + this.themeModeIndex = 0, + this.itemStyleIndex = 0, + }); + + factory AppConfigPo.fromPo(dynamic map) { + return AppConfigPo( + showBackGround: map['showBackGround'] ?? false, + showOverlayTool: map['showOverlayTool'] ?? false, + showPerformanceOverlay: map['showPerformanceOverlay'] ?? false, + fontFamilyIndex: map['fontFamilyIndex'] ?? 1, + themeColorIndex: map['themeColorIndex'] ?? 5, + codeStyleIndex: map['codeStyleIndex'] ?? 0, + themeModeIndex: map['themeModeIndex'] ?? 0, + itemStyleIndex: map['itemStyleIndex'] ?? 0, + languageIndex: map['languageIndex'] ?? 0, + ); + } + + Map toJson() => { + 'showBackGround': showBackGround, + 'showOverlayTool': showOverlayTool, + 'showPerformanceOverlay': showPerformanceOverlay, + 'fontFamilyIndex': fontFamilyIndex, + 'themeColorIndex': themeColorIndex, + 'codeStyleIndex': codeStyleIndex, + 'themeModeIndex': themeModeIndex, + 'itemStyleIndex': itemStyleIndex, + 'languageIndex': languageIndex, + }; +} diff --git a/modules/basic_system/storage/lib/src/sp_storage/sp_storage.dart b/modules/basic_system/storage/lib/src/sp_storage/sp_storage.dart new file mode 100644 index 000000000..98b06559c --- /dev/null +++ b/modules/basic_system/storage/lib/src/sp_storage/sp_storage.dart @@ -0,0 +1,25 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +import 'cao/app_config_cao.dart'; + +class SpStorage { + SpStorage._(); + + static final SpStorage _instance = SpStorage._(); + + factory SpStorage() => _instance; + + SharedPreferences? _sp; + + SharedPreferences get spf => _sp!; + + late AppConfigCao _appConfig; + + AppConfigCao get appConfig => _appConfig; + + Future initSp() async { + if (_sp != null) return; + _sp = _sp ?? await SharedPreferences.getInstance(); + _appConfig = AppConfigCao(_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/modules/basic_system/storage/test/db_storage_test.dart b/modules/basic_system/storage/test/db_storage_test.dart new file mode 100644 index 000000000..47a0629bf --- /dev/null +++ b/modules/basic_system/storage/test/db_storage_test.dart @@ -0,0 +1,11 @@ +import 'package:flutter_test/flutter_test.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/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/modules/basic_system/toly_ui/CHANGELOG.md b/modules/basic_system/toly_ui/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/basic_system/toly_ui/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/basic_system/toly_ui/LICENSE b/modules/basic_system/toly_ui/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/basic_system/toly_ui/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/basic_system/toly_ui/README.md b/modules/basic_system/toly_ui/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/basic_system/toly_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/basic_system/toly_ui/analysis_options.yaml b/modules/basic_system/toly_ui/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/basic_system/toly_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/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/modules/basic_system/toly_ui/lib/button/feedback_widget.dart b/modules/basic_system/toly_ui/lib/button/feedback_widget.dart new file mode 100644 index 000000000..f90777d74 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/button/feedback_widget.dart @@ -0,0 +1,104 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +/// create by 张风捷特烈 on 2020-04-10 +/// contact me by email 1981462002@qq.com +/// 说明: + +enum FeedMode { + scale, + fade, + rotate, +} + +class FeedbackWidget extends StatefulWidget { + final Widget child; + final FeedMode mode; + final Duration duration; + final Function()? onPressed; + final Function()? onEnd; + final Function()? onLongPressed; + final double a; + + const FeedbackWidget({Key? key, + required this.child, + this.mode = FeedMode.scale, + this.a = 0.9, + this.onLongPressed, + this.duration = const Duration(milliseconds: 150), + this.onPressed, + this.onEnd, + }) : super(key: key); + + @override + _FeedBackState createState() => _FeedBackState(); +} + +class _FeedBackState extends State with SingleTickerProviderStateMixin { + late AnimationController _controller; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + vsync: this, + duration: widget.duration, + )..addStatusListener((s) { + if (s == AnimationStatus.completed) { + _controller.reverse().then((value) { + widget.onEnd?.call(); + }); + } + }); + } + + // 当父层状态执行 setState, 当前 State 不会执行 initState,而是 didUpdateWidget, + // 因此如果上层状态对某些 widget 配置进行修改,那么当前状态对象便无法知晓,比如 duration 、 + // 如果配置不同了需要在 didUpdateWidget 回调中更新 + // + @override + void didUpdateWidget(FeedbackWidget oldWidget) { + super.didUpdateWidget(oldWidget); + if (widget.duration != oldWidget.duration) { + _controller.duration = widget.duration; + } + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + 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) { + double rate = (widget.a - 1) * _controller.value + 1; + switch (mode) { + case FeedMode.scale: + return Transform.scale(scale: rate, child: widget.child); + case FeedMode.fade: + return Opacity(opacity: rate, child: widget.child); + case FeedMode.rotate: + return Transform.rotate(angle: rate * pi * 2, child: widget.child); + } + } +} diff --git a/modules/basic_system/toly_ui/lib/code/code.dart b/modules/basic_system/toly_ui/lib/code/code.dart new file mode 100644 index 000000000..93359fb63 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/code/code.dart @@ -0,0 +1,5 @@ +export 'code_widget.dart'; +export 'high_light_code.dart'; +export 'highlighter_style.dart'; +export 'language/language.dart'; +export 'language/dart_languge.dart'; \ No newline at end of file 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/modules/basic_system/toly_ui/lib/code/high_light_code.dart b/modules/basic_system/toly_ui/lib/code/high_light_code.dart new file mode 100644 index 000000000..fc15af2dc --- /dev/null +++ b/modules/basic_system/toly_ui/lib/code/high_light_code.dart @@ -0,0 +1,275 @@ +/// create by 张风捷特烈 on 2020-04-15 +/// contact me by email 1981462002@qq.com +/// 说明: + +// Copyright 2016 The Chromium 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 'package:flutter/material.dart'; + +import 'package:string_scanner/string_scanner.dart'; + +import 'highlighter_style.dart'; +import 'language/dart_languge.dart'; +import 'language/language.dart'; + +/// final SyntaxHighlighterStyle style = SyntaxHighlighterStyle.lightThemeStyle(); +/// DartSyntaxHighlighter(style).format(source) + +abstract class Highlighter { + // ignore: one_member_abstracts + ProgramLanguage language; + + + Highlighter({required this.language}); + + TextSpan format(String src); +} + +//暗黑模式下的高亮样式 +class CodeHighlighter extends Highlighter { + CodeHighlighter( + {ProgramLanguage language = const DartLanguage(), HighlighterStyle? style}):super(language: language) { + _spans = <_HighlightSpan>[]; + _style = style ?? HighlighterStyle.fromColors(HighlighterStyle.lightColor); + } + + late HighlighterStyle _style; + + String _src=''; + late StringScanner _scanner; + + List<_HighlightSpan> _spans=[]; + + @override + TextSpan format(String src) { + _src = src; + _scanner = StringScanner(_src); + + if (_generateSpans()) { + // Successfully parsed the code + final List formattedText = []; + int currentPosition = 0; + + for (_HighlightSpan span in _spans) { + if (currentPosition != span.start) { + formattedText + .add(TextSpan(text: _src.substring(currentPosition, span.start))); + } + + formattedText.add(TextSpan( + style: span.textStyle(_style), text: span.textForSpan(_src))); + + currentPosition = span.end; + } + + if (currentPosition != _src.length) { + formattedText + .add(TextSpan(text: _src.substring(currentPosition, _src.length))); + } + + return TextSpan(style: _style.baseStyle, children: formattedText); + } else { + // Parsing failed, return with only basic formatting + return TextSpan(style: _style.baseStyle, text: src); + } + } + + bool _generateSpans() { + int lastLoopPosition = _scanner.position; + + while (!_scanner.isDone) { + // Skip White space + _scanner.scan(RegExp(r'\s+')); + + // Block comments + if (_scanner.scan(RegExp(r'/\*(.|\n)*\*/'))) { + _spans.add(_HighlightSpan(_HighlightType.comment, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Line comments + if (_scanner.scan(RegExp(r'//'))) { + final int startComment = _scanner.lastMatch?.start??0; + bool eof = false; + int endComment; + if (_scanner.scan(RegExp(r'(.*\r\n)|(.*\n)'))) { + int? end = _scanner.lastMatch?.end; + endComment = end==null?0:end - 1; + } else { + eof = true; + endComment = _src.length; + } + + _spans.add(_HighlightSpan(_HighlightType.comment, startComment, endComment)); + + if (eof) break; + continue; + } + + // Raw r"String" + if (_scanner.scan(RegExp(r'r".*"'))) { + _spans.add(_HighlightSpan(_HighlightType.string, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Raw r'String' + if (_scanner.scan(RegExp(r"r'.*'"))) { + _spans.add(_HighlightSpan(_HighlightType.string, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Multiline """String""" + if (_scanner.scan(RegExp(r'"""(?:[^"\\]|\\(.|\n))*"""'))) { + _spans.add(_HighlightSpan(_HighlightType.string, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Multiline '''String''' + if (_scanner.scan(RegExp(r"'''(?:[^'\\]|\\(.|\n))*'''"))) { + _spans.add(_HighlightSpan(_HighlightType.string, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // "String" + if (_scanner.scan(RegExp(r'"(?:[^"\\]|\\.)*"'))) { + _spans.add(_HighlightSpan(_HighlightType.string, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // 'String' + if (_scanner.scan(RegExp(r"'(?:[^'\\]|\\.)*'"))) { + _spans.add(_HighlightSpan(_HighlightType.string, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Double + if (_scanner.scan(RegExp(r'\d+\.\d+'))) { + _spans.add(_HighlightSpan(_HighlightType.number, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Integer + if (_scanner.scan(RegExp(r'\d+'))) { + _spans.add(_HighlightSpan(_HighlightType.number, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Punctuation + if (_scanner.scan(RegExp(r'[\[\]{}().!=<>&\|\?\+\-\*/%\^~;:,]'))) { + _spans.add(_HighlightSpan(_HighlightType.punctuation, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Meta data + if (_scanner.scan(RegExp(r'@\w+'))) { + _spans.add(_HighlightSpan(_HighlightType.keyword, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Words + if (_scanner.scan(RegExp(r'\w+'))) { + _HighlightType? type; + String word = _scanner.lastMatch?[0]??''; + + if (word.startsWith('_')) word = word.substring(1); + + if (language.containsKeywords(word)) { + type = _HighlightType.keyword; + } else if (language.containsInTypes(word)) { + type = _HighlightType.keyword; + } else if (_firstLetterIsUpperCase(word)) { + type = _HighlightType.klass; + } else if (word.length >= 2 && + word.startsWith('k') && + _firstLetterIsUpperCase(word.substring(1))) { + type = _HighlightType.constant; + } + if (type != null) { + _spans.add(_HighlightSpan( + type, _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + } + } + // Check if this loop did anything + if (lastLoopPosition == _scanner.position) { + // Failed to parse this file, abort gracefully + return false; + } + lastLoopPosition = _scanner.position; + } + + _simplify(); + return true; + } + + void _simplify() { + for (int i = _spans.length - 2; i >= 0; i -= 1) { + if (_spans[i].type == _spans[i + 1].type && _spans[i].end == _spans[i + 1].start) { + _spans[i] = _HighlightSpan(_spans[i].type, _spans[i].start, _spans[i + 1].end); + _spans.removeAt(i + 1); + } + } + } + + bool _firstLetterIsUpperCase(String str) { + if (str.isNotEmpty) { + final String first = str.substring(0, 1); + return first == first.toUpperCase(); + } + return false; + } +} + +enum _HighlightType { + number, + comment, + keyword, + string, + punctuation, + klass, + constant +} + +class _HighlightSpan { + _HighlightSpan(this.type, this.start, this.end); + + final _HighlightType type; + final int start; + final int end; + + String textForSpan(String src) { + return src.substring(start, end); + } + + TextStyle? textStyle(HighlighterStyle? style) { + if (type == _HighlightType.number) { + return style?.numberStyle; + } else if (type == _HighlightType.comment) { + return style?.commentStyle; + } else if (type == _HighlightType.keyword) { + return style?.keywordStyle; + } else if (type == _HighlightType.string) { + return style?.stringStyle; + } else if (type == _HighlightType.punctuation) { + return style?.punctuationStyle; + } else if (type == _HighlightType.klass) { + return style?.classStyle; + } else if (type == _HighlightType.constant) { + return style?.constantStyle; + } else { + return style?.baseStyle; + } + } +} diff --git a/lib/views/components/permanent/code/highlighter_style.dart b/modules/basic_system/toly_ui/lib/code/highlighter_style.dart similarity index 91% rename from lib/views/components/permanent/code/highlighter_style.dart rename to modules/basic_system/toly_ui/lib/code/highlighter_style.dart index 3980bfc63..ef08b3d53 100644 --- a/lib/views/components/permanent/code/highlighter_style.dart +++ b/modules/basic_system/toly_ui/lib/code/highlighter_style.dart @@ -127,13 +127,13 @@ class HighlighterStyle { ), backgroundColor: Color(colors[8]), ); - final TextStyle baseStyle; - final TextStyle numberStyle; - final TextStyle commentStyle; - final TextStyle keywordStyle; - final TextStyle stringStyle; - final TextStyle punctuationStyle; - final TextStyle classStyle; - final TextStyle constantStyle; - final Color backgroundColor; + final TextStyle? baseStyle; + final TextStyle? numberStyle; + final TextStyle? commentStyle; + final TextStyle? keywordStyle; + final TextStyle? stringStyle; + final TextStyle? punctuationStyle; + final TextStyle? classStyle; + final TextStyle? constantStyle; + final Color? backgroundColor; } \ No newline at end of file diff --git a/modules/basic_system/toly_ui/lib/code/language/dart_languge.dart b/modules/basic_system/toly_ui/lib/code/language/dart_languge.dart new file mode 100644 index 000000000..8341d1473 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/code/language/dart_languge.dart @@ -0,0 +1,42 @@ + +import 'language.dart'; + +/// create by 张风捷特烈 on 2021/1/21 +/// contact me by email 1981462002@qq.com +/// 说明: + +class DartLanguage extends ProgramLanguage{ + + const DartLanguage() : super('Dart'); + + static const List _kDartKeywords = [ + 'abstract', 'as', 'assert', 'async', 'await', 'break', 'case', 'catch', + 'class', 'const', 'continue', 'default', 'deferred', 'do', 'dynamic', 'else', + 'enum', 'export', 'external', 'extends', 'factory', 'false', 'final', + 'finally', 'for', 'get', 'if', 'implements', 'import', 'in', 'is', 'library', + 'new', 'null', 'operator', 'part', 'rethrow', 'return', 'set', 'static', + 'super', 'switch', 'sync', 'this', 'throw', 'true', 'try', 'typedef', 'var', + 'void', 'while', 'with', 'yield' + ]; + + static const List _kDartInTypes = [ + 'int', 'double', 'num', 'bool' + ]; + + @override + List get keywords => _kDartKeywords; + + @override + List get inTypes => [ + 'int', 'double', 'num', 'bool' + ]; + + @override + bool containsInTypes(String word) =>_kDartKeywords.contains(word); + + @override + bool containsKeywords(String word)=>_kDartInTypes.contains(word); + + + +} \ No newline at end of file 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/modules/basic_system/toly_ui/lib/decorations/round_rect_rab_indicator.dart b/modules/basic_system/toly_ui/lib/decorations/round_rect_rab_indicator.dart new file mode 100644 index 000000000..9fb82bf01 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/decorations/round_rect_rab_indicator.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; + +class RoundRectTabIndicator extends Decoration { + /// Create an underline style selected tab indicator. + /// + /// The [borderSide] and [insets] arguments must not be null. + const RoundRectTabIndicator({ + this.borderSide = const BorderSide(width: 2.0, color: Colors.white), + this.insets = EdgeInsets.zero, + }) : assert(borderSide != null), + assert(insets != null); + + /// The color and weight of the horizontal line drawn below the selected tab. + final BorderSide borderSide; + + /// Locates the selected tab's underline relative to the tab's boundary. + /// + /// The [TabBar.indicatorSize] property can be used to define the tab + /// indicator's bounds in terms of its (centered) tab widget with + /// [TabBarIndicatorSize.label], or the entire tab with + /// [TabBarIndicatorSize.tab]. + final EdgeInsetsGeometry insets; + + @override + Decoration? lerpFrom(Decoration? a, double t) { + if (a is RoundRectTabIndicator) { + return RoundRectTabIndicator( + borderSide: BorderSide.lerp(a.borderSide, borderSide, t), + insets: EdgeInsetsGeometry.lerp(a.insets, insets, t)!, + ); + } + return super.lerpFrom(a, t); + } + + @override + Decoration? lerpTo(Decoration? b, double t) { + if (b is RoundRectTabIndicator) { + return RoundRectTabIndicator( + borderSide: BorderSide.lerp(borderSide, b.borderSide, t), + insets: EdgeInsetsGeometry.lerp(insets, b.insets, t)!, + ); + } + return super.lerpTo(b, t); + } + + @override + BoxPainter createBoxPainter([VoidCallback? onChanged]) { + return _UnderlinePainter(this, onChanged); + } + + Rect _indicatorRectFor(Rect rect, TextDirection textDirection) { + assert(rect != null); + assert(textDirection != null); + final Rect indicator = insets.resolve(textDirection).deflateRect(rect); + return Rect.fromLTWH( + indicator.left, + indicator.bottom - borderSide.width, + indicator.width, + borderSide.width, + ); + } + + @override + Path getClipPath(Rect rect, TextDirection textDirection) { + return Path()..addRect(_indicatorRectFor(rect, textDirection)); + } +} + +class _UnderlinePainter extends BoxPainter { + _UnderlinePainter(this.decoration, VoidCallback? onChanged) + : assert(decoration != null), + super(onChanged); + + final RoundRectTabIndicator decoration; + + @override + void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { + assert(configuration != null); + assert(configuration.size != null); + final Rect rect = offset & configuration.size!; + final TextDirection textDirection = configuration.textDirection!; + final Rect indicator = decoration + ._indicatorRectFor(rect, textDirection) + .deflate(decoration.borderSide.width / 2.0); + final Paint paint = decoration.borderSide.toPaint() + ..strokeCap = StrokeCap.round; + canvas.drawLine(indicator.bottomLeft, indicator.bottomRight, paint); + } +} \ No newline at end of file diff --git a/modules/basic_system/toly_ui/lib/default/loading/planet_loading.dart b/modules/basic_system/toly_ui/lib/default/loading/planet_loading.dart new file mode 100644 index 000000000..29ba49b86 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/default/loading/planet_loading.dart @@ -0,0 +1,53 @@ +import 'dart:math'; + +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 +/// 说明: + +class PlateLoading extends StatelessWidget { + const PlateLoading({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 150, + height: 150, + child: Stack(alignment: Alignment.center, children: [ + const Text('loading ...'), + MathRunner( + reverse: false, + f: (t) => 0.4*cos(t * pi), + g: (t) => 0.7 * sin(t * pi), + child: const Circle(color: Colors.blue,radius: 8,)), + + MathRunner( + reverse: false, + f: (t) => 0.7 * cos(t * pi), + g: (t) => 1 * sin(t * pi), + child: const Circle(color: Colors.yellow,radius: 8,)), + MathRunner( + reverse: false, + f: (t) => -0.8 *cos(t * pi), + g: (t) => 1 * sin(t * pi), + child: const Circle(color: Colors.red,radius: 8,)), + MathRunner( + reverse: false, + f: (t) => 1*cos(t * pi), + g: (t) => 0.7 * sin(t * pi), + child: const Circle(color: Colors.green,radius: 8,)), + + MathRunner( + reverse: false, + f: (t) => 1 * cos(t * pi), + g: (t) => -0.7 * sin(t * pi), + child: const Circle(color: Colors.purple,radius: 8,)), + ]), + ); + } +} diff --git a/modules/basic_system/toly_ui/lib/dialog/alert_conform_dialog.dart b/modules/basic_system/toly_ui/lib/dialog/alert_conform_dialog.dart new file mode 100644 index 000000000..a3346bac5 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/dialog/alert_conform_dialog.dart @@ -0,0 +1,132 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +class AlertConformDialog extends StatelessWidget { + final String title; + final String content; + final String conformText; + final String cancelText; + final bool titleDivider; + + final Function()? onConform; + final VoidCallback? onCancel; + final Color? conformTextColor; + + const AlertConformDialog( + {this.title = "title", + this.content = "content", + this.conformText = "确定", + this.cancelText = "取消", + this.onConform, + this.titleDivider = false, + this.onCancel, + this.conformTextColor, + Key? key}) + : super(key: key); + + final TextStyle noticeStyle = + const TextStyle(color: Colors.grey, fontSize: 16); + final TextStyle cancelTextStyle = const TextStyle( + color: Colors.grey, fontSize: 18, fontWeight: FontWeight.bold); + + final TextStyle subTextStyle = const TextStyle( + color: Color(0xff929292), fontSize: 16, fontWeight: FontWeight.bold); + + @override + Widget build(BuildContext context) { + return Dialog( + backgroundColor: Colors.white, + elevation: 0, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(15))), + child: SizedBox( + // height: 120, + width: 320, + // color: Colors.green, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: EdgeInsets.only(top: titleDivider?10:15,bottom: titleDivider?10:0), + child: Center( + child: Text( + title, + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + )), + ), + if(titleDivider) + const Divider(height: 1,), + Center( + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 30, horizontal: 20), + child: Text( + content, + style: noticeStyle, + ), + ), + ), + Row( + children: [ + Expanded( + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + onCancel?.call(); + Navigator.of(context).pop(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: () async { + Navigator.of(context).pop(); + onConform?.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( + conformText, + style: TextStyle( + color: conformTextColor, + fontSize: 18, + fontWeight: FontWeight.bold), + ), + ), + )), + ], + ), + ], + ), + ), + ); + } +} diff --git a/modules/basic_system/toly_ui/lib/dialog/delete_message_panel.dart b/modules/basic_system/toly_ui/lib/dialog/delete_message_panel.dart new file mode 100644 index 000000000..32a89c39c --- /dev/null +++ b/modules/basic_system/toly_ui/lib/dialog/delete_message_panel.dart @@ -0,0 +1,255 @@ + + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +typedef AsyncTask = Future Function(BuildContext context); + +/// create by 张风捷特烈 on 2020-04-23 +/// contact me by email 1981462002@qq.com +/// 说明: +class DeleteMessagePanel extends StatelessWidget { + final String title; + final String msg; + final String? conformText; + final String? cancelText; + final AsyncTask task; + final Widget? icon; + + const DeleteMessagePanel({ + Key? key, + required this.title, + required this.msg, + required this.task, + this.conformText, + this.icon, + this.cancelText, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + ButtonStyle style = ElevatedButton.styleFrom( + backgroundColor: Colors.redAccent, + elevation: 0, + padding: EdgeInsets.zero, + shape: const StadiumBorder(), + ); + Color? cancelTextColor = isDark?Colors.white:Theme.of(context).textTheme.displayMedium?.color; + return SizedBox( + width: 350, + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + icon?? const Icon( + Icons.warning_amber_rounded, + color: Colors.orange, + ), + const SizedBox( + width: 20, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + title, + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.bold), + ), + Padding( + padding: const EdgeInsets.only( + top: 15, + bottom: 15, + ), + child: Text( + msg, + style: const TextStyle(fontSize: 14), + ), + ), + Row( + children: [ + const Spacer(), + OutlinedButton( + onPressed: () { + Navigator.of(context).pop(); + }, + style: OutlinedButton.styleFrom( + // backgroundColor: Color(value), + elevation: 0, + padding: EdgeInsets.zero, + shape: const StadiumBorder(), + ), + child: Text( + cancelText?? '取消', + style: + TextStyle(fontSize: 12, color: cancelTextColor,height: 1), + )), + const SizedBox( + width: 10, + ), + AsyncButton( + conformText: conformText??'删除', + task: task, + style: style, + ), + ], + ) + ], + ), + ), + ], + ), + ), + ); + } +} + + +class MobileMessagePanel extends StatelessWidget { + final String title; + final String msg; + final String? conformText; + final String? cancelText; + final Widget? icon; + final Color? conformColor; + final AsyncTask task; + + const MobileMessagePanel({ + Key? key, + required this.title, + required this.msg, + required this.task, + this.conformText, + this.conformColor, + this.icon, + this.cancelText, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + + ButtonStyle style = ElevatedButton.styleFrom( + backgroundColor: conformColor??Colors.redAccent, + elevation: 0, + padding: EdgeInsets.zero, + minimumSize: Size(70, 35), + shape: const StadiumBorder(), + ); + Color? cancelTextColor =isDark?Colors.white: Theme.of(context).textTheme.displayMedium?.color; + return SizedBox( + width: 350, + child: Padding( + padding: const EdgeInsets.all(20.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ icon?? const Icon( + Icons.warning_amber_rounded, + color: Colors.orange, + ), + const SizedBox( + width: 6, + ), Text( + title, + style: const TextStyle( + fontSize: 16, fontWeight: FontWeight.bold), + )], + ), + Padding( + padding: const EdgeInsets.only( + top: 15, + bottom: 15, + ), + child: Text( + msg, + style: const TextStyle(fontSize: 14), + ), + ), + Row( + children: [ + const Spacer(), + OutlinedButton( + onPressed: () { + Navigator.of(context).pop(); + }, + style: OutlinedButton.styleFrom( + // backgroundColor: Color(value), + elevation: 0, + padding: EdgeInsets.zero, + minimumSize: Size(70, 35), + shape: const StadiumBorder(), + ), + child: Text( + cancelText?? '取消', + style: + TextStyle(fontSize: 12, color: cancelTextColor,height: 1), + )), + const SizedBox( + width: 10, + ), + AsyncButton( + conformText: conformText??'删除', + task: task, + style: style, + ), + ], + ) + ], + ), + ), + ); + } +} + +class AsyncButton extends StatefulWidget { + final ButtonStyle? style; + final AsyncTask task; + final String conformText; + + const AsyncButton({ + super.key, + required this.task, + this.style, + required this.conformText, + }); + + @override + State createState() => _AsyncButtonState(); +} + +class _AsyncButtonState extends State { + bool _loading = false; + + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: _loading ? null : _doTask, + style: widget.style??ElevatedButton.styleFrom( + elevation: 0, + padding: EdgeInsets.zero, + shape: const StadiumBorder()), + child: _loading + ? const CupertinoActivityIndicator(radius: 8) + :Text( + widget.conformText, + style: const TextStyle(fontSize: 12,height: 1), + )); + } + + void _doTask() async { + setState(() { + _loading = true; + }); + await widget.task(context); + setState(() { + _loading = false; + }); + } +} diff --git a/lib/views/components/permanent/edit_panel.dart b/modules/basic_system/toly_ui/lib/input/edit_panel.dart similarity index 92% rename from lib/views/components/permanent/edit_panel.dart rename to modules/basic_system/toly_ui/lib/input/edit_panel.dart index 7143bf03a..9071d3072 100644 --- a/lib/views/components/permanent/edit_panel.dart +++ b/modules/basic_system/toly_ui/lib/input/edit_panel.dart @@ -4,8 +4,8 @@ typedef ChangeCallback = void Function(String str); ///输入面板 class EditPanel extends StatefulWidget { - EditPanel( - {Key key, + const EditPanel( + {Key? key, this.backgroundColor = Colors.white, this.color = Colors.lightBlue, this.minLines = 4, @@ -24,7 +24,7 @@ class EditPanel extends StatefulWidget { final double fontSize; //字号 final String hint; //提示字 final bool submitClear; //提交是否清空文字 - final ChangeCallback onChange; //提交监听 + final ChangeCallback? onChange; //提交监听 final String defaultText; //提交监听 @override @@ -32,14 +32,14 @@ class EditPanel extends StatefulWidget { } class _EditPanelState extends State { - Radius _radius; //边角半径 + Radius _radius=Radius.zero; //边角半径 - TextEditingController _controller; + late TextEditingController _controller; @override void initState() { _radius = Radius.circular(widget.fontSize * 0.618); - _controller = TextEditingController(text: widget.defaultText??''); + _controller = TextEditingController(text: widget.defaultText); super.initState(); } @@ -92,7 +92,7 @@ class _EditPanelState extends State { ), onChanged: (str) { //文字变化监听 - if (widget.onChange != null) widget.onChange(str); + widget.onChange?.call(str); }, onSubmitted: (str) { //提交监听 diff --git a/lib/views/components/permanent/icon_input.dart b/modules/basic_system/toly_ui/lib/input/icon_input.dart similarity index 85% rename from lib/views/components/permanent/icon_input.dart rename to modules/basic_system/toly_ui/lib/input/icon_input.dart index bbd7cc9d1..d261774ea 100644 --- a/lib/views/components/permanent/icon_input.dart +++ b/modules/basic_system/toly_ui/lib/input/icon_input.dart @@ -9,7 +9,7 @@ class IconInput extends StatelessWidget { final Widget textFiled; final IconData icon; - IconInput({Key key,this.textFiled,this.icon}) : super(key: key); + const IconInput({Key? key,required this.textFiled,required this.icon}) : super(key: key); @override Widget build(BuildContext context) { @@ -25,7 +25,7 @@ class IconInput extends StatelessWidget { child: Row( children: [ Padding( - padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), + padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 15.0), child: Icon( icon, color: Colors.grey, diff --git a/modules/basic_system/toly_ui/lib/input/input_button.dart b/modules/basic_system/toly_ui/lib/input/input_button.dart new file mode 100644 index 000000000..4a90f436e --- /dev/null +++ b/modules/basic_system/toly_ui/lib/input/input_button.dart @@ -0,0 +1,140 @@ +import 'package:flutter/material.dart'; + +typedef SubmitCallback = void Function(String str); + +class InputButtonConfig { + final double height; //高度 + final IconData iconData; //图标 + final String hint; //提示文字 + final double fontSize; //文字大小 + final Widget? front; //前面图标 + final bool submitClear; //是否提交清空 + + const InputButtonConfig({ + this.height = 36, + this.iconData = Icons.add, + this.fontSize = 14, + this.submitClear = true, + this.front, + this.hint = "I want to say...", + }); +} + +class InputButton extends StatefulWidget { + final SubmitCallback? onSubmit; + final ValueChanged? onChanged; + final VoidCallback? onTap; + final InputButtonConfig config; + final String defaultText; + + const InputButton( + {Key? key, + this.onSubmit, + this.onChanged, + this.defaultText = '请输入', + this.onTap, + this.config = const InputButtonConfig()}) + : super(key: key); + + @override + _InputButtonState createState() => _InputButtonState(); +} + +class _InputButtonState extends State { + double _height = 0; + double _fontSize = 0; + Radius _radius = Radius.zero; + + late TextEditingController _controller; + + @override + void initState() { + super.initState(); + _height = widget.config.height; + _fontSize = widget.config.fontSize; + _radius = Radius.circular(6); + _controller = TextEditingController(text: widget.defaultText); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + Widget textField = TextField( + controller: _controller, + maxLines: 1, + style: TextStyle( + fontSize: _fontSize, + color: Colors.lightBlue, + backgroundColor: Colors.white), + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + hintText: widget.config.hint, + hintStyle: TextStyle(color: Colors.black26, fontSize: _fontSize), + contentPadding: EdgeInsets.only(left: 14.0, top: -_fontSize), + focusedBorder: UnderlineInputBorder( + borderSide: const BorderSide(color: Colors.white), + borderRadius: + BorderRadius.only(topLeft: _radius, bottomLeft: _radius), + ), + enabledBorder: UnderlineInputBorder( + borderSide: const BorderSide(color: Colors.white), + borderRadius: + BorderRadius.only(topLeft: _radius, bottomLeft: _radius), + ), + ), + onChanged: (str) { + widget.onChanged?.call(str); + }, + onTap: widget.onTap, + ); + Widget btn = ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0, + padding: EdgeInsets.zero, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only(topRight: Radius.circular(6),bottomRight: Radius.circular(6))), + backgroundColor: const Color(0x99E0E0E0), + ), + child: Icon(widget.config.iconData,color: Theme.of(context).primaryColor,), + onPressed: () { + FocusScope.of(context).requestFocus(FocusNode()); //收起键盘 + widget.onSubmit?.call(_controller.text); + if (widget.config.submitClear) { + setState(() { + _controller.clear(); + }); + } + }, + ); + Widget inputBtn = Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Expanded( + child: SizedBox( + child: textField, + height: _height, + ), + ), + ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.zero, + bottomLeft: Radius.zero, + topRight: _radius, + bottomRight: _radius), + child: SizedBox( + child: btn, + width: _height, + height: _height, + ), + ), + ], + ); + return inputBtn; + } +} diff --git a/modules/basic_system/toly_ui/lib/markdown/markdown_widget.dart b/modules/basic_system/toly_ui/lib/markdown/markdown_widget.dart new file mode 100644 index 000000000..df48f4c89 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/markdown/markdown_widget.dart @@ -0,0 +1,184 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; +import 'md_text_styles.dart'; +import 'syntax_high_lighter.dart'; + + +class MarkdownWidget extends StatelessWidget { + static const int kWhite = 0; + static const int kDarkLight = 1; + static const int kDarkTheme = 2; + + final String markdownData; + final int style; + + const MarkdownWidget({Key? key, this.markdownData = "", this.style = kWhite}) : super(key: key); + + MarkdownStyleSheet _getCommonSheet(BuildContext context, Color codeBackground) { + MarkdownStyleSheet markdownStyleSheet = MarkdownStyleSheet.fromTheme(Theme.of(context)); + return markdownStyleSheet.copyWith( + codeblockDecoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(4.0)), + color: codeBackground, + border: Border.all( + color: MdTextStyles.subTextColor, width: 0.3))) + .copyWith( + blockquoteDecoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(4.0)), + color: MdTextStyles.subTextColor, + border: Border.all( + color: MdTextStyles.subTextColor, width: 0.3)), + blockquote: MdTextStyles.smallTextWhite); + } + + _getStyleSheetDark(BuildContext context) { + return _getCommonSheet(context, const Color.fromRGBO(40, 44, 52, 1.00)).copyWith( + p: MdTextStyles.smallTextWhite, + h1: MdTextStyles.largeLargeTextWhite, + h2: MdTextStyles.largeTextWhiteBold, + h3: MdTextStyles.normalTextMitWhiteBold, + h4: MdTextStyles.middleTextWhite, + h5: MdTextStyles.smallTextWhite, + h6: MdTextStyles.smallTextWhite, + em: const TextStyle(fontStyle: FontStyle.italic), + strong: MdTextStyles.middleTextWhiteBold, + code: MdTextStyles.smallSubText, + ); + } + + MarkdownStyleSheet _getStyleSheetWhite(BuildContext context) { + return _getCommonSheet(context, const Color.fromRGBO(40, 44, 52, 1.00)).copyWith( + p: MdTextStyles.smallText, + h1: MdTextStyles.largeLargeText, + h2: MdTextStyles.largeTextBold, + h3: MdTextStyles.normalTextBold, + h4: MdTextStyles.middleText, + h5: MdTextStyles.smallText, + h6: MdTextStyles.smallText, + strong: MdTextStyles.middleTextBold, + code: MdTextStyles.smallSubText, + ); + } + + _getStyleSheetTheme(BuildContext context) { + return _getCommonSheet(context, const Color.fromRGBO(40, 44, 52, 1.00)).copyWith( + p: MdTextStyles.smallTextWhite, + h1: MdTextStyles.largeLargeTextWhite, + h2: MdTextStyles.largeTextWhiteBold, + h3: MdTextStyles.normalTextMitWhiteBold, + h4: MdTextStyles.middleTextWhite, + h5: MdTextStyles.smallTextWhite, + h6: MdTextStyles.smallTextWhite, + em: const TextStyle(fontStyle: FontStyle.italic), + strong: MdTextStyles.middleTextWhiteBold, + code: MdTextStyles.smallSubText, + ); + } + + _getBackgroundColor(context) { + Color background = MdTextStyles.white; + switch (style) { + case kDarkLight: + background = MdTextStyles.primaryLightValue; + break; + case kDarkTheme: + background = Theme.of(context).primaryColor; + break; + } + return background; + } + + _getStyle(BuildContext context) { + MarkdownStyleSheet styleSheet = _getStyleSheetWhite(context); + switch (style) { + case kDarkLight: + styleSheet = _getStyleSheetDark(context); + break; + case kDarkTheme: + styleSheet = _getStyleSheetTheme(context); + break; + } + return styleSheet; + } + + _getMarkDownData(String markdownData) { + ///优化图片显示 + RegExp exp = RegExp(r'!\[.*\]\((.+)\)'); + RegExp expImg = RegExp("|/>)"); + RegExp expSrc = RegExp("src=['\"]?([^'\"]*)['\"]?"); + + String mdDataCode = markdownData; + try { + Iterable tags = exp.allMatches(markdownData); + if (tags.isNotEmpty) { + for (Match m in tags) { + String imageMatch = m.group(0)??''; + if (!imageMatch.contains(".svg")) { + String match = imageMatch.replaceAll(")", "?raw=true)"); + if (!match.contains(".svg") && match.contains("http")) { + ///增加点击 + String src = match + .replaceAll( RegExp(r'!\[.*\]\('), "") + .replaceAll(")", ""); + String actionMatch = "[$match]($src)"; + match = actionMatch; + } else { + match = ""; + } + mdDataCode = mdDataCode.replaceAll(m.group(0)??'', match); + } + } + } + + ///优化img标签的src资源 + tags = expImg.allMatches(markdownData); + if (tags.isNotEmpty) { + for (Match m in tags) { + String imageTag = m.group(0)??''; + String match = imageTag; + Iterable srcTags = expSrc.allMatches(imageTag); + for (Match srcMatch in srcTags) { + String srcString = srcMatch.group(0)??''; + if (srcString.contains("http")) { + String newSrc = srcString.substring( + srcString.indexOf("http"), srcString.length - 1) + + "?raw=true"; + match = "[![]($newSrc)]($newSrc)"; + } + } + mdDataCode = mdDataCode.replaceAll(imageTag, match); + } + } + } catch (e) { + print(e.toString()); + } + return mdDataCode; + } + + @override + Widget build(BuildContext context) { + return Container( + color: _getBackgroundColor(context), + padding: const EdgeInsets.all(5.0), + child: SingleChildScrollView( + child: MarkdownBody( + styleSheet: _getStyle(context), + syntaxHighlighter: Highlighter(), + data: _getMarkDownData(markdownData), + onTapLink: (String text, String? href, String title) { +// CommonUtils.launchUrl(context, source); + }, + ), + ), + ); + } +} + +class Highlighter extends SyntaxHighlighter { + @override + TextSpan format(String source) { + String showSource = source.replaceAll("<", "<"); + showSource = showSource.replaceAll(">", ">"); + return DartSyntaxHighlighter().format(showSource); + } +} diff --git a/modules/basic_system/toly_ui/lib/markdown/md_text_styles.dart b/modules/basic_system/toly_ui/lib/markdown/md_text_styles.dart new file mode 100644 index 000000000..0819a6fcf --- /dev/null +++ b/modules/basic_system/toly_ui/lib/markdown/md_text_styles.dart @@ -0,0 +1,175 @@ + +import 'package:flutter/material.dart'; + +class MdTextStyles{ + static const Color primaryValue = Color(0xFF24292E); + static const Color primaryLightValue = Color(0xFF42464b); + static const Color primaryDarkValue = Color(0xFF121917); + static const Color miWhite = Color(0xffececec); + static const Color white = Color(0xFFFFFFFF); + static const Color actionBlue = Color(0xff267aff); + static const Color subTextColor = Color(0xff959595); + static const Color subLightTextColor = Color(0xffc4c4c4); + static const Color mainTextColor = primaryDarkValue; + static const Color textColorWhite = white; + + static const lagerTextSize = 30.0; + static const bigTextSize = 23.0; + static const normalTextSize = 18.0; + static const middleTextWhiteSize = 16.0; + static const smallTextSize = 14.0; + static const minTextSize = 12.0; + + static const minText = TextStyle( + color: subLightTextColor, + fontSize: minTextSize, + ); + + + static const smallTextWhite = TextStyle( + color: textColorWhite, + fontSize: smallTextSize, + ); + + + static const smallText = TextStyle( + color: mainTextColor, + fontSize: smallTextSize, + ); + + static const smallTextBold = TextStyle( + color: mainTextColor, + fontSize: smallTextSize, + fontWeight: FontWeight.bold, + ); + + static const smallSubLightText = TextStyle( + color: subLightTextColor, + fontSize: smallTextSize, + ); + + static const smallActionLightText = TextStyle( + color: actionBlue, + fontSize: smallTextSize, + ); + + static const smallMiLightText = TextStyle( + color: miWhite, + fontSize: smallTextSize, + ); + + static const smallSubText = TextStyle( + color: subTextColor, + fontSize: smallTextSize, + ); + + static const middleText = TextStyle( + color: mainTextColor, + fontSize: middleTextWhiteSize, + ); + + static const middleTextWhite = TextStyle( + color: textColorWhite, + fontSize: middleTextWhiteSize, + ); + + static const middleSubText = TextStyle( + color: subTextColor, + fontSize: middleTextWhiteSize, + ); + + static const middleSubLightText = TextStyle( + color: subLightTextColor, + fontSize: middleTextWhiteSize, + ); + + static const middleTextBold = TextStyle( + color: mainTextColor, + fontSize: middleTextWhiteSize, + fontWeight: FontWeight.bold, + ); + + static const middleTextWhiteBold = TextStyle( + color: textColorWhite, + fontSize: middleTextWhiteSize, + fontWeight: FontWeight.bold, + ); + + static const middleSubTextBold = TextStyle( + color: subTextColor, + fontSize: middleTextWhiteSize, + fontWeight: FontWeight.bold, + ); + + static const normalText = TextStyle( + color: mainTextColor, + fontSize: normalTextSize, + ); + + static const normalTextBold = TextStyle( + color: mainTextColor, + fontSize: normalTextSize, + fontWeight: FontWeight.bold, + ); + + static const normalSubText = TextStyle( + color: subTextColor, + fontSize: normalTextSize, + ); + + static const normalTextWhite = TextStyle( + color: textColorWhite, + fontSize: normalTextSize, + ); + + static const normalTextMitWhiteBold = TextStyle( + color: miWhite, + fontSize: normalTextSize, + fontWeight: FontWeight.bold, + ); + + static const normalTextActionWhiteBold = TextStyle( + color: actionBlue, + fontSize: normalTextSize, + fontWeight: FontWeight.bold, + ); + + static const normalTextLight = TextStyle( + color: primaryLightValue, + fontSize: normalTextSize, + ); + + static const largeText = TextStyle( + color: mainTextColor, + fontSize: bigTextSize, + ); + + static const largeTextBold = TextStyle( + color: mainTextColor, + fontSize: bigTextSize, + fontWeight: FontWeight.bold, + ); + + static const largeTextWhite = TextStyle( + color: textColorWhite, + fontSize: bigTextSize, + ); + + static const largeTextWhiteBold = TextStyle( + color: textColorWhite, + fontSize: bigTextSize, + fontWeight: FontWeight.bold, + ); + + static const largeLargeTextWhite = TextStyle( + color: textColorWhite, + fontSize: lagerTextSize, + fontWeight: FontWeight.bold, + ); + + static const largeLargeText = TextStyle( + color: primaryValue, + fontSize: lagerTextSize, + fontWeight: FontWeight.bold, + ); +} \ No newline at end of file diff --git a/modules/basic_system/toly_ui/lib/markdown/syntax_high_lighter.dart b/modules/basic_system/toly_ui/lib/markdown/syntax_high_lighter.dart new file mode 100644 index 000000000..10ae34fd1 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/markdown/syntax_high_lighter.dart @@ -0,0 +1,413 @@ +import 'package:string_scanner/string_scanner.dart'; +import 'package:flutter/material.dart'; + +class SyntaxHighlighterStyle { + SyntaxHighlighterStyle( + {this.baseStyle, + this.numberStyle, + this.commentStyle, + this.keywordStyle, + this.stringStyle, + this.punctuationStyle, + this.classStyle, + this.constantStyle}); + +//123 + static SyntaxHighlighterStyle defaultStyle() { + return SyntaxHighlighterStyle( + baseStyle: const TextStyle(color: Color.fromRGBO(212, 212, 212, 1.0)), + numberStyle: TextStyle(color: Colors.blue[800]), + commentStyle: const TextStyle(color: Color.fromRGBO(124, 126, 120, 1.0)), + keywordStyle: const TextStyle(color: Color.fromRGBO(228, 125, 246, 1.0)), + stringStyle: const TextStyle(color: Color.fromRGBO(150, 190, 118, 1.0)), + punctuationStyle: + const TextStyle(color: Color.fromRGBO(212, 212, 212, 1.0)), + classStyle: const TextStyle(color: Color.fromRGBO(150, 190, 118, 1.0)), + constantStyle: TextStyle(color: Colors.brown[500])); + } + + final TextStyle? baseStyle; + final TextStyle? numberStyle; + final TextStyle? commentStyle; + final TextStyle? keywordStyle; + final TextStyle? stringStyle; + final TextStyle? punctuationStyle; + final TextStyle? classStyle; + final TextStyle? constantStyle; +} + +abstract class SyntaxCostomHighlighter { + TextSpan format(String src); +} + +class DartSyntaxHighlighter extends SyntaxCostomHighlighter { + DartSyntaxHighlighter([this._style]) { + _spans = <_HighlightSpan>[]; + + _style ??= SyntaxHighlighterStyle.defaultStyle(); + } + + SyntaxHighlighterStyle? _style; + + static const List _kKeywords = [ + 'abstract', + 'as', + 'assert', + 'async', + 'await', + 'break', + 'case', + 'catch', + 'class', + 'const', + 'continue', + 'default', + 'deferred', + 'do', + 'dynamic', + 'else', + 'enum', + 'export', + 'external', + 'extends', + 'factory', + 'false', + 'final', + 'finally', + 'for', + 'get', + 'if', + 'implements', + 'import', + 'in', + 'is', + 'library', + '', + 'null', + 'operator', + 'part', + 'rethrow', + 'return', + 'set', + 'static', + 'super', + 'switch', + 'sync', + 'this', + 'throw', + 'true', + 'try', + 'typedef', + 'var', + 'void', + 'while', + 'with', + 'yield', + 'print', + 'function', + 'public', + 'protected', + 'private', + 'namespace', + 'using', + 'extends', + 'let', + 'export', + 'default', + 'import', + 'from', + 'PureCommponent', + 'constructor', + 'render', + '\$sudo', + 'console', + 'instanceof' + ]; + + static const List _kBuiltInTypes = [ + 'int', + 'double', + 'num', + 'bool' + ]; + + String _src=''; + late StringScanner _scanner; + + List<_HighlightSpan> _spans =[]; + + @override + TextSpan format(String src) { + _src = src; + _scanner = StringScanner(_src); + + if (_generateSpans()) { + // Successfully parsed the code + List formattedText = []; + int currentPosition = 0; + + for (_HighlightSpan span in _spans) { + if (currentPosition != span.start) { + formattedText.add( + TextSpan(text: _src.substring(currentPosition, span.start))); + } + + formattedText.add( TextSpan( + style: span.textStyle(_style), text: span.textForSpan(_src))); + + currentPosition = span.end; + } + + if (currentPosition != _src.length) { + formattedText.add( + TextSpan(text: _src.substring(currentPosition, _src.length))); + } + + return TextSpan(style: _style?.baseStyle, children: formattedText); + } else { + // Parsing failed, return with only basic formatting + return TextSpan(style: _style?.baseStyle, text: src); + } + } + + bool _generateSpans() { + int lastLoopPosition = _scanner.position; + + try { + while (!_scanner.isDone) { + // Skip White space + _scanner.scan( RegExp(r"\s+")); + + // Block comments + if (_scanner.scan( RegExp(r"/\*(.|\n)*\*/"))) { + _spans.add( _HighlightSpan(_HighlightType.comment, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Line comments + if (_scanner.scan("//")) { + int startComment = _scanner.lastMatch?.start??0; + + bool eof = false; + int endComment; + if (_scanner.scan( RegExp(r".*\n"))) { + endComment = _scanner.lastMatch?.end??0 - 1; + } else { + eof = true; + endComment = _src.length; + } + + _spans.add( _HighlightSpan( + _HighlightType.comment, startComment, endComment)); + + if (eof) break; + + continue; + } + + if (_scanner.scan("#")) { + int startComment = _scanner.lastMatch?.start??0; + + bool eof = false; + int endComment; + + if (_scanner.scan( RegExp(r".*\n"))) { + endComment = _scanner.lastMatch?.end??0 - 1; + } else { + eof = true; + endComment = _src.length; + } + + _spans.add( _HighlightSpan( + _HighlightType.comment, startComment, endComment)); + + if (eof) break; + + continue; + } + // Raw r"String" + if (_scanner.scan( RegExp(r'r".*"'))) { + _spans.add( _HighlightSpan(_HighlightType.string, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Raw r'String' + if (_scanner.scan( RegExp(r"r'.*'"))) { + _spans.add( _HighlightSpan(_HighlightType.string, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Multiline """String""" + if (_scanner.scan( RegExp(r'"""(?:[^"\\]|\\(.|\n))*"""'))) { + _spans.add( _HighlightSpan(_HighlightType.string, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Multiline '''String''' + if (_scanner.scan( RegExp(r"'''(?:[^'\\]|\\(.|\n))*'''"))) { + _spans.add( _HighlightSpan(_HighlightType.string, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // "String" + if (_scanner.scan( RegExp(r'"(?:[^"\\]|\\.)*"'))) { + _spans.add( _HighlightSpan(_HighlightType.string, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // 'String' + if (_scanner.scan( RegExp(r"'(?:[^'\\]|\\.)*'"))) { + _spans.add( _HighlightSpan(_HighlightType.string, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Double + if (_scanner.scan( RegExp(r"\d+\.\d+"))) { + _spans.add( _HighlightSpan(_HighlightType.number, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Integer + if (_scanner.scan( RegExp(r"\d+"))) { + _spans.add( _HighlightSpan(_HighlightType.number, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Punctuation + if (_scanner.scan( RegExp(r"[\[\]{}().!=<>&\|\?\+\-\*/%\^~;:,]"))) { + _spans.add( _HighlightSpan(_HighlightType.punctuation, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + //中文 + if (_scanner.scan( RegExp(r"[\u4e00-\u9fa5]"))) { + _spans.add( _HighlightSpan(_HighlightType.punctuation, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Metadata + if (_scanner.scan( RegExp(r"@\w+"))) { + _spans.add( _HighlightSpan(_HighlightType.keyword, + _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + continue; + } + + // Words + if (_scanner.scan( RegExp(r"\w+"))) { + _HighlightType? type; + + String word = _scanner.lastMatch![0]!; + if (word.startsWith("_")) word = word.substring(1); + + if (_kKeywords.contains(word)) { + type = _HighlightType.keyword; + } else if (_kBuiltInTypes.contains(word)) { + type = _HighlightType.keyword; + } else if (_firstLetterIsUpperCase(word)) { + type = _HighlightType.klass; + } else if (word.length >= 2 && + word.startsWith("k") && + _firstLetterIsUpperCase(word.substring(1))) { + type = _HighlightType.constant; + } + + if (type != null) { + _spans.add( _HighlightSpan( + type, _scanner.lastMatch?.start??0, _scanner.lastMatch?.end??0)); + } + } + + // Check if this loop did anything + if (lastLoopPosition == _scanner.position) { + // Failed to parse this file, abort gracefully + if (_spans.isNotEmpty) { + _spans.add( _HighlightSpan(_HighlightType.punctuation, + lastLoopPosition, _scanner.string.length - 1)); + _simplify(); + return true; + } + return false; + } + + lastLoopPosition = _scanner.position; + } + } catch (e) { + print(e.toString()); + } + + _simplify(); + return true; + } + + void _simplify() { + for (int i = _spans.length - 2; i >= 0; i -= 1) { + if (_spans[i].type == _spans[i + 1].type && + _spans[i].end == _spans[i + 1].start) { + _spans[i] = _HighlightSpan( + _spans[i].type, _spans[i].start, _spans[i + 1].end); + _spans.removeAt(i + 1); + } + } + } + + bool _firstLetterIsUpperCase(String str) { + if (str.isNotEmpty) { + String first = str.substring(0, 1); + return first == first.toUpperCase(); + } + return false; + } +} + +enum _HighlightType { + number, + comment, + keyword, + string, + punctuation, + klass, + constant +} + +class _HighlightSpan { + _HighlightSpan(this.type, this.start, this.end); + + final _HighlightType type; + final int start; + final int end; + + String textForSpan(String src) { + return src.substring(start, end); + } + + TextStyle? textStyle(SyntaxHighlighterStyle? style) { + if (type == _HighlightType.number) { + return style?.numberStyle; + } else if (type == _HighlightType.comment) { + return style?.commentStyle; + } else if (type == _HighlightType.keyword) { + return style?.keywordStyle; + } else if (type == _HighlightType.string) { + return style?.stringStyle; + } else if (type == _HighlightType.punctuation) { + return style?.punctuationStyle; + } else if (type == _HighlightType.klass) { + return style?.classStyle; + } else if (type == _HighlightType.constant) { + return style?.constantStyle; + } else { + return style?.baseStyle; + } + } +} diff --git a/modules/basic_system/toly_ui/lib/object/windmill.dart b/modules/basic_system/toly_ui/lib/object/windmill.dart new file mode 100644 index 000000000..9e271be28 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/object/windmill.dart @@ -0,0 +1,62 @@ + +import 'dart:math'; + +import 'package:flutter/material.dart'; + +class WindmillWidget extends StatelessWidget { + final double rotate; + final double radius; + const WindmillWidget({Key? key, this.rotate=0,this.radius = 60}) : super(key: key); + + @override + Widget build(BuildContext context) { + return CustomPaint( + size: Size(radius, radius), + painter: WindmillPainter(rotate), + ); + } +} + +const List kColors = [ + Color(0xffE74437), + Color(0xffFBBD19), + Color(0xff3482F0), + Color(0xff30A04C) +]; + +class WindmillPainter extends CustomPainter { + final double rotate; + + WindmillPainter(this.rotate); + + @override + void paint(Canvas canvas, Size size) { + + canvas.translate(size.width / 2, size.height / 2); + double d = size.width * 0.5; + canvas.rotate(rotate); + Paint paint = Paint(); + for (Color color in kColors) { + Path path1 = Path() + ..moveTo(0, -d * 46 / 203) + ..lineTo(0, -d * 203 / 203) + ..lineTo(102 / 203 * d, -102 / 203 * d) + ..lineTo(12 / 203 * d, -12 / 203 * d) + ..close(); + canvas.drawPath(path1, paint..color = color); + + Path path2 = Path() + ..moveTo(12 / 203 * d, -12 / 203 * d) + ..lineTo(102 / 203 * d, -102 / 203 * d) + ..lineTo(102 / 203 * d, 0) + ..lineTo(46 / 203 * d, 0) + ..close(); + canvas.drawPath(path2, paint..color = color.withOpacity(0.2)); + canvas.rotate(pi / 2); + } + } + + + @override + bool shouldRepaint(covariant WindmillPainter oldDelegate) => oldDelegate.rotate!=rotate; +} \ No newline at end of file diff --git a/modules/basic_system/toly_ui/lib/popable/drop_selectable_widget.dart b/modules/basic_system/toly_ui/lib/popable/drop_selectable_widget.dart new file mode 100644 index 000000000..b0b637c8d --- /dev/null +++ b/modules/basic_system/toly_ui/lib/popable/drop_selectable_widget.dart @@ -0,0 +1,207 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +typedef OnDropSelected = void Function(int index); + +class DropSelectableWidget extends StatefulWidget { + final List data; + final OnDropSelected? onDropSelected; + final Color disableColor; + final double iconSize; + final double height; + final double width; + final double fontSize; + + const DropSelectableWidget( + {Key? key, + this.data = const [], + this.onDropSelected, + this.disableColor = Colors.black, + this.iconSize = 24, + this.height = 30, + this.width = 200, + this.fontSize = 14, + }) + : super(key: key); + + @override + _DropSelectableWidgetState createState() => _DropSelectableWidgetState(); +} + +class _DropSelectableWidgetState extends State + with SingleTickerProviderStateMixin { + late FocusNode _node; + bool _focused = false; + late FocusAttachment _nodeAttachment; + OverlayEntry? _overlayEntry; + late AnimationController _ctrl; + late Animation animation; + final LayerLink layerLink = LayerLink(); + + int _selectedIndex = 0; + + @override + void initState() { + super.initState(); + + _ctrl = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 200), + ); + + animation = Tween(begin: 0, end: pi).animate(_ctrl); + _node = FocusNode() + ..addListener(() { + if (_node.hasFocus != _focused) { + if (!_focused) { + _ctrl.forward(); + _showOverlay(); + } else { + _hideOverlay(); + _ctrl.reverse(); + } + setState(() { + _focused = _node.hasFocus; + }); + } + }); + _nodeAttachment = _node.attach(context); + } + + @override + void dispose() { + _node.dispose(); + _ctrl.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + _nodeAttachment.reparent(); + return TapRegion( + groupId: 'selector', + onTapOutside: (_){ + _node.unfocus(); + }, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (_focused) { + _node.unfocus(); + } else { + _node.requestFocus(); + } + }, + child: CompositedTransformTarget( + link: layerLink, + child: buildTarget(), + ), + ), + ); + } + + void _showOverlay() { + _overlayEntry = _createOverlayEntry(); + Overlay.of(context)?.insert(_overlayEntry!); + } + + void _hideOverlay() { + _overlayEntry?.remove(); + } + + Widget buildTarget() { + return Container( + width: widget.width, + height: widget.height, + padding: const EdgeInsets.only(left: 10, right: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + border: Border.all( + color: _focused ? Colors.blue : widget.disableColor, + )), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.data.isNotEmpty ? widget.data[_selectedIndex] : "暂无数据",style: TextStyle( + height: 1, + fontSize: widget.fontSize + ),), + AnimatedBuilder( + animation: animation, + builder: (_, child) => Transform.rotate( + angle: animation.value, + child: child, + ), + child: Icon( + Icons.keyboard_arrow_down, + size: widget.iconSize, + ), + ), + ], + ), + ); + } + + OverlayEntry _createOverlayEntry() => OverlayEntry( + builder: (BuildContext context) => UnconstrainedBox( + child: CompositedTransformFollower( + link: layerLink, + targetAnchor: Alignment.bottomCenter, + followerAnchor: Alignment.topCenter, + child: Padding( + padding: const EdgeInsets.only(top: 4.0), + child: Material( + shape: const RoundedRectangleBorder( + side: BorderSide.none, + borderRadius: BorderRadius.all(Radius.circular(5))), + elevation: 1, + child: ClipRRect( + borderRadius: BorderRadius.circular(5), + child: Container( + height: 200, + // alignment: Alignment.center, + decoration: const BoxDecoration( + color: Color(0xffDAE3FF), + ), + // padding: const EdgeInsets.only(top: 5), + width: widget.width, + child: CupertinoScrollbar( + child: ListView.builder( + padding: EdgeInsets.zero, + // shrinkWrap: true, + itemCount: widget.data.length, + itemBuilder: _buildItem), + ), + ), + ), + ), + ), + ), + ), + ); + + Widget _buildItem(BuildContext context, int index) { + return TapRegion( + groupId: 'selector', + child: Material( + child: InkWell( + onTap: () { + if (_selectedIndex != index) widget.onDropSelected?.call(index); + _selectedIndex = index; + _overlayEntry?.markNeedsBuild(); + _node.unfocus(); + }, + child: Container( + padding: const EdgeInsets.all(8), + color: index == _selectedIndex + ? Colors.blue.withOpacity(0.2) + : Colors.transparent, + child: Text(widget.data[index],style: TextStyle(fontSize: widget.fontSize),)), + ), + ), + ); + } +} \ No newline at end of file diff --git a/lib/views/components/permanent/burst_menu.dart b/modules/basic_system/toly_ui/lib/selector/burst_menu.dart similarity index 81% rename from lib/views/components/permanent/burst_menu.dart rename to modules/basic_system/toly_ui/lib/selector/burst_menu.dart index e4ae0960d..09acb4a98 100644 --- a/lib/views/components/permanent/burst_menu.dart +++ b/modules/basic_system/toly_ui/lib/selector/burst_menu.dart @@ -27,12 +27,12 @@ class BurstMenu extends StatefulWidget { final Duration duration; final BurstType burstType; final Curve curve; - final BurstMenuItemClick burstMenuItemClick; + final BurstMenuItemClick? burstMenuItemClick; const BurstMenu({ - Key key, - @required this.menus, - @required this.center, + Key? key, + required this.menus, + required this.center, this.radius = 100, this.swapAngle = 120, this.startAngle = -60, @@ -43,57 +43,57 @@ class BurstMenu extends StatefulWidget { this.burstMenuItemClick, }) : super(key: key); - BurstMenu.topLeft({ - this.menus, + const BurstMenu.topLeft({Key? key, + required this.menus, this.burstMenuItemClick, - this.radius, - this.center, + this.radius = 100, + required this.center, this.hideOpacity = 0, this.curve = Curves.ease, this.duration = const Duration(milliseconds: 300), this.burstType = BurstType.topLeft, this.swapAngle = 90, this.startAngle = 0, - }); + }) : super(key: key); - BurstMenu.bottomLeft({ - this.menus, + const BurstMenu.bottomLeft({Key? key, + required this.menus, this.burstMenuItemClick, - this.radius, - this.center, + this.radius = 100, + required this.center, this.hideOpacity = 0, this.curve = Curves.ease, this.duration = const Duration(milliseconds: 300), this.burstType = BurstType.bottomLeft, this.swapAngle = 90, this.startAngle = -90, - }); + }) : super(key: key); - BurstMenu.topRight({ - this.menus, + const BurstMenu.topRight({Key? key, + required this.menus, this.burstMenuItemClick, - this.radius, - this.center, + this.radius = 100, + required this.center, this.hideOpacity = 0, this.curve = Curves.ease, this.duration = const Duration(milliseconds: 500), this.burstType = BurstType.topRight, this.swapAngle = -90, this.startAngle = 180, - }); + }) : super(key: key); - BurstMenu.bottomRight({ - this.menus, + const BurstMenu.bottomRight({Key? key, + required this.menus, this.burstMenuItemClick, - this.radius, - this.center, + this.radius = 100, + required this.center, this.hideOpacity = 0, this.curve = Curves.ease, this.duration = const Duration(milliseconds: 300), this.burstType = BurstType.bottomRight, this.swapAngle = 90, this.startAngle = 180, - }); + }) : super(key: key); @override BurstMenuState createState() => BurstMenuState(); @@ -101,11 +101,11 @@ class BurstMenu extends StatefulWidget { class BurstMenuState extends State with SingleTickerProviderStateMixin { - AnimationController _controller; + late AnimationController _controller; // 是否已关闭 bool _closed = true; - Animation curveAnim; // 1.定义曲线动画 + late Animation curveAnim; // 1.定义曲线动画 @override void initState() { @@ -114,8 +114,8 @@ class BurstMenuState extends State duration: widget.duration, vsync: this, ); - curveAnim = CurvedAnimation( - parent: _controller, curve: widget.curve); //<--2.创建曲线动画 + curveAnim = + CurvedAnimation(parent: _controller, curve: widget.curve); //<--2.创建曲线动画 } @override @@ -132,6 +132,7 @@ class BurstMenuState extends State ? widget.radius : widget.radius * 2, alignment: Alignment.center, + // color: Colors.orange.withOpacity(0.5), child: Flow( delegate: _CircleFlowDelegate(curveAnim, startAngle: widget.startAngle, @@ -153,7 +154,7 @@ class BurstMenuState extends State toggle(); return; } - bool close = widget.burstMenuItemClick.call(index); + bool close = widget.burstMenuItemClick?.call(index) ?? false; if (close) toggle(); } @@ -207,7 +208,7 @@ class _CircleFlowDelegate extends FlowDelegate { void paintChildren(FlowPaintingContext context) { double radius = context.size.shortestSide / 2; final double halfCenterSize = - context.getChildSize(context.childCount - 1).width / 2; + context.getChildSize(context.childCount - 1)?.width ?? 0 / 2; switch (burstType) { case BurstType.circle: @@ -215,7 +216,7 @@ class _CircleFlowDelegate extends FlowDelegate { break; case BurstType.topLeft: Offset centerOffset = - Offset(-radius + halfCenterSize, -radius + halfCenterSize); + Offset(-radius + halfCenterSize, -radius + halfCenterSize); paintWithOffset(context, centerOffset); break; case BurstType.bottomLeft: @@ -249,13 +250,16 @@ class _CircleFlowDelegate extends FlowDelegate { if (animation.value > hideOpacity) { for (int i = 0; i < count; i++) { - final double cSizeX = context.getChildSize(i).width / 2; - final double cSizeY = context.getChildSize(i).height / 2; + Size? size = context.getChildSize(i); + if(size == null) continue; + final double cSizeX = context.getChildSize(i)!.width/ 2; + final double cSizeY = context.getChildSize(i)!.height/ 2; final double beforeRadius = (radius - cSizeX); final double now = beforeRadius + centerOffset.dy.abs(); final swapRadius = (radius - cSizeX) / beforeRadius * now; + final double offsetX = animation.value * swapRadius * cos(i * perRad + rotate) + radius + @@ -277,15 +281,13 @@ class _CircleFlowDelegate extends FlowDelegate { } } + Size? size = context.getChildSize(context.childCount - 1); + if (size == null) return; context.paintChild( context.childCount - 1, transform: Matrix4.translationValues( - radius - - context.getChildSize(context.childCount - 1).width / 2 + - centerOffset.dx, - radius - - context.getChildSize(context.childCount - 1).height / 2 + - centerOffset.dy, + radius - size.width / 2 + centerOffset.dx, + radius - size.height / 2 + centerOffset.dy, 0.0, ), ); diff --git a/lib/views/components/project/color_chooser.dart b/modules/basic_system/toly_ui/lib/selector/color_chooser.dart similarity index 78% rename from lib/views/components/project/color_chooser.dart rename to modules/basic_system/toly_ui/lib/selector/color_chooser.dart index f012ee223..9d70c648a 100644 --- a/lib/views/components/project/color_chooser.dart +++ b/modules/basic_system/toly_ui/lib/selector/color_chooser.dart @@ -1,20 +1,22 @@ import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/permanent/circle.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; + +import '../button/feedback_widget.dart'; + + typedef CheckCallback = void Function(T color); class ColorChooser extends StatefulWidget { - ColorChooser( - {Key key, + const ColorChooser( + {Key? key, this.defaultIndex=0, this.radius = 10, - @required this.colors, - @required this.onChecked}) + required this.colors, + required this.onChecked}) : super(key: key); final double radius; final List colors; - final Function(Color) onChecked; + final Function(Color)? onChecked; final int defaultIndex; @override @@ -22,7 +24,7 @@ class ColorChooser extends StatefulWidget { } class _ColorChooserState extends State { - List _checkLi; + List _checkLi=[]; int _perPosition = 0; @override @@ -43,19 +45,19 @@ class _ColorChooserState extends State { _checkLi[_perPosition] = false; _perPosition = i; _checkLi[i] = true; - if (widget.onChecked != null) widget.onChecked(widget.colors[i]); + widget.onChecked?.call(widget.colors[i]); setState(() {}); }, - child: Circle( - color: widget.colors[i], + child: CircleAvatar( + backgroundColor: widget.colors[i], radius: widget.radius, child: _checkLi[i] - ? Icon( + ? const Icon( Icons.star, size: 15, color: Colors.white, ) - : null, + : const SizedBox.shrink(), // checked: _checkLi[i] ))); } @@ -64,11 +66,11 @@ class _ColorChooserState extends State { } class IconChooser extends StatefulWidget { - IconChooser( - {Key key, + const IconChooser( + {Key? key, this.radius = 20, - @required this.icons, - @required this.onChecked, + required this.icons, + required this.onChecked, this.initialIndex = 0}) : super(key: key); final double radius; @@ -81,7 +83,7 @@ class IconChooser extends StatefulWidget { } class _IconChooserState extends State { - List _checkLi; + List _checkLi=[]; int _perPosition = 0; @override @@ -100,7 +102,7 @@ class _IconChooserState extends State { _checkLi[_perPosition] = false; _perPosition = i; _checkLi[i] = true; - if (widget.onChecked != null) widget.onChecked(i); + widget.onChecked(i); setState(() {}); }, child: buildIcon(checked: _checkLi[i], icon: widget.icons[i]))); @@ -112,7 +114,7 @@ class _IconChooserState extends State { children: li); } - buildIcon({bool checked, IconData icon}) { + Widget buildIcon({bool checked = false, IconData? icon}) { Color defaultColor = Colors.black26; Color activeColor = Colors.blue; return Icon( diff --git a/lib/views/components/permanent/multi_chip_filter.dart b/modules/basic_system/toly_ui/lib/selector/multi_chip_filter.dart similarity index 80% rename from lib/views/components/permanent/multi_chip_filter.dart rename to modules/basic_system/toly_ui/lib/selector/multi_chip_filter.dart index a9fa7928c..665e1c68d 100644 --- a/lib/views/components/permanent/multi_chip_filter.dart +++ b/modules/basic_system/toly_ui/lib/selector/multi_chip_filter.dart @@ -9,17 +9,17 @@ typedef BoolWidgetBuilder = Widget Function(BuildContext context, bool selected) class MultiChipFilter extends StatefulWidget { final List data; final BoolWidgetBuilder labelBuilder; - final IndexedWidgetBuilder avatarBuilder; + final IndexedWidgetBuilder? avatarBuilder; final Function(List) onChange; - MultiChipFilter({@required this.data,@required this.labelBuilder,this.avatarBuilder,@required this.onChange}); + const MultiChipFilter({Key? key, required this.data,required this.labelBuilder,this.avatarBuilder,required this.onChange}) : super(key: key); @override _MultiChipFilterState createState() => _MultiChipFilterState(); } class _MultiChipFilterState extends State> { - List _selected = []; + final List _selected = []; @override Widget build(BuildContext context) { @@ -34,12 +34,12 @@ class _MultiChipFilterState extends State> { bool selected = _selected.contains(index); return FilterChip( selectedColor: Colors.orange.withAlpha(55), - labelPadding: EdgeInsets.only(left: 5,right: 5), + labelPadding: const EdgeInsets.only(left: 5,right: 5), selectedShadowColor: Colors.blue, shadowColor: Colors.orangeAccent, pressElevation: 5, elevation: 3, - avatar: widget.avatarBuilder==null?null:widget.avatarBuilder(context,index), + avatar: widget.avatarBuilder==null?null:widget.avatarBuilder!(context,index), label: widget.labelBuilder(context,selected), selected: selected, onSelected: (bool value) { @@ -49,7 +49,7 @@ class _MultiChipFilterState extends State> { } else { _selected.removeWhere((i) => i == index); } - if(widget.onChange!=null) widget.onChange(_selected); + widget.onChange(_selected); }); }, ); diff --git a/modules/basic_system/toly_ui/lib/sliver_header/sliver_pinned_header.dart b/modules/basic_system/toly_ui/lib/sliver_header/sliver_pinned_header.dart new file mode 100644 index 000000000..9602dedc4 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/sliver_header/sliver_pinned_header.dart @@ -0,0 +1,48 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +class SliverPinnedHeader extends StatelessWidget { + final PreferredSizeWidget child; + final Color color; + + SliverPinnedHeader({required this.child, this.color = Colors.white}); + + @override + Widget build(BuildContext context) { + return SliverPersistentHeader( + pinned: true, + delegate: _SliverPinnedHeaderDelegate( + child: child, + color: color + ), + ); + } +} + +class _SliverPinnedHeaderDelegate extends SliverPersistentHeaderDelegate { + final PreferredSizeWidget child; + final Color color; + + + _SliverPinnedHeaderDelegate({required this.child, required this.color}); + + @override + Widget build(BuildContext context, double shrinkOffset, + bool overlapsContent) { + return ColoredBox( + color: color, + child: child); + } + + @override + double get maxExtent => child.preferredSize.height; + + @override + double get minExtent => child.preferredSize.height; + + @override + bool shouldRebuild(covariant _SliverPinnedHeaderDelegate oldDelegate) { + return oldDelegate.child != child + ||oldDelegate.color != color; + } +} \ No newline at end of file diff --git a/modules/basic_system/toly_ui/lib/sliver_header/sliver_snap_header.dart b/modules/basic_system/toly_ui/lib/sliver_header/sliver_snap_header.dart new file mode 100644 index 000000000..8468cc03d --- /dev/null +++ b/modules/basic_system/toly_ui/lib/sliver_header/sliver_snap_header.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +class SliverSnapHeader extends StatefulWidget { + final PreferredSizeWidget child; + + const SliverSnapHeader({ + Key? key, + required this.child, + }) : super(key: key); + + @override + State createState() => _SliverSnapHeaderState(); +} + +class _SliverSnapHeaderState extends State + with TickerProviderStateMixin { + FloatingHeaderSnapConfiguration? _snapConfiguration; + PersistentHeaderShowOnScreenConfiguration? _showOnScreenConfiguration; + + void _initSnapConfiguration() { + _snapConfiguration = FloatingHeaderSnapConfiguration( + curve: Curves.easeOut, + duration: const Duration(milliseconds: 200), + ); + + _showOnScreenConfiguration = + const PersistentHeaderShowOnScreenConfiguration( + minShowOnScreenExtent: double.infinity); + } + + @override + void initState() { + super.initState(); + _initSnapConfiguration(); + } + + @override + Widget build(BuildContext context) { + return SliverPersistentHeader( + floating: true, + pinned: false, + delegate: _SliverSnapHeaderDelegate( + vsync: this, + child: widget.child, + snapConfiguration: _snapConfiguration, + showOnScreenConfiguration: _showOnScreenConfiguration), + ); + } +} + +class _SliverSnapHeaderDelegate extends SliverPersistentHeaderDelegate { + + final PreferredSizeWidget child; + + @override + final TickerProvider vsync; + + _SliverSnapHeaderDelegate({ + required this.child, + required this.snapConfiguration, + required this.vsync, + required this.showOnScreenConfiguration, + }); + + @override + final FloatingHeaderSnapConfiguration? snapConfiguration; + + @override + final PersistentHeaderShowOnScreenConfiguration? showOnScreenConfiguration; + + @override + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { + return child; + } + + @override + double get maxExtent => child.preferredSize.height; + + @override + double get minExtent => child.preferredSize.height; + + @override + bool shouldRebuild(covariant _SliverSnapHeaderDelegate oldDelegate) { + return oldDelegate.child != child || + vsync != oldDelegate.vsync || + snapConfiguration != oldDelegate.snapConfiguration || + showOnScreenConfiguration != oldDelegate.showOnScreenConfiguration; + } +} diff --git a/modules/basic_system/toly_ui/lib/ti/circle.dart b/modules/basic_system/toly_ui/lib/ti/circle.dart new file mode 100644 index 000000000..d98a440e2 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/ti/circle.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +class Circle extends StatelessWidget { + final Color color; + final double radius; + final bool showShadow; + final Widget? child; + + const Circle({Key? key, this.color=Colors.blue, this.radius=6,this.showShadow=true,this.child}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + alignment: Alignment.center, + child: child ?? const SizedBox.shrink(), + width: 2*radius, + height: 2*radius, + decoration: BoxDecoration( + color: color, + shape: BoxShape.circle, + boxShadow: [ + if (showShadow) + const BoxShadow( + color: Colors.grey, + offset: Offset(.5,.5), + blurRadius: .5, + )] + ), + ); + } +} 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/modules/basic_system/toly_ui/lib/ti/circle_text.dart b/modules/basic_system/toly_ui/lib/ti/circle_text.dart new file mode 100644 index 000000000..32c6a87ba --- /dev/null +++ b/modules/basic_system/toly_ui/lib/ti/circle_text.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; + +class CircleText extends StatelessWidget { + const CircleText({ + Key? key, + required this.text, + this.size = 70, + this.fontSize = 24, + this.color = Colors.white, + this.shadowColor, + this.backgroundColor, + this.roundColor, + }) : super(key: key); + + final String text; //图片 + final double size; //大小 + final double fontSize; //大小 + final Color? shadowColor; //阴影颜色 + final Color color; //阴影颜色 + final Color? roundColor; //边框颜色 + final Color? backgroundColor; //边框颜色 + + @override + Widget build(BuildContext context) { + Widget headIcon = 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: const EdgeInsets.all(3), + child: Container( + alignment: Alignment.center, + width: size, + height: size, + decoration: BoxDecoration( + shape: BoxShape.circle, //圆形装饰线 + color: backgroundColor ?? const Color(0xffD8F5FF), + ), + child: Text( + text.length > 2 ? text.substring(0, 2) : text, + style: TextStyle( + fontSize: fontSize, + color: color, + fontWeight: FontWeight.bold, + shadows: const [ + Shadow( + //阴影 + color: Colors.grey, + offset: Offset(1.0, 1.0), blurRadius: 1.0, + ) + ], + ), + )), + ), + ); + return headIcon; + } +} diff --git a/lib/views/components/permanent/color_wrapper.dart b/modules/basic_system/toly_ui/lib/ti/color_wrapper.dart similarity index 80% rename from lib/views/components/permanent/color_wrapper.dart rename to modules/basic_system/toly_ui/lib/ti/color_wrapper.dart index e14a0bd28..fd0bb84e3 100644 --- a/lib/views/components/permanent/color_wrapper.dart +++ b/modules/basic_system/toly_ui/lib/ti/color_wrapper.dart @@ -5,17 +5,17 @@ import 'package:flutter/material.dart'; /// 说明: class WrapColor extends StatelessWidget { - final Widget child; - final Color color; + final Widget? child; + final Color? color; final double radius; final EdgeInsetsGeometry padding; - WrapColor( - {this.child, - this.color, + const WrapColor( + {Key? key, this.child, + this.color, this.radius = 5, this.padding = - const EdgeInsets.only(left: 4, right: 4, top: 0, bottom: 0)}); + const EdgeInsets.only(left: 4, right: 4, top: 0, bottom: 0)}) : super(key: key); @override Widget build(BuildContext context) { @@ -35,7 +35,7 @@ class Circled extends StatelessWidget { final Color color; final double radius; - Circled({this.child, this.color = Colors.blue, this.radius = 15}); + const Circled({Key? key, required this.child, this.color = Colors.blue, this.radius = 15}) : super(key: key); @override Widget build(BuildContext context) { diff --git a/modules/basic_system/toly_ui/lib/ti/math_runner.dart b/modules/basic_system/toly_ui/lib/ti/math_runner.dart new file mode 100644 index 000000000..77e595fd1 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/ti/math_runner.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; + +/// create by 张风捷特烈 on 2020/10/24 +/// contact me by email 1981462002@qq.com +/// 说明: + +typedef FunNum1 = Function(double t); + +class MathRunner extends StatefulWidget { + const MathRunner( + {Key? key, + this.child, + required this.f, + required this.g, + this.reverse = true}) + : super(key: key); + + final Widget? child; + final FunNum1 f; + final FunNum1 g; + final bool reverse; + + @override + _MathRunnerState createState() => _MathRunnerState(); +} + +class _MathRunnerState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation animationX; + double _x = -1.0; + double _y = 0; + + @override + void initState() { + _controller = AnimationController( + vsync: this, + duration: const Duration(seconds: 3), + )..repeat(reverse: widget.reverse); + animationX = Tween(begin: -1.0, end: 1.0).animate(_controller) + ..addListener(() { + setState(() { + _x = widget.f(animationX.value); + _y = widget.g(animationX.value); + }); + }); + super.initState(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Align( + alignment: Alignment(_x, _y), + child: widget.child, + ); + } +} 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/lib/views/components/permanent/tag.dart b/modules/basic_system/toly_ui/lib/ti/tag.dart similarity index 81% rename from lib/views/components/permanent/tag.dart rename to modules/basic_system/toly_ui/lib/ti/tag.dart index 957d3c713..0f3d8a4e4 100644 --- a/lib/views/components/permanent/tag.dart +++ b/modules/basic_system/toly_ui/lib/ti/tag.dart @@ -6,11 +6,11 @@ class Tag extends StatelessWidget { final double tranRate; final Color color; - const Tag({this.size = const Size(100, 150),this.shadowHeight=9.0,this.tranRate=0.25,this.color=Colors.red}); + const Tag({Key? key, this.size = const Size(100, 150),this.shadowHeight=9.0,this.tranRate=0.25,this.color=Colors.red}) : super(key: key); @override Widget build(BuildContext context) { - return Container( + return SizedBox( width: size.width, height: size.height, child: CustomPaint( @@ -27,19 +27,21 @@ class Tag extends StatelessWidget { class _TagPaint extends CustomPainter { Path path = Path(); Path shadowPath = Path(); - Paint _paint; - final tranRate; + final Paint _paint; + final double tranRate; final double shadowHeight; final Color color; final rate = 0.5; - _TagPaint({this.tranRate, this.color ,this.shadowHeight}) + _TagPaint({this.tranRate=0,required this.color ,required this.shadowHeight}) : _paint = Paint()..color = color; @override void paint(Canvas canvas, Size size) { canvas.clipRect(Offset.zero & size); + shadowPath.reset(); + path.reset(); path.moveTo(0, 0); path.relativeLineTo(size.width-shadowHeight*rate, 0); diff --git a/lib/views/components/permanent/text_typer.dart b/modules/basic_system/toly_ui/lib/ti/text_typer.dart similarity index 89% rename from lib/views/components/permanent/text_typer.dart rename to modules/basic_system/toly_ui/lib/ti/text_typer.dart index 52ca62820..34e5e4ef4 100644 --- a/lib/views/components/permanent/text_typer.dart +++ b/modules/basic_system/toly_ui/lib/ti/text_typer.dart @@ -7,9 +7,9 @@ class TextTyper extends StatefulWidget { final Duration duration; final TextStyle textStyle; - TextTyper( - {this.text, - Key key, + const TextTyper( + {required this.text, + Key? key, this.textStyle = const TextStyle(fontWeight: FontWeight.w600, fontSize: 20.0), this.duration = const Duration(milliseconds: 150)}) @@ -21,7 +21,7 @@ class TextTyper extends StatefulWidget { class _TextTyperState extends State { final ValueNotifier data = ValueNotifier(""); - Timer _timer; + Timer? _timer; @override void initState() { @@ -58,7 +58,7 @@ class _TextTyperState extends State { ); } - Widget _buildByAnim(BuildContext context, Widget child) => Text( + Widget _buildByAnim(BuildContext context, Widget? child) => Text( data.value, style: widget.textStyle, ); diff --git a/modules/basic_system/toly_ui/lib/ti/toly_switch_list_tile.dart b/modules/basic_system/toly_ui/lib/ti/toly_switch_list_tile.dart new file mode 100644 index 000000000..ad38db694 --- /dev/null +++ b/modules/basic_system/toly_ui/lib/ti/toly_switch_list_tile.dart @@ -0,0 +1,383 @@ +// 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 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +// Examples can assume: +// void setState(VoidCallback fn) { } +// bool _isSelected = true; + +enum _SwitchListTileType { material, adaptive } + +/// A [ListTile] with a [Switch]. In other words, a switch with a label. +/// +/// {@youtube 560 315 https://www.youtube.com/watch?v=0igIjvtEWNU} +/// +/// The entire list tile is interactive: tapping anywhere in the tile toggles +/// the switch. Tapping and dragging the [Switch] also triggers the [onChanged] +/// callback. +/// +/// To ensure that [onChanged] correctly triggers, the state passed +/// into [value] must be properly managed. This is typically done by invoking +/// [State.setState] in [onChanged] to toggle the state value. +/// +/// The [value], [onChanged], [activeColor], [activeThumbImage], and +/// [inactiveThumbImage] properties of this widget are identical to the +/// similarly-named properties on the [Switch] widget. +/// +/// The [title], [subtitle], [isThreeLine], and [dense] properties are like +/// those of the same name on [ListTile]. +/// +/// The [selected] property on this widget is similar to the [ListTile.selected] +/// property. This tile's [activeColor] is used for the selected item's text color, or +/// the theme's [SwitchThemeData.overlayColor] if [activeColor] is null. +/// +/// This widget does not coordinate the [selected] state and the +/// [value]; to have the list tile appear selected when the +/// switch button is on, use the same value for both. +/// +/// The switch is shown on the right by default in left-to-right languages (i.e. +/// in the [ListTile.trailing] slot) which can be changed using [controlAffinity]. +/// The [secondary] widget is placed in the [ListTile.leading] slot. +/// +/// This widget requires a [Material] widget ancestor in the tree to paint +/// itself on, which is typically provided by the app's [Scaffold]. +/// The [tileColor], and [selectedTileColor] are not painted by the +/// [TolySwitchListTile] itself but by the [Material] widget ancestor. In this +/// case, one can wrap a [Material] widget around the [TolySwitchListTile], e.g.: +/// +/// {@tool snippet} +/// ```dart +/// Container( +/// color: Colors.green, +/// child: Material( +/// child: SwitchListTile( +/// tileColor: Colors.red, +/// title: const Text('SwitchListTile with red background'), +/// value: true, +/// onChanged:(bool? value) { }, +/// ), +/// ), +/// ) +/// ``` +/// {@end-tool} +/// +/// ## Performance considerations when wrapping [TolySwitchListTile] with [Material] +/// +/// Wrapping a large number of [TolySwitchListTile]s individually with [Material]s +/// is expensive. Consider only wrapping the [TolySwitchListTile]s that require it +/// or include a common [Material] ancestor where possible. +/// +/// To show the [TolySwitchListTile] as disabled, pass null as the [onChanged] +/// callback. +/// +/// {@tool dartpad} +/// ![SwitchListTile sample](https://flutter.github.io/assets-for-api-docs/assets/material/switch_list_tile.png) +/// +/// This widget shows a switch that, when toggled, changes the state of a [bool] +/// member field called `_lights`. +/// +/// ** See code in examples/api/lib/material/switch_list_tile/switch_list_tile.0.dart ** +/// {@end-tool} +/// +/// ## Semantics in SwitchListTile +/// +/// Since the entirety of the SwitchListTile is interactive, it should represent +/// itself as a single interactive entity. +/// +/// To do so, a SwitchListTile widget wraps its children with a [MergeSemantics] +/// widget. [MergeSemantics] will attempt to merge its descendant [Semantics] +/// nodes into one node in the semantics tree. Therefore, SwitchListTile will +/// throw an error if any of its children requires its own [Semantics] node. +/// +/// For example, you cannot nest a [RichText] widget as a descendant of +/// SwitchListTile. [RichText] has an embedded gesture recognizer that +/// requires its own [Semantics] node, which directly conflicts with +/// SwitchListTile's desire to merge all its descendants' semantic nodes +/// into one. Therefore, it may be necessary to create a custom radio tile +/// widget to accommodate similar use cases. +/// +/// {@tool dartpad} +/// ![Switch list tile semantics sample](https://flutter.github.io/assets-for-api-docs/assets/material/switch_list_tile_semantics.png) +/// +/// Here is an example of a custom labeled radio widget, called +/// LinkedLabelRadio, that includes an interactive [RichText] widget that +/// handles tap gestures. +/// +/// ** See code in examples/api/lib/material/switch_list_tile/switch_list_tile.1.dart ** +/// {@end-tool} +/// +/// ## SwitchListTile isn't exactly what I want +/// +/// If the way SwitchListTile pads and positions its elements isn't quite what +/// you're looking for, you can create custom labeled switch widgets by +/// combining [Switch] with other widgets, such as [Text], [Padding] and +/// [InkWell]. +/// +/// {@tool dartpad} +/// ![Custom switch list tile sample](https://flutter.github.io/assets-for-api-docs/assets/material/switch_list_tile_custom.png) +/// +/// Here is an example of a custom LabeledSwitch widget, but you can easily +/// make your own configurable widget. +/// +/// ** See code in examples/api/lib/material/switch_list_tile/switch_list_tile.2.dart ** +/// {@end-tool} +/// +/// See also: +/// +/// * [ListTileTheme], which can be used to affect the style of list tiles, +/// including switch list tiles. +/// * [CheckboxListTile], a similar widget for checkboxes. +/// * [RadioListTile], a similar widget for radio buttons. +/// * [ListTile] and [Switch], the widgets from which this widget is made. +class TolySwitchListTile extends StatelessWidget { + /// Creates a combination of a list tile and a switch. + /// + /// The switch tile itself does not maintain any state. Instead, when the + /// state of the switch changes, the widget calls the [onChanged] callback. + /// Most widgets that use a switch will listen for the [onChanged] callback + /// and rebuild the switch tile with a new [value] to update the visual + /// appearance of the switch. + /// + /// The following arguments are required: + /// + /// * [value] determines whether this switch is on or off. + /// * [onChanged] is called when the user toggles the switch on or off. + const TolySwitchListTile({ + super.key, + required this.value, + required this.onChanged, + this.tileColor, + this.activeColor, + this.activeTrackColor, + this.inactiveThumbColor, + this.inactiveTrackColor, + this.activeThumbImage, + this.inactiveThumbImage, + this.title, + this.subtitle, + this.isThreeLine = false, + this.dense, + this.contentPadding, + this.secondary, + this.selected = false, + this.autofocus = false, + this.controlAffinity = ListTileControlAffinity.platform, + this.shape, + this.selectedTileColor, + this.visualDensity, + this.focusNode, + this.onFocusChange, + this.enableFeedback, + this.hoverColor, + }); + + /// Whether this switch is checked. + /// + /// This property must not be null. + final bool value; + + /// Called when the user toggles the switch on or off. + /// + /// The switch passes the new value to the callback but does not actually + /// change state until the parent widget rebuilds the switch tile with the + /// new value. + /// + /// If null, the switch will be displayed as disabled. + /// + /// The callback provided to [onChanged] should update the state of the parent + /// [StatefulWidget] using the [State.setState] method, so that the parent + /// gets rebuilt; for example: + /// + /// {@tool snippet} + /// ```dart + /// SwitchListTile( + /// value: _isSelected, + /// onChanged: (bool newValue) { + /// setState(() { + /// _isSelected = newValue; + /// }); + /// }, + /// title: const Text('Selection'), + /// ) + /// ``` + /// {@end-tool} + final ValueChanged? onChanged; + + /// The color to use when this switch is on. + /// + /// Defaults to accent color of the current [Theme]. + final Color? activeColor; + + /// The color to use on the track when this switch is on. + /// + /// Defaults to [ThemeData.toggleableActiveColor] with the opacity set at 50%. + /// + /// Ignored if created with [SwitchListTile.adaptive]. + final Color? activeTrackColor; + + /// The color to use on the thumb when this switch is off. + /// + /// Defaults to the colors described in the Material design specification. + /// + /// Ignored if created with [SwitchListTile.adaptive]. + final Color? inactiveThumbColor; + + /// The color to use on the track when this switch is off. + /// + /// Defaults to the colors described in the Material design specification. + /// + /// Ignored if created with [SwitchListTile.adaptive]. + final Color? inactiveTrackColor; + + /// {@macro flutter.material.ListTile.tileColor} + final Color? tileColor; + + /// An image to use on the thumb of this switch when the switch is on. + final ImageProvider? activeThumbImage; + + /// An image to use on the thumb of this switch when the switch is off. + /// + /// Ignored if created with [SwitchListTile.adaptive]. + final ImageProvider? inactiveThumbImage; + + /// The primary content of the list tile. + /// + /// Typically a [Text] widget. + final Widget? title; + + /// Additional content displayed below the title. + /// + /// Typically a [Text] widget. + final Widget? subtitle; + + /// A widget to display on the opposite side of the tile from the switch. + /// + /// Typically an [Icon] widget. + final Widget? secondary; + + /// Whether this list tile is intended to display three lines of text. + /// + /// If false, the list tile is treated as having one line if the subtitle is + /// null and treated as having two lines if the subtitle is non-null. + final bool isThreeLine; + + /// Whether this list tile is part of a vertically dense list. + /// + /// If this property is null then its value is based on [ListTileThemeData.dense]. + final bool? dense; + + /// The tile's internal padding. + /// + /// Insets a [TolySwitchListTile]'s contents: its [title], [subtitle], + /// [secondary], and [Switch] widgets. + /// + /// If null, [ListTile]'s default of `EdgeInsets.symmetric(horizontal: 16.0)` + /// is used. + final EdgeInsetsGeometry? contentPadding; + + /// Whether to render icons and text in the [activeColor]. + /// + /// No effort is made to automatically coordinate the [selected] state and the + /// [value] state. To have the list tile appear selected when the switch is + /// on, pass the same value to both. + /// + /// Normally, this property is left to its default value, false. + final bool selected; + + /// {@macro flutter.widgets.Focus.autofocus} + final bool autofocus; + + + /// Defines the position of control and [secondary], relative to text. + /// + /// By default, the value of [controlAffinity] is [ListTileControlAffinity.platform]. + final ListTileControlAffinity controlAffinity; + + /// {@macro flutter.material.ListTile.shape} + final ShapeBorder? shape; + + /// If non-null, defines the background color when [TolySwitchListTile.selected] is true. + final Color? selectedTileColor; + + /// Defines how compact the list tile's layout will be. + /// + /// {@macro flutter.material.themedata.visualDensity} + final VisualDensity? visualDensity; + + /// {@macro flutter.widgets.Focus.focusNode} + final FocusNode? focusNode; + + /// {@macro flutter.material.inkwell.onFocusChange} + final ValueChanged? onFocusChange; + + /// {@macro flutter.material.ListTile.enableFeedback} + /// + /// See also: + /// + /// * [Feedback] for providing platform-specific feedback to certain actions. + final bool? enableFeedback; + + /// The color for the tile's [Material] when a pointer is hovering over it. + final Color? hoverColor; + + @override + Widget build(BuildContext context) { + final Widget control = Transform.scale( + scale: 0.8, + child: CupertinoSwitch( + value: value, + activeColor: activeColor, + // thumbColor: activet, + trackColor: activeTrackColor, + onChanged:onChanged + )); + + Widget? leading, trailing; + switch (controlAffinity) { + case ListTileControlAffinity.leading: + leading = control; + trailing = secondary; + break; + case ListTileControlAffinity.trailing: + case ListTileControlAffinity.platform: + leading = secondary; + trailing = control; + break; + } + + final ThemeData theme = Theme.of(context); + final SwitchThemeData switchTheme = SwitchTheme.of(context); + final Set states = { + if (selected) MaterialState.selected, + }; + final Color effectiveActiveColor = activeColor + ?? switchTheme.thumbColor?.resolve(states) + ?? theme.colorScheme.secondary; + return MergeSemantics( + child: ListTile( + selectedColor: effectiveActiveColor, + leading: leading, + title: title, + subtitle: subtitle, + trailing: trailing, + isThreeLine: isThreeLine, + dense: dense, + contentPadding: contentPadding, + enabled: onChanged != null, + onTap: onChanged != null ? () { onChanged!(!value); } : null, + selected: selected, + selectedTileColor: selectedTileColor, + autofocus: autofocus, + shape: shape, + tileColor: tileColor, + visualDensity: visualDensity, + focusNode: focusNode, + onFocusChange: onFocusChange, + enableFeedback: enableFeedback, + hoverColor: hoverColor, + ), + ); + } +} diff --git a/modules/basic_system/toly_ui/lib/toly_ui.dart b/modules/basic_system/toly_ui/lib/toly_ui.dart new file mode 100644 index 000000000..39daae04c --- /dev/null +++ b/modules/basic_system/toly_ui/lib/toly_ui.dart @@ -0,0 +1,27 @@ +export 'code/code.dart'; +export 'ti/circle_image.dart'; +export 'input/edit_panel.dart'; +export 'input/icon_input.dart'; +export 'input/input_button.dart'; +export 'ti/circle_text.dart'; +export 'ti/tag.dart'; +export 'ti/text_typer.dart'; +export 'ti/circle.dart'; +export 'ti/color_wrapper.dart'; +export 'ti/math_runner.dart'; +export 'ti/panel.dart'; +export 'button/feedback_widget.dart'; +export 'popable/drop_selectable_widget.dart'; +export 'selector/color_chooser.dart'; +export 'selector/multi_chip_filter.dart'; +export 'selector/burst_menu.dart'; +export 'decorations/round_rect_rab_indicator.dart'; +export 'dialog/alert_conform_dialog.dart'; +export 'dialog/delete_message_panel.dart'; +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; + +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/modules/basic_system/utils/.gitignore b/modules/basic_system/utils/.gitignore new file mode 100644 index 000000000..96486fd93 --- /dev/null +++ b/modules/basic_system/utils/.gitignore @@ -0,0 +1,30 @@ +# 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/modules/basic_system/utils/.metadata b/modules/basic_system/utils/.metadata new file mode 100644 index 000000000..6c08927eb --- /dev/null +++ b/modules/basic_system/utils/.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: e3c29ec00c9c825c891d75054c63fcc46454dca1 + channel: stable + +project_type: package diff --git a/modules/basic_system/utils/CHANGELOG.md b/modules/basic_system/utils/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/basic_system/utils/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/basic_system/utils/LICENSE b/modules/basic_system/utils/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/basic_system/utils/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/basic_system/utils/README.md b/modules/basic_system/utils/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/basic_system/utils/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/basic_system/utils/analysis_options.yaml b/modules/basic_system/utils/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/basic_system/utils/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/lib/app/utils/color_utils.dart b/modules/basic_system/utils/lib/src/color_utils.dart similarity index 96% rename from lib/app/utils/color_utils.dart rename to modules/basic_system/utils/lib/src/color_utils.dart index 3e245dd46..f95fdcaed 100644 --- a/lib/app/utils/color_utils.dart +++ b/modules/basic_system/utils/lib/src/color_utils.dart @@ -4,8 +4,6 @@ import 'package:flutter/material.dart'; import 'random_provider.dart'; - - class ColorUtils { static Color randomColor({ int limitA = 120, @@ -38,10 +36,10 @@ class ColorUtils { print(e); } switch (code.length) { - case 1 + 6://6位 + case 7://6位 result = Color(value + 0xFF000000); break; - case 1 + 8://8位 + case 9://8位 result = Color(value); break; default: diff --git a/lib/app/utils/convert_man.dart b/modules/basic_system/utils/lib/src/convert_man.dart similarity index 75% rename from lib/app/utils/convert_man.dart rename to modules/basic_system/utils/lib/src/convert_man.dart index f6386ed99..d6c4af3a7 100644 --- a/lib/app/utils/convert_man.dart +++ b/modules/basic_system/utils/lib/src/convert_man.dart @@ -2,15 +2,15 @@ /// contact me by email 1981462002@qq.com /// 说明: -final double _kMillisLimit = 1000.0; +const double _kMillisLimit = 1000.0; -final double _kSecondsLimit = 60 * _kMillisLimit; +const double _kSecondsLimit = 60 * _kMillisLimit; -final double _kMinutesLimit = 60 * _kSecondsLimit; +const double _kMinutesLimit = 60 * _kSecondsLimit; -final double _kHourLimit = 24 * _kMinutesLimit; +const double _kHourLimit = 24 * _kMinutesLimit; -final double _kDaysLimit = 30 * _kHourLimit; +const double _kDaysLimit = 30 * _kHourLimit; class ConvertMan { @@ -37,9 +37,7 @@ class ConvertMan { } static String _getDateStr(DateTime date) { - if (date == null || date.toString() == null) { - return ""; - } else if (date.toString().length < 10) { + if (date.toString().length < 10) { return date.toString(); } return date.toString().substring(0, 10); diff --git a/modules/basic_system/utils/lib/src/http_utils/http_util.dart b/modules/basic_system/utils/lib/src/http_utils/http_util.dart new file mode 100644 index 000000000..e0b734e55 --- /dev/null +++ b/modules/basic_system/utils/lib/src/http_utils/http_util.dart @@ -0,0 +1,46 @@ +import 'package:dio/dio.dart'; + +import 'token_interceptor.dart'; + +const Duration _kReceiveTimeout = Duration(milliseconds: 5000); +const Duration _kSendTimeout = Duration(milliseconds: 5000); +const Duration _kConnectTimeout = Duration(milliseconds: 5000); + +class HttpUtil { + TokenInterceptors? tokenInterceptors; + + static final HttpUtil _instance = HttpUtil._internal(); + + Dio? _dio; + + static HttpUtil get instance => _instance; + + ///通用全局单例,第一次使用时初始化 + HttpUtil._internal() { + _dio ??= Dio( + BaseOptions( + connectTimeout: _kConnectTimeout, + sendTimeout: _kSendTimeout, + receiveTimeout: _kReceiveTimeout), + ); + // _dio!.interceptors.add(LogsInterceptors()); + // _dio.interceptors.add(ResponseInterceptors()); + } + + + Dio get client => _dio!; + + void setToken(String token) { + print('---token---$token-------'); + tokenInterceptors = TokenInterceptors(token: token); + _dio!.interceptors.add(tokenInterceptors!); + } + + void deleteToken() { + _dio!.interceptors.remove(tokenInterceptors); + } + + void rebase(String baseIp) { + _dio!.options.baseUrl = baseIp; + } +} diff --git a/modules/basic_system/utils/lib/src/http_utils/http_utils.dart b/modules/basic_system/utils/lib/src/http_utils/http_utils.dart new file mode 100644 index 000000000..ae0ad5b68 --- /dev/null +++ b/modules/basic_system/utils/lib/src/http_utils/http_utils.dart @@ -0,0 +1,2 @@ +export 'http_util.dart'; +export 'task_result.dart'; \ No newline at end of file diff --git a/modules/basic_system/utils/lib/src/http_utils/logs_interceptor.dart b/modules/basic_system/utils/lib/src/http_utils/logs_interceptor.dart new file mode 100644 index 000000000..04c3368cb --- /dev/null +++ b/modules/basic_system/utils/lib/src/http_utils/logs_interceptor.dart @@ -0,0 +1,29 @@ +import 'package:dio/dio.dart'; + +class LogsInterceptors extends InterceptorsWrapper { + + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { +// print("==========请求baseUrl:${options.baseUrl} =========="); +// print("==========请求url:${options.path} =========="); +// print('==========请求头: ' + options.headers.toString()+"=========="); +// if (options.data != null) { +// print('==========请求参数: ' + options.data.toString()+"=========="); +// } + return handler.next(options); + } + + @override + void onError(DioError err, ErrorInterceptorHandler handler) { + print('==========请求异常: ' + err.toString()+"=========="); + if(err.response!=null){ + print('==========请求异常信息: ' + err.response.toString()+"=========="); + } + return handler.next(err); + } + + @override + void onResponse(Response response, ResponseInterceptorHandler handler) { + return handler.next(response); // continue + } +} \ No newline at end of file diff --git a/lib/app/utils/http_utils/response_interceptor.dart b/modules/basic_system/utils/lib/src/http_utils/response_interceptor.dart similarity index 97% rename from lib/app/utils/http_utils/response_interceptor.dart rename to modules/basic_system/utils/lib/src/http_utils/response_interceptor.dart index 008204f78..fe2034b34 100644 --- a/lib/app/utils/http_utils/response_interceptor.dart +++ b/modules/basic_system/utils/lib/src/http_utils/response_interceptor.dart @@ -1,5 +1,5 @@ // import 'package:dio/dio.dart'; -// import 'result_bean.dart'; +// import 'task_result.dart'; // // // 拦截返回数据,进行统一处理 // class ResponseInterceptors extends InterceptorsWrapper { diff --git a/modules/basic_system/utils/lib/src/http_utils/task_result.dart b/modules/basic_system/utils/lib/src/http_utils/task_result.dart new file mode 100644 index 000000000..cd732a569 --- /dev/null +++ b/modules/basic_system/utils/lib/src/http_utils/task_result.dart @@ -0,0 +1,25 @@ +class TaskResult { + final T? data; + final bool success; + final String msg; + final int count; + + TaskResult({this.data, this.success=false, this.msg='',this.count=0}); + + @override + String toString() { + return 'RepResult{data: $data, status: $success, msg:$msg}'; + } + + const TaskResult.error({required this.msg}) + : success = false, + data = null, + count = 0 + ; + + const TaskResult.success({ + this.data, + this.msg = '', + this.count = 0, + }) : success = true; +} diff --git a/modules/basic_system/utils/lib/src/http_utils/token_interceptor.dart b/modules/basic_system/utils/lib/src/http_utils/token_interceptor.dart new file mode 100644 index 000000000..97c12c59b --- /dev/null +++ b/modules/basic_system/utils/lib/src/http_utils/token_interceptor.dart @@ -0,0 +1,29 @@ +import 'package:dio/dio.dart'; +import 'package:jwt_decoder/jwt_decoder.dart'; + +const String _kTokenKey = 'Authorization'; +const String _kTokenPrefix = 'Bearer '; + +class TokenInterceptors extends InterceptorsWrapper { + String token; + + TokenInterceptors({this.token = ''}); + + void Function()? onTokenDisabled; + + @override + void onRequest(RequestOptions options, RequestInterceptorHandler handler) { + if (token != '') { + bool disable = JwtDecoder.isExpired(token); + if (disable) { + onTokenDisabled?.call(); + } + } + if (token.isNotEmpty) { + options.headers[_kTokenKey] = '$_kTokenPrefix$token'; + } + return handler.next(options); + } + + +} \ No newline at end of file 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/modules/basic_system/utils/lib/src/toast.dart b/modules/basic_system/utils/lib/src/toast.dart new file mode 100644 index 000000000..eff03a103 --- /dev/null +++ b/modules/basic_system/utils/lib/src/toast.dart @@ -0,0 +1,32 @@ +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/modules/basic_system/utils/lib/utils.dart b/modules/basic_system/utils/lib/utils.dart new file mode 100644 index 000000000..f83611f36 --- /dev/null +++ b/modules/basic_system/utils/lib/utils.dart @@ -0,0 +1,6 @@ +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/modules/basic_system/utils/test/utils_test.dart b/modules/basic_system/utils/test/utils_test.dart new file mode 100644 index 000000000..ba04b36ea --- /dev/null +++ b/modules/basic_system/utils/test/utils_test.dart @@ -0,0 +1,5 @@ +import 'package:flutter_test/flutter_test.dart'; + +void main() { + +} diff --git a/modules/knowledge_system/algorithm/.gitignore b/modules/knowledge_system/algorithm/.gitignore new file mode 100644 index 000000000..96486fd93 --- /dev/null +++ b/modules/knowledge_system/algorithm/.gitignore @@ -0,0 +1,30 @@ +# 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/modules/knowledge_system/algorithm/.metadata b/modules/knowledge_system/algorithm/.metadata new file mode 100644 index 000000000..6c08927eb --- /dev/null +++ b/modules/knowledge_system/algorithm/.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: e3c29ec00c9c825c891d75054c63fcc46454dca1 + channel: stable + +project_type: package diff --git a/modules/knowledge_system/algorithm/CHANGELOG.md b/modules/knowledge_system/algorithm/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/knowledge_system/algorithm/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/knowledge_system/algorithm/LICENSE b/modules/knowledge_system/algorithm/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/knowledge_system/algorithm/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/knowledge_system/algorithm/README.md b/modules/knowledge_system/algorithm/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/knowledge_system/algorithm/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/algorithm/analysis_options.yaml b/modules/knowledge_system/algorithm/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/knowledge_system/algorithm/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/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/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions.dart new file mode 100644 index 000000000..70cceba6f --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions.dart @@ -0,0 +1,46 @@ +import 'functions/bubble.dart'; +import 'functions/cocktail.dart'; +import 'functions/comb.dart'; +import 'functions/cycle.dart'; +import 'functions/gnome.dart'; +import 'functions/heap.dart'; +import 'functions/insertion.dart'; +import 'functions/merge.dart'; +import 'functions/pigeonHole.dart'; +import 'functions/quick.dart'; +import 'functions/selection.dart'; +import 'functions/shell.dart'; + +typedef SortFunction = Future Function(List src, SortCallback callback); +typedef SortCallback = Future Function(List dist); + +Map sortFunctionMap = { + 'insertion': insertionSort, + 'bubble': bubbleSort, + 'cocktail': cocktailSort, + 'comb': combSort, + 'pigeonHole': pigeonHoleSort, + 'shell': shellSort, + 'selection': selectionSort, + 'gnome': gnomeSort, + 'cycle': cycleSort, + 'heap': heapSort, + 'quick': quickSort, + 'merge': mergeSort, +}; + +Map sortNameMap = { + 'insertion': '插入排序', + 'bubble': '冒泡排序', + 'cocktail': '鸡尾酒排序(双向冒泡排序)', + 'comb': '梳排序', + 'pigeonHole': '鸽巢排序', + 'shell': '希尔排序', + 'selection': '选择排序', + 'gnome': '侏儒排序', + 'cycle': '循环排序', + 'heap': '堆排序', + 'quick': '快速排序', + 'merge': '归并排序', +}; + diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/bubble.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/bubble.dart new file mode 100644 index 000000000..3cf3c9967 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/bubble.dart @@ -0,0 +1,19 @@ +import '../functions.dart'; + +///冒泡排序 +Future bubbleSort(List src, SortCallback callback ) async{ + //控制需要进行排序的次数。每一轮循环都会确定一个数字的最终位置。 + for (int i = 0; i < src.length; ++i) { + //遍历当前未排序的元素,通过相邻的元素比较并交换位置来完成排序。 + for (int j = 0; j < src.length - i - 1; ++j) { + //如果 _numbers[j] 大于 _numbers[j + 1],则交换它们的位置,确保较大的元素移到右边。 + if (src[j] > src[j + 1]) { + int temp = src[j]; + src[j] = src[j + 1]; + src[j + 1] = temp; + } + //实现一个延迟,以便在ui上展示排序的动画效果 + await callback(src); + } + } +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/cocktail.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/cocktail.dart new file mode 100644 index 000000000..8c2d18cd1 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/cocktail.dart @@ -0,0 +1,52 @@ +import '../functions.dart'; + +///鸡尾酒排序(双向冒泡排序) +Future cocktailSort(List src, SortCallback callback ) async { + bool swapped = true; // 表示是否进行了交换 + int start = 0; // 当前未排序部分的起始位置 + int end = src.length; // 当前未排序部分的结束位置 + + // 开始排序循环,只有当没有进行交换时才会退出循环 + while (swapped == true) { + swapped = false; + + // 从左往右遍历需要排序的部分 + for (int i = start; i < end - 1; ++i) { + // 对每两个相邻元素进行比较 + if (src[i] > src[i + 1]) { + // 如果前面的元素大于后面的元素,则交换它们的位置 + int temp = src[i]; + src[i] = src[i + 1]; + src[i + 1] = temp; + swapped = true; // 进行了交换 + } + + // 实现动画效果,延迟一段时间后更新数组状态 + await callback(src); + } + + // 如果没有进行交换,则说明已经排好序,退出循环 + if (swapped == false) break; + // 重设为false,准备进行下一轮排序 + swapped = false; + // 将end设置为上一轮排序的最后一个元素的位置 + end = end - 1; + + // 从右往左遍历需要排序的部分 + for (int i = end - 1; i >= start; i--) { + // 对每两个相邻元素进行比较 + if (src[i] > src[i + 1]) { + // 如果前面的元素大于后面的元素,则交换它们的位置 + int temp = src[i]; + src[i] = src[i + 1]; + src[i + 1] = temp; + swapped = true; // 进行了交换 + } + + // 实现动画效果,延迟一段时间后更新数组状态 + await callback(src); + } + // 将start向右移一位,准备下一轮排序 + start = start + 1; + } +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/comb.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/comb.dart new file mode 100644 index 000000000..821f4a972 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/comb.dart @@ -0,0 +1,34 @@ +import '../functions.dart'; + +///梳排序(Comb Sort) +Future combSort(List src, SortCallback callback) async{ + int gap = src.length; + + bool swapped = true; + + // 当间隔不为1或存在交换时执行循环 + while (gap != 1 || swapped == true) { + // 通过缩小间隔来逐步将元素归位 + gap = getNextGap(gap); + swapped = false; + for (int i = 0; i < src.length - gap; i++) { + // 如果当前元素大于间隔位置上的元素,则交换它们的位置 + if (src[i] > src[i + gap]) { + int temp = src[i]; + src[i] = src[i + gap]; + src[i + gap] = temp; + swapped = true; + } + + // 实现一个延迟,以便在 UI 上展示排序的动画效果。 + await callback(src); + } + } +} + +int getNextGap(int gap) { + // 根据当前间隔值计算下一个间隔值 + gap = (gap * 10) ~/ 13; + if (gap < 1) return 1; + return gap; +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/cycle.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/cycle.dart new file mode 100644 index 000000000..4bef6eb95 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/cycle.dart @@ -0,0 +1,54 @@ +import '../functions.dart'; + +///循环排序 +Future cycleSort(List src, SortCallback callback) async { + int writes = 0; + for (int cycleStart = 0; cycleStart <= src.length - 2; cycleStart++) { + int item = src[cycleStart]; + int pos = cycleStart; + + // 在未排序部分中寻找比当前元素小的元素个数 + for (int i = cycleStart + 1; i < src.length; i++) { + if (src[i] < item) pos++; + } + + // 如果当前元素已经在正确位置上,则跳过此次迭代 + if (pos == cycleStart) { + continue; + } + + // 将当前元素放置到正确的位置上,并记录写操作次数 + while (item == src[pos]) { + pos += 1; + } + if (pos != cycleStart) { + int temp = item; + item = src[pos]; + src[pos] = temp; + writes++; + } + + // 循环将位于当前位置的元素放置到正确的位置上 + while (pos != cycleStart) { + pos = cycleStart; + // 继续在未排序部分中寻找比当前元素小的元素个数 + for (int i = cycleStart + 1; i < src.length; i++) { + if (src[i] < item) pos += 1; + } + + // 将当前元素放置到正确的位置上,并记录写操作次数 + while (item == src[pos]) { + pos += 1; + } + if (item != src[pos]) { + int temp = item; + item = src[pos]; + src[pos] = temp; + writes++; + } + + // 添加延迟操作以展示排序过程 + await callback(src); + } + } +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/gnome.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/gnome.dart new file mode 100644 index 000000000..5e08fc33a --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/gnome.dart @@ -0,0 +1,22 @@ +import '../functions.dart'; + +///地精排序 (侏儒排序) +Future gnomeSort(List src, SortCallback callback) async { + int index = 0; + while (index < src.length) { + // 当 index 小于数组长度时执行循环 + if (index == 0) index++; + if (src[index] >= src[index - 1]) { + // 如果当前元素大于等于前面的元素,则将 index 加1 + index++; + } else { + // 否则,交换这两个元素,并将 index 减1(使得元素可以沉到正确位置) + int temp = src[index]; + src[index] = src[index - 1]; + src[index - 1] = temp; + index--; + } + await callback(src); + } + return; +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/heap.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/heap.dart new file mode 100644 index 000000000..9f5410b27 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/heap.dart @@ -0,0 +1,38 @@ +import '../functions.dart'; + +///堆排序 +Future heapSort(List src, SortCallback callback) async { + // 从最后一个非叶子节点开始,构建最大堆 + for (int i = src.length ~/ 2; i >= 0; i--) { + await heapify(src,callback, src.length, i); + } + + // 依次取出最大堆的根节点(最大值),并进行堆化 + for (int i = src.length - 1; i >= 0; i--) { + int temp = src[0]; + src[0] = src[i]; + src[i] = temp; + await heapify(src, callback,i, 0); + } +} + +Future heapify(List src, SortCallback callback, int n, int i) async{ + int largest = i; + int l = 2 * i + 1; // 左子节点索引 + int r = 2 * i + 2; // 右子节点索引 + + // 如果左子节点存在并且大于父节点,则更新最大值索引 + if (l < n && src[l] > src[largest]) largest = l; + + // 如果右子节点存在并且大于父节点或左子节点,则更新最大值索引 + if (r < n && src[r] > src[largest]) largest = r; + + // 如果最大值索引不等于当前节点索引,则交换节点值,并递归进行堆化 + if (largest != i) { + int temp = src[i]; + src[i] = src[largest]; + src[largest] = temp; + heapify(src,callback, n, largest); + } + await callback(src); +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/insertion.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/insertion.dart new file mode 100644 index 000000000..b1c781449 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/insertion.dart @@ -0,0 +1,19 @@ +import '../functions.dart'; + +///插入排序 +Future insertionSort(List src, SortCallback callback) async { + for (int i = 1; i < src.length; i++) { + int temp = src[i]; // 将当前元素存储到临时变量 temp 中 + int j = i - 1; // j 表示已排序部分的最后一个元素的索引 + + // 在已排序部分从后往前查找,找到合适位置插入当前元素 + while (j >= 0 && temp < src[j]) { + src[j + 1] = src[j]; // 当前元素比已排序部分的元素小,将元素后移一位 + --j; // 向前遍历 + // 更新排序结果回调 + await callback(src); + } + src[j + 1] = temp; // 插入当前元素到已排序部分的正确位置 + await callback(src); + } +} diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/merge.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/merge.dart new file mode 100644 index 000000000..12135b462 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/merge.dart @@ -0,0 +1,79 @@ +import '../functions.dart'; + +//快速排序 +Future mergeSort(List src, SortCallback callback) async { + await _mergeSort(src,callback,0,src.length-1); +} + +///归并排序 +Future _mergeSort(List src, SortCallback callback,int leftIndex, int rightIndex) async { + // 定义一个名为 merge 的异步函数,用于合并两个有序子数组 + Future merge(int leftIndex, int middleIndex, int rightIndex) async { + // 计算左侧子数组和右侧子数组的大小 + int leftSize = middleIndex - leftIndex + 1; + int rightSize = rightIndex - middleIndex; + + // 创建左侧子数组和右侧子数组 + List leftList = List.generate(leftSize, (index) => 0); + List rightList = List.generate(rightSize, (index) => 0); + + // 将原始数组中的元素分别复制到左侧子数组和右侧子数组中 + for (int i = 0; i < leftSize; i++) { + leftList[i] = src[leftIndex + i]; + } + for (int j = 0; j < rightSize; j++) { + rightList[j] = src[middleIndex + j + 1]; + } + + // 初始化游标和索引 + int i = 0, j = 0; + int k = leftIndex; + + // 比较左侧子数组和右侧子数组的元素,并按顺序将较小的元素放入原始数组中 + while (i < leftSize && j < rightSize) { + if (leftList[i] <= rightList[j]) { + src[k] = leftList[i]; + i++; + } else { + src[k] = rightList[j]; + j++; + } + + await callback(src); + + k++; + } + + // 将左侧子数组或右侧子数组中剩余的元素放入原始数组中 + while (i < leftSize) { + src[k] = leftList[i]; + i++; + k++; + + await callback(src); + } + + while (j < rightSize) { + src[k] = rightList[j]; + j++; + k++; + + await callback(src); + } + } + + // 如果左索引小于右索引,则递归地对数组进行归并排序 + if (leftIndex < rightIndex) { + // 计算中间索引位置 + int middleIndex = (rightIndex + leftIndex) ~/ 2; + + // 分别对左侧子数组和右侧子数组进行归并排序 + await _mergeSort(src,callback,leftIndex, middleIndex); + await _mergeSort(src,callback,middleIndex + 1, rightIndex); + + await callback(src); + + // 合并两个有序子数组 + await merge(leftIndex, middleIndex, rightIndex); + } +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/oddEven.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/oddEven.dart new file mode 100644 index 000000000..bd6f548a0 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/oddEven.dart @@ -0,0 +1,37 @@ +import '../functions.dart'; + +///奇偶排序(Odd-Even Sort) +Future oddEvenSort(List src, SortCallback callback) async { + bool isSorted = false; + + while (!isSorted) { + // 当 isSorted 为 false 时执行循环 + isSorted = true; // 先假设数组已经排好序 + + for (int i = 1; i <= src.length - 2; i = i + 2) { + // 对奇数索引位置进行比较 + if (src[i] > src[i + 1]) { + // 如果当前元素大于后面的元素,则交换它们的值 + int temp = src[i]; + src[i] = src[i + 1]; + src[i + 1] = temp; + isSorted = false; // 若发生了交换,则说明数组仍未完全排序,将 isSorted 设为 false + await callback(src); + } + } + + for (int i = 0; i <= src.length - 2; i = i + 2) { + // 对偶数索引位置进行比较 + if (src[i] > src[i + 1]) { + // 如果当前元素大于后面的元素,则交换它们的值 + int temp = src[i]; + src[i] = src[i + 1]; + src[i + 1] = temp; + isSorted = false; + await callback(src); + } + } + } + + return; +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/pigeonHole.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/pigeonHole.dart new file mode 100644 index 000000000..83bbce108 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/pigeonHole.dart @@ -0,0 +1,33 @@ +import '../functions.dart'; + +///鸽巢排序 +Future pigeonHoleSort(List src, SortCallback callback ) async{ + int min = src[0]; + int max = src[0]; + int range, i, j, index; + + // 找到数组中的最大值和最小值 + for (int a = 0; a < src.length; a++) { + if (src[a] > max) max = src[a]; + if (src[a] < min) min = src[a]; + } + + // 计算鸽巢的个数 + range = max - min + 1; + List p = List.generate(range, (i) => 0); + + // 将数字分配到各个鸽巢中 + for (i = 0; i < src.length; i++) { + p[src[i] - min]++; + } + + index = 0; + + // 将鸽巢中的数字取出,重新放回到数组中 + for (j = 0; j < range; j++) { + while (p[j]-- > 0) { + src[index++] = j + min; + await callback(src); + } + } +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/quick.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/quick.dart new file mode 100644 index 000000000..25b9685f6 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/quick.dart @@ -0,0 +1,65 @@ +import '../functions.dart'; + +//快速排序 +Future quickSort(List src, SortCallback callback) async { + await _quickSort(src,callback,0,src.length-1); +} + +///快速排序 +Future _quickSort(List src, SortCallback callback,int leftIndex,int rightIndex) async { + // 定义一个名为 _partition 的异步函数,用于划分数组,并返回划分后的基准元素的索引位置 + Future _partition(int left, int right) async { + // 选择中间位置的元素作为基准元素 + int p = (left + (right - left) / 2).toInt(); + + // 交换基准元素和最右边的元素 + var temp = src[p]; + src[p] = src[right]; + src[right] = temp; + await callback(src); + + // 初始化游标 cursor + int cursor = left; + + // 遍历数组并根据基准元素将元素交换到左侧或右侧 + for (int i = left; i < right; i++) { + if (cf(src[i], src[right]) <= 0) { + // 如果当前元素小于等于基准元素,则交换它和游标位置的元素 + var temp = src[i]; + src[i] = src[cursor]; + src[cursor] = temp; + cursor++; + await callback(src); + } + } + + // 将基准元素放置在游标位置 + temp = src[right]; + src[right] = src[cursor]; + src[cursor] = temp; + + await callback(src); + + return cursor; // 返回基准元素的索引位置 + } + + // 如果左索引小于右索引,则递归地对数组进行快速排序 + if (leftIndex < rightIndex) { + int p = await _partition(leftIndex, rightIndex); + + await _quickSort(src,callback,leftIndex, p - 1); // 对基准元素左侧的子数组进行快速排序 + + await _quickSort(src,callback, p + 1, rightIndex); // 对基准元素右侧的子数组进行快速排序 + } +} + +// 比较函数,用于判断两个元素的大小关系 +cf(int a, int b) { + if (a < b) { + return -1; // 若 a 小于 b,则返回 -1 + } else if (a > b) { + return 1; // 若 a 大于 b,则返回 1 + } else { + return 0; // 若 a 等于 b,则返回 0 + } +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/selection.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/selection.dart new file mode 100644 index 000000000..185dae216 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/selection.dart @@ -0,0 +1,18 @@ +import '../functions.dart'; + +///选择排序 +Future selectionSort(List src, SortCallback callback ) async { + for (int i = 0; i < src.length; i++) { + for (int j = i + 1; j < src.length; j++) { + // 遍历未排序部分,内层循环控制变量 j + if (src[i] > src[j]) { + // 判断当前元素是否比后续元素小 + int temp = src[j]; + // 交换当前元素和后续较小的元素 + src[j] = src[i]; + src[i] = temp; + } + await callback(src); + } + } +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/shell.dart b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/shell.dart new file mode 100644 index 000000000..232f8724d --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/algorithm/sort/functions/shell.dart @@ -0,0 +1,21 @@ +import '../functions.dart'; + +///希尔排序 +Future shellSort(List src, SortCallback callback) async{ + //定义变量 gap 并初始化为数组长度的一半。每次循环完成后将 gap 减半直到等于 0。 + for (int gap = src.length ~/ 2; gap > 0; gap ~/= 2) { + //遍历每个子序列并进行插入排序。初始时从第一个子序列的第二个元素开始,即 i = gap,以 gap 为步长逐个遍历每个子序列。 + for (int i = gap; i < src.length; i += 1) { + //将当前遍历到的元素赋值给它 + int temp = src[i]; + //内部使用一个 for 循环来实现插入排序。 + //循环开始时定义变量 j 并将其初始化为当前遍历到的元素的下标。通过不断比较前后相隔 gap 的元素大小并交换位置,将当前元素插入到正确的位置。 + int j; + for (j = i; j >= gap && src[j - gap] > temp; j -= gap) { + src[j] = src[j - gap]; + } + src[j] = temp; + await callback(src); + } + } +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/data_scope/sort_config.dart b/modules/knowledge_system/algorithm/lib/src/data_scope/sort_config.dart new file mode 100644 index 000000000..4cf0e8f09 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/data_scope/sort_config.dart @@ -0,0 +1,28 @@ + +class SortConfig { + final int count; + final int seed; + final Duration duration; + final String name; + + SortConfig({ + this.count = 100, + this.duration = const Duration(microseconds: 1500), + this.seed = -1, + this.name = 'insertion', + }); + + SortConfig copyWith({ + int? count, + int? seed, + Duration? duration, + String? name, + }) => + SortConfig( + count:count??this.count, + seed:seed??this.seed, + duration:duration??this.duration, + name:name??this.name, + ); +} + 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/modules/knowledge_system/algorithm/lib/src/sort/data_painter.dart b/modules/knowledge_system/algorithm/lib/src/sort/data_painter.dart new file mode 100644 index 000000000..e719d6bc1 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/sort/data_painter.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; + +class DataPainter extends CustomPainter { + final List data; + + DataPainter({required this.data}); + + @override + void paint(Canvas canvas, Size size) { + canvas.clipRect(Offset.zero & size); + double itemWidth = size.width / data.length; + + Paint paint = Paint(); + paint.strokeWidth = itemWidth; + paint.strokeCap = StrokeCap.round; + + for (int i = 0; i < data.length; i++) { + int value = data[i]; + if (value < 1000 * .10) { + paint.color = Colors.blue.shade100; + } else if (value < 1000 * .20) { + paint.color = Colors.blue.shade200; + } else if (value < 1000 * .30) { + paint.color = Colors.blue.shade300; + } else if (value < 1000 * .40) { + paint.color = Colors.blue.shade400; + } else if (value < 1000 * .50) { + paint.color = Colors.blue.shade500; + } else if (value < 1000 * .60) { + paint.color = Colors.blue.shade600; + } else if (value < 1000 * .70) { + paint.color = Colors.blue.shade700; + } else if (value < 1000 * .80) { + paint.color = Colors.blue.shade800; + } else if (value < 1000 * .90) { + paint.color = Colors.blue.shade900; + } else { + paint.color = const Color(0xFF011E51); + } + canvas.drawLine( + Offset(i * itemWidth + itemWidth / 2, 0), + Offset( + i * itemWidth + itemWidth / 2, + size.height * (value / 1000), + ), + paint); + } + } + + @override + bool shouldRepaint(covariant DataPainter oldDelegate) { + return true; + } +} diff --git a/modules/knowledge_system/algorithm/lib/src/sort/sort_page.dart b/modules/knowledge_system/algorithm/lib/src/sort/sort_page.dart new file mode 100644 index 000000000..99a65dfbd --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/sort/sort_page.dart @@ -0,0 +1,24 @@ +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}); + + @override + Widget build(BuildContext context) { + return const Scaffold( + endDrawer: Drawer( + child: SortSettings(), + ), + body: Column( + children: [ + DeskSortBar(), + Expanded(child: SortPaper()), + ], + ), + ); + } +} \ No newline at end of file diff --git a/modules/knowledge_system/algorithm/lib/src/sort/sort_parper.dart b/modules/knowledge_system/algorithm/lib/src/sort/sort_parper.dart new file mode 100644 index 000000000..f1cd8cac5 --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/sort/sort_parper.dart @@ -0,0 +1,23 @@ + +import 'package:flutter/material.dart'; + +import '../../algorithm.dart'; +import 'data_painter.dart'; + +class SortPaper extends StatelessWidget{ + const SortPaper({super.key}); + + + @override + Widget build(BuildContext context) { + SortState state = SortStateScope.of(context); + List numbers = state.data; + + return CustomPaint( + painter: DataPainter(data: numbers), + child: ConstrainedBox(constraints: BoxConstraints.expand()), + ); + } +} + + diff --git a/modules/knowledge_system/algorithm/lib/src/sort/sort_setting.dart b/modules/knowledge_system/algorithm/lib/src/sort/sort_setting.dart new file mode 100644 index 000000000..44a27351e --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/sort/sort_setting.dart @@ -0,0 +1,121 @@ +import 'package:algorithm/algorithm.dart'; +import 'package:flutter/material.dart'; + + +class SortSettings extends StatefulWidget { + + const SortSettings({super.key,}); + + @override + State createState() => _SortSettingsState(); +} + +class _SortSettingsState extends State { + late TextEditingController _count = + TextEditingController(); + late TextEditingController _duration = TextEditingController(); + late TextEditingController _seed = + TextEditingController(); + + @override + void initState() { + super.initState(); + } + + @override + void didChangeDependencies() { + super.didChangeDependencies(); + SortState state = SortStateScope.of(context); + _count.text = state.config.count.toString(); + _duration.text = state.config.duration.inMicroseconds.toString(); + _seed.text = state.config.seed.toString(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + backgroundColor: Colors.white, + appBar: AppBar( + backgroundColor: Colors.white, + leading: BackButton(), + actions: [ + IconButton(onPressed: (){ + SortState state = SortStateScope.of(context); + state.config =state.config.copyWith( + count: int.parse(_count.text), + duration: Duration( + microseconds: int.parse(_duration.text), + ), + seed: int.parse(_seed.text) + ); + Navigator.of(context).pop(); + }, icon: Icon(Icons.check))], + iconTheme: IconThemeData(color: Colors.black), + titleTextStyle: TextStyle( + color: Colors.black, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + centerTitle: true, + title: Text('排序算法配置'), + ), + body: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Row( + children: [ + Text('数据数量(个数):'), + const SizedBox( + width: 20, + ), + Expanded( + child: TextField( + controller: _count, + )), + ], + ), + Row( + children: [ + Text('时间间隔(微秒):'), + const SizedBox( + width: 20, + ), + Expanded( + child: TextField( + controller: _duration, + )), + ], + ), + Row( + children: [ + Text('随机种子:'), + const SizedBox( + width: 20, + ), + Expanded( + child: TextField( + controller: _seed, + )), + ], + ), + Spacer(), + // ElevatedButton( + // onPressed: () { + // SortState state = SortStateScope.of(context); + // state.config =state.config.copyWith( + // count: int.parse(_count.text), + // duration: Duration( + // microseconds: int.parse(_duration.text), + // ), + // seed: int.parse(_seed.text) + // ); + // Navigator.of(context).pop(); + // }, + // child: Text('确定设置')) + ], + ), + ), + ); + } +} 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/modules/knowledge_system/algorithm/lib/src/sort/top_bar/sort_button.dart b/modules/knowledge_system/algorithm/lib/src/sort/top_bar/sort_button.dart new file mode 100644 index 000000000..b7103001c --- /dev/null +++ b/modules/knowledge_system/algorithm/lib/src/sort/top_bar/sort_button.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; +import 'package:tolyui/tolyui.dart'; + +import '../../../algorithm.dart'; + +class SortButton extends StatelessWidget { + const SortButton({super.key}); + + @override + Widget build(BuildContext context) { + 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 TolyAction( + onTap: action, + 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/modules/knowledge_system/algorithm/test/utils_test.dart b/modules/knowledge_system/algorithm/test/utils_test.dart new file mode 100644 index 000000000..ba04b36ea --- /dev/null +++ b/modules/knowledge_system/algorithm/test/utils_test.dart @@ -0,0 +1,5 @@ +import 'package:flutter_test/flutter_test.dart'; + +void main() { + +} diff --git a/modules/knowledge_system/artifact/.gitignore b/modules/knowledge_system/artifact/.gitignore new file mode 100644 index 000000000..96486fd93 --- /dev/null +++ b/modules/knowledge_system/artifact/.gitignore @@ -0,0 +1,30 @@ +# 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/modules/knowledge_system/artifact/.metadata b/modules/knowledge_system/artifact/.metadata new file mode 100644 index 000000000..6c08927eb --- /dev/null +++ b/modules/knowledge_system/artifact/.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: e3c29ec00c9c825c891d75054c63fcc46454dca1 + channel: stable + +project_type: package diff --git a/modules/knowledge_system/artifact/CHANGELOG.md b/modules/knowledge_system/artifact/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/knowledge_system/artifact/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/knowledge_system/artifact/LICENSE b/modules/knowledge_system/artifact/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/knowledge_system/artifact/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/knowledge_system/artifact/README.md b/modules/knowledge_system/artifact/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/knowledge_system/artifact/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/artifact/analysis_options.yaml b/modules/knowledge_system/artifact/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/knowledge_system/artifact/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/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/modules/knowledge_system/artifact/lib/src/articles/bloc/exp.dart b/modules/knowledge_system/artifact/lib/src/articles/bloc/exp.dart new file mode 100644 index 000000000..2f17c65fc --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/bloc/exp.dart @@ -0,0 +1,2 @@ +export 'article/bloc.dart'; +export 'columnize/bloc.dart'; \ No newline at end of file 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/modules/knowledge_system/artifact/lib/src/articles/data/model/columnize.dart b/modules/knowledge_system/artifact/lib/src/articles/data/model/columnize.dart new file mode 100644 index 000000000..63c4debb7 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/data/model/columnize.dart @@ -0,0 +1,71 @@ + +class Columnize { + final String? username; + final String title; + final String subtitle; + final String? url; + final String? cover; + final int create; + final int update; + final int count; + final int userId; + final int id; + + Columnize({ + this.username = '', + required this.title, + this.subtitle = '', + required this.url, + this.cover = '', + this.update = 0, + this.create = 0, + this.count = 0, + this.id = -1, + this.userId = -1, + }); + + Map toJson() => { + "username": username, + "id": id, + "title": title, + "createAt": create, + "subtitle": subtitle, + "userId": userId, + "url": url, + "cover": cover, + "updateAt": update, + "count": count, + }; + + factory Columnize.fromMap(dynamic map) => Columnize( + username: map['userName'] ?? '', + title: map['title'] ?? '', + create: DateTime.parse(map['createAt']).millisecondsSinceEpoch, + update: DateTime.parse(map['updateAt']).millisecondsSinceEpoch, + subtitle: map['subtitle'] ?? '', + url: map['url'] ?? '', + cover: map['caver'] ?? '', + count: map['count'] ?? 0, + userId: map['userId'] ?? 0, + id: map['columnizeId'] ?? -1, + ); + + factory Columnize.fromDb(dynamic map)=> + Columnize( + id: map['id'] ?? '', + username: map['username'] ?? '', + userId: map['userId'] ?? '', + title: map['title'] ?? '', + create: map['createAt'] ?? 0 , + count: map['count'] ?? 0 , + update: map['updateAt'] ?? 0, + subtitle: map['subtitle'] ?? '', + url: map['url'] ?? '', + cover: map['cover'] ?? '', + ); + + @override + String toString() { + return 'Columnize{username: $username, title: $title, subtitle: $subtitle, url: $url, cover: $cover, create: $create, update: $update, count: $count}'; + } +} 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/modules/knowledge_system/artifact/lib/src/articles/view/article/article_detail_page.dart b/modules/knowledge_system/artifact/lib/src/articles/view/article/article_detail_page.dart new file mode 100644 index 000000000..367184573 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/view/article/article_detail_page.dart @@ -0,0 +1,80 @@ +import 'package:app/app.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import 'package:webview_flutter/webview_flutter.dart'; + +import '../../data/exp.dart'; + +class ArticleDetailPage extends StatefulWidget { + final Article article; + + const ArticleDetailPage({Key? key, required this.article}) : super(key: key); + + @override + State createState() => _ArticleDetailPageState(); +} + +class _ArticleDetailPageState extends State { + late WebViewController controller; + + int progress = 0; + + @override + void initState() { + print(widget.article.url); + super.initState(); + controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setBackgroundColor(const Color(0x00000000)) + ..loadRequest(Uri.parse('https://juejin.cn${widget.article.url}')) + ..setNavigationDelegate( + NavigationDelegate( + onProgress: (int progress) { + print(progress); + this.progress =progress; + setState(() { + + }); + }, + onPageStarted: (String url) { + + }, + onPageFinished: (String url) { + }, + )); + } + + _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'); + } + } + + @override + Widget build(BuildContext context) { + + + return Scaffold( + appBar: AppBar(title: Text(widget.article.title),actions: [ + IconButton(onPressed: (){ + _launchURL('https://juejin.cn${widget.article.url}'); + }, icon: Icon(TolyIcon.icon_artifact,size: 20,)) + ],), + body: Stack( + alignment: Alignment.center, + children: [ + WebViewWidget(controller: controller), + if(progress!=100) + Center( + child: CupertinoActivityIndicator(), + ) + ], + ), + ); + } +} diff --git a/modules/knowledge_system/artifact/lib/src/articles/view/article/column_detail_page.dart b/modules/knowledge_system/artifact/lib/src/articles/view/article/column_detail_page.dart new file mode 100644 index 000000000..cbbc67f9c --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/view/article/column_detail_page.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + + +import '../../data/exp.dart'; +import 'sliver_article.dart'; + +class ColumnDetailPage extends StatelessWidget { + final Columnize columnize; + + const ColumnDetailPage({Key? key, required this.columnize}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text(columnize.title), + ), + body: CustomScrollView( + slivers: [ + SliverPadding( + padding: EdgeInsets.only(top: 8), + sliver: SliverArticlePanel()) + // SliverAppBar( + // title: Text(columnize.title), + // ), + ], + ), + ); + } +} diff --git a/modules/knowledge_system/artifact/lib/src/articles/view/article/columnize_page_view.dart b/modules/knowledge_system/artifact/lib/src/articles/view/article/columnize_page_view.dart new file mode 100644 index 000000000..d3f1f4ff5 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/view/article/columnize_page_view.dart @@ -0,0 +1,286 @@ +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 '../../bloc/article/bloc.dart'; +import '../../bloc/exp.dart'; +import '../../data/exp.dart'; +import '../../data/repository/article_repository.dart'; + +import 'column_detail_page.dart'; + +DateFormat formatLong = DateFormat('yyyy-MM-dd HH:mm:ss'); + +const colors = [ + Color(0xFF0829FB), + Color(0xFFF60C0C), + Color(0xFFE7F716), + Color(0xFFF3B913), + Color(0xFF3DF30B), + Color(0xFFB709F4), + Color(0xFF0DF6EF), +]; + +class ColumnizePageView extends StatefulWidget { + const ColumnizePageView({Key? key}) : super(key: key); + + @override + _ColumnizePageViewState createState() => _ColumnizePageViewState(); +} + +class _ColumnizePageViewState 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.5 : 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 = context.watch().state.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; + }, + ); + 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 ColumnizeItem( + columnize: data[realIndex], + color: colors[realIndex % colors.length], + ); + } + + 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(6.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; + }); + } +} + +class ColumnizeItem extends StatelessWidget { + final Columnize columnize; + final Color color; + const ColumnizeItem({Key? key, required this.columnize, required this.color}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () { + ArticleRepository repository = context.read().repository; + Navigator.of(context).push(SlidePageRoute( + child: MultiBlocProvider(providers: [ + BlocProvider( + create: (_) => + ArticleBloc(repository, groupId: columnize.id, pageSize: 100) + ..init(), + ), + ], child: ColumnDetailPage(columnize: columnize)))); + }, + child: Container( + alignment: Alignment.topLeft, + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 10), + // margin: EdgeInsets.only(left: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + columnize.title, + style: + TextStyle(fontWeight: FontWeight.bold, fontSize: 15), + ), + Text( + '作者: ${columnize.username}', + style: TextStyle( + fontSize: 12, + color: Color( + 0xff6A6D76, + )), + ), + ], + ), + Spacer(), + Container( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6), + border: Border.all(color: Color(0xff3872E0))), + child: Text( + '${columnize.count} 篇', + style: TextStyle( + color: Color(0xff3872E0), fontSize: 12, height: 1.1), + ), + ) + ], + ), + const SizedBox( + height: 6, + ), + Text( + '专栏简介: ${columnize.subtitle}', + style: TextStyle( + color: Color( + 0xffA3A3A3, + ), + fontSize: 12), + maxLines: 3, + ), + Spacer(), + Row( + children: [ + Text( + '更新时间: ${formatLong.format(DateTime.fromMillisecondsSinceEpoch(columnize.update, isUtc: true))}', + style: TextStyle( + color: Color( + 0xff6A6D76, + ), + fontSize: 12), + ), + Spacer(), + ], + ), + ], + ), + decoration: BoxDecoration( + gradient: + LinearGradient(transform: GradientRotation(3 * pi / 4), colors: [ + color.withOpacity(0.1), + color.withOpacity(0.08), + color.withOpacity(0), + // Theme.of(context).primaryColor.withAlpha(88) + ]), // color: color.withOpacity(0.1), + borderRadius: BorderRadius.circular(8), + ), + ), + ); + } +} 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/modules/knowledge_system/artifact/lib/src/articles/view/article/toly_article_scroll_page.dart b/modules/knowledge_system/artifact/lib/src/articles/view/article/toly_article_scroll_page.dart new file mode 100644 index 000000000..81815e97a --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/view/article/toly_article_scroll_page.dart @@ -0,0 +1,88 @@ +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:tolyui_refresh/tolyui_refresh.dart'; + +import '../../bloc/exp.dart'; + +import 'sliver_article.dart'; +import 'sliver_columnize.dart'; + +class TolyArticleScrollPage extends StatefulWidget { + const TolyArticleScrollPage({Key? key}) : super(key: key); + + @override + State createState() => _TolyArticleScrollPageState(); +} + +class _TolyArticleScrollPageState extends State { + final RefreshController _refreshController = + RefreshController(initialRefresh: false); + + int maxCount = 60; + + @override + void dispose() { + _refreshController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RefreshConfigWrapper( + child: TolyRefresh( + enablePullUp: true, + onRefresh: _onRefresh, + onLoading: _loadMore, + controller: _refreshController, + child: CustomScrollView( + slivers: [ + // SliverOverlapInjector( + // handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), + // ), + SliverPadding( + padding: EdgeInsets.only(top: 10, bottom: 10), + sliver: const SliverToBoxAdapter( + child: ColumnizeViewPage(), + ), + ), + SliverArticlePanel(), + ], + ) + + // ListView.builder( + // padding: EdgeInsets.only(top: 10), + // itemCount: data.length, + // itemBuilder: _buildItem, + // ), + ), + ); + } + + void _onRefresh() async { + // monitor network fetch + await Future.delayed(Duration(milliseconds: 500)); + + _refreshController.refreshCompleted(); + } + + void _loadMore() async { + ArticleBloc bloc = context.read(); + await context.read().loadNextPageMore(); + // int length = data.length; + ArticleState state = bloc.state; + if (state is ArticleWithData) { + if (state.data.length >= state.total) { + _refreshController.loadNoData(); + await Future.delayed(Duration(milliseconds: 2000)); + _refreshController.resetNoData(); + return; + } + + _refreshController.loadComplete(); + } + } +} diff --git a/modules/knowledge_system/artifact/lib/src/articles/view/artifact_page.dart b/modules/knowledge_system/artifact/lib/src/articles/view/artifact_page.dart new file mode 100644 index 000000000..1e7c65204 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/articles/view/artifact_page.dart @@ -0,0 +1,286 @@ +import 'dart:math'; + +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 '../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; + +import 'building/building_panel.dart'; + +class ArtifactPage extends StatefulWidget { + const ArtifactPage({Key? key}) : super(key: key); + + @override + State createState() => _ArtifactPageState(); +} + +// 任意处下滑可出现 AppBar +// |--- NestedScrollView#floatHeaderSlivers: true +// |--- SliverAppBar#floating: true + +// 任意处下滑可出现 AppBar + 轻滑出现 +// |--- NestedScrollView#floatHeaderSlivers: true +// |--- SliverAppBar#floating: true +// |--- SliverAppBar#snap: true + +const List kArtifactInfo = [ + '分类收录张风捷特烈的博客文章', + '可视化排序算法', + '收录布局方案,提供界面样板', + 'Flutter 知识小要点,一网打尽', +]; + +class _ArtifactPageState extends State + with SingleTickerProviderStateMixin { + late TabController controller; + List data = []; + + @override + void initState() { + super.initState(); + controller = TabController(length: 2, vsync: this); + controller.addListener(_listen); + data = List.generate(5, (index) => 'Init $index'); + } + + int _curIndex = 0; + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + ArticleRepository aRepository = const ArticleRepository(); + ColumnizeRepository cRepository = const ColumnizeRepository(); + + @override + Widget build(BuildContext context) { + double bottom = MediaQuery.of(context).padding.bottom; + String name = SortStateScope.of(context).config.name; + + return MultiBlocProvider( + providers: [ + BlocProvider( + create: (_) => ColumnizeBloc(cRepository)..init()), + BlocProvider( + create: (_) => ArticleBloc(aRepository)..init()), + ], + child: Scaffold( + endDrawer: SortSettings(), + backgroundColor: const Color(0xffF2F3F5), + bottomNavigationBar: Container(height: bottom), + body: NestedScrollView( + headerSliverBuilder: _buildAppBar, + floatHeaderSlivers: true, + body: TabBarView( + controller: controller, + children: [ + TolyArticleScrollPage(), + 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(), + SortSelector(), + ], + ), + ), + Expanded(child: SortPaper()), + ], + ), + // BuildingPanel(), + // BuildingPanel(), + ], + ), + ), + ), + ); + } + + _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'); + } + } + + List _buildAppBar(BuildContext context, bool innerBoxIsScrolled) { + // print('innerBoxIsScrolled:$innerBoxIsScrolled'); + return [ + // SliverOverlapAbsorber( + // handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), + // + // sliver: + SliverAppBar( + floating: true, + snap: true, + pinned: true, + backgroundColor: Colors.white, + leading: _curIndex == 1 ? SortButton() : null, + + // flexibleSpace: Image.network( + // 'https://p6-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/448d4eb270f44edab0192a1281141954~tplv-k3u1fbpfcp-watermark.image?', + // fit: BoxFit.cover, + // ), + + // expandedHeight: 120, + + flexibleSpace: Container( + // height: 240, + decoration: BoxDecoration( + gradient: LinearGradient(colors: [ + Color(0xffD3D5F5), + Color(0xffC8EBFA), + ], transform: GradientRotation(pi / 4))), + ), + // flexibleSpace: Doodle(), + // leading: Center( + // child: Text( + // '宝具库', + // style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + // )), + // actions: [Icon(Icons.cached_outlined)], + titleSpacing: 0, + // leadingWidth: 40, + + title: Column( + children: [ + Text( + 'Flutter 知识宝库', + style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + const SizedBox( + height: 4, + ), + Text( + kArtifactInfo[_curIndex], + maxLines: 2, + style: TextStyle( + fontSize: 10, + fontWeight: FontWeight.normal, + color: Colors.purple), + ), + ], + ), + actions: [ + if (_curIndex == 1) + IconButton( + onPressed: () { + Scaffold.of(context).openEndDrawer(); + }, + icon: Icon(Icons.settings)), + if (_curIndex != 1) + IconButton(onPressed: () {}, icon: Icon(Icons.search_rounded)) + ], + // title: Padding( + // padding: const EdgeInsets.only(right: 8.0), + // child: CupertinoTextField(), + // ), + bottom: TabBar( + controller: controller, + tabs: [ + Tab( + // icon: Icon(Icons.account_balance_wallet_outlined), + text: context.l10n.knowledgeTabToly, + ), + Tab( + // icon: Icon(Icons.account_balance_wallet_outlined), + 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, + // ), + ], + ), + ), // ) + ]; + } + + Widget? _buildItem(BuildContext context, int index) { + return ListTile( + tileColor: Colors.white, + title: Text(data[index]), + ); + } + + void _listen() { + print('${controller.index}'); + if (_curIndex != controller.index) { + setState(() { + _curIndex = controller.index; + }); + } + } +} + +class Doodle extends StatelessWidget { + const Doodle({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return RepaintBoundary( + child: CustomPaint( + painter: DoodlePainter(), + child: const Center(), + ), + ); + } +} + +class DoodlePainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + Paint pen = Paint(); + pen.shader = ui.Gradient.linear( + Offset.zero, + Offset(size.width, size.height), + [const Color(0xffD3D5F5), const Color(0xffC8EBFA)], + [0, 1], + TileMode.mirror, + Matrix4.rotationZ(pi / 4).storage); + canvas.drawRect(Offset.zero & size, pen); + // print(size); + canvas.drawCircle( + Offset(size.width / 2, size.height / 2), + size.height / 6, + Paint() + ..color = Colors.grey + ..style = PaintingStyle.stroke); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +} 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/modules/knowledge_system/artifact/lib/src/points/data/api/app_info.dart b/modules/knowledge_system/artifact/lib/src/points/data/api/app_info.dart new file mode 100644 index 000000000..73201835f --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/data/api/app_info.dart @@ -0,0 +1,55 @@ +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 = await HttpUtil.instance + .client + .get(PathUnit.appInfo+"/$appName") + .catchError((err) { + errorMsg = err.toString(); + }); + + // 获取的数据非空且 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}'; + } +} \ No newline at end of file diff --git a/modules/knowledge_system/artifact/lib/src/points/data/api/category_api.dart b/modules/knowledge_system/artifact/lib/src/points/data/api/category_api.dart new file mode 100644 index 000000000..df6be8fb0 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/data/api/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/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/modules/knowledge_system/artifact/lib/src/points/data/model/github_model.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/github_model.dart new file mode 100644 index 000000000..0d4bd5eec --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/data/model/github_model.dart @@ -0,0 +1,6 @@ +export './github_user.dart'; +export './issue.dart'; +export './issue_comment.dart'; +export './license.dart'; +export './repository.dart'; +export './repository_permissions.dart'; diff --git a/modules/knowledge_system/artifact/lib/src/points/data/model/github_user.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/github_user.dart new file mode 100644 index 000000000..56b7739cc --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/data/model/github_user.dart @@ -0,0 +1,172 @@ +/// create by 张风捷特烈 on 2020/6/17 +/// contact me by email 1981462002@qq.com +/// 说明: + +class GithubUser { + GithubUser( + this.login, + this.id, + this.nodeId, + this.avatarUrl, + this.gravatarId, + this.url, + this.htmlUrl, + this.followersUrl, + this.followingUrl, + this.gistsUrl, + this.starredUrl, + this.subscriptionsUrl, + this.organizationsUrl, + this.reposUrl, + this.eventsUrl, + this.receivedEventsUrl, + this.type, + this.siteAdmin, + this.name, + this.company, + this.blog, + this.location, + this.email, + this.starred, + this.bio, + this.publicRepos, + this.publicGists, + this.followers, + this.following, + this.createdAt, + this.updatedAt, + this.privateGists, + this.totalPrivateRepos, + this.ownedPrivateRepos, + this.diskUsage, + this.collaborators, + this.twoFactorAuthentication); + + String? login; + int? id; + String? nodeId; + String? avatarUrl; + String? gravatarId; + String? url; + String? htmlUrl; + String? followersUrl; + String? followingUrl; + String? gistsUrl; + String? starredUrl; + String? subscriptionsUrl; + String? organizationsUrl; + String? reposUrl; + String? eventsUrl; + String? receivedEventsUrl; + String? type; + bool? siteAdmin; + String? name; + String? company; + String? blog; + String? location; + String? email; + String? starred; + String? bio; + int? publicRepos; + int? publicGists; + int? followers; + int? following; + DateTime? createdAt; + DateTime? updatedAt; + int? privateGists; + int? totalPrivateRepos; + int? ownedPrivateRepos; + int? diskUsage; + int? collaborators; + bool? twoFactorAuthentication; + + + factory GithubUser.fromJson(Map json) => GithubUser( + json['login'] as String?, + json['id'] as int?, + json['node_id'] as String?, + json['avatar_url'] as String?, + json['gravatar_id'] as String?, + json['url'] as String?, + json['html_url'] as String?, + json['followers_url'] as String?, + json['following_url'] as String?, + json['gists_url'] as String?, + json['starred_url'] as String?, + json['subscriptions_url'] as String?, + json['organizations_url'] as String?, + json['repos_url'] as String?, + json['events_url'] as String?, + json['received_events_url'] as String?, + json['type'] as String?, + json['site_admin'] as bool?, + json['name'] as String?, + json['company'] as String?, + json['blog'] as String?, + json['location'] as String?, + json['email'] as String?, + json['starred'] as String?, + json['bio'] as String?, + json['public_repos'] as int?, + json['public_gists'] as int?, + json['followers'] as int?, + json['following'] as int?, + json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + json['private_gists'] as int?, + json['total_private_repos'] as int?, + json['owned_private_repos'] as int?, + json['disk_usage'] as int?, + json['collaborators'] as int?, + json['two_factor_authentication'] as bool?, + ); + + + Map toJson() => { + 'login': login, + 'id': id, + 'node_id': nodeId, + 'avatar_url': avatarUrl, + 'gravatar_id': gravatarId, + 'url': url, + 'html_url': htmlUrl, + 'followers_url': followersUrl, + 'following_url': followingUrl, + 'gists_url': gistsUrl, + 'starred_url': starredUrl, + 'subscriptions_url': subscriptionsUrl, + 'organizations_url': organizationsUrl, + 'repos_url': reposUrl, + 'events_url': eventsUrl, + 'received_events_url': receivedEventsUrl, + 'type': type, + 'site_admin': siteAdmin, + 'name': name, + 'company': company, + 'blog': blog, + 'location': location, + 'email': email, + 'starred': starred, + 'bio': bio, + 'public_repos': publicRepos, + 'public_gists': publicGists, + 'followers': followers, + 'following': following, + 'created_at': createdAt?.toIso8601String(), + 'updated_at': updatedAt?.toIso8601String(), + 'private_gists': privateGists, + 'total_private_repos': totalPrivateRepos, + 'owned_private_repos': ownedPrivateRepos, + 'disk_usage': diskUsage, + 'collaborators': collaborators, + 'two_factor_authentication': twoFactorAuthentication, + }; + + // 命名构造函数 + GithubUser.empty(); + +} diff --git a/modules/knowledge_system/artifact/lib/src/points/data/model/issue.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/issue.dart new file mode 100644 index 000000000..b999aabe3 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/data/model/issue.dart @@ -0,0 +1,117 @@ +import 'package:equatable/equatable.dart'; +import 'github_user.dart'; + +class Issue extends Equatable { + final int? id; + final int? number; + final String? title; + final String? state; + final bool? locked; + + final int? commentNum; + + final DateTime? createdAt; + + final DateTime? updatedAt; + + final DateTime? closedAt; + final String? body; + + final String? bodyHtml; + + final GithubUser? user; + + final String? repoUrl; + + final String? htmlUrl; + + final GithubUser? closeBy; + + const Issue( + this.id, + this.number, + this.title, + this.state, + this.locked, + this.commentNum, + this.createdAt, + this.updatedAt, + this.closedAt, + this.body, + this.bodyHtml, + this.user, + this.repoUrl, + this.htmlUrl, + this.closeBy, + ); + + static Issue fromJson(dynamic json) => Issue( + json['id'] as int?, + json['number'] as int?, + json['title'] as String?, + json['state'] as String?, + json['locked'] as bool?, + json['comments'] as int?, + json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + json['closed_at'] == null + ? null + : DateTime.parse(json['closed_at'] as String), + json['body'] as String?, + json['body_html'] as String?, + json['user'] == null + ? null + : GithubUser.fromJson(json['user'] as Map), + json['repository_url'] as String?, + json['html_url'] as String?, + json['closed_by'] == null + ? null + : GithubUser.fromJson(json['closed_by'] as Map), + ); + + Map toJson() => { + 'id': id, + 'number': number, + 'title': title, + 'state': state, + 'locked': locked, + 'comments': commentNum, + 'created_at': createdAt?.toIso8601String(), + 'updated_at': updatedAt?.toIso8601String(), + 'closed_at': closedAt?.toIso8601String(), + 'body': body, + 'body_html': bodyHtml, + 'user': user, + 'repository_url': repoUrl, + 'html_url': htmlUrl, + 'closed_by': closeBy, + }; + + @override + String toString() { + return 'Issue{id: $id, number: $number, title: $title, state: $state, locked: $locked, commentNum: $commentNum, createdAt: $createdAt, updatedAt: $updatedAt, closedAt: $closedAt, body: $body, bodyHtml: $bodyHtml, user: $user, repoUrl: $repoUrl, htmlUrl: $htmlUrl, closeBy: $closeBy}'; + } + + @override + List get props => [ + id, + number, + title, + state, + locked, + commentNum, + createdAt, + updatedAt, + closedAt, + body, + bodyHtml, + user, + repoUrl, + htmlUrl, + closeBy, + ]; +} diff --git a/modules/knowledge_system/artifact/lib/src/points/data/model/issue_comment.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/issue_comment.dart new file mode 100644 index 000000000..28a984eb8 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/data/model/issue_comment.dart @@ -0,0 +1,62 @@ + + + +import 'github_user.dart'; + +class IssueComment{ + int? id; + GithubUser? user; + + DateTime? createdAt; + + DateTime? updatedAt; + + String? authorAssociation; + String? body; + + String? bodyHtml; + String? type; + String? htmlUrl; + + IssueComment( + this.id, + this.user, + this.createdAt, + this.updatedAt, + this.authorAssociation, + this.body, + this.bodyHtml, + this.type, + this.htmlUrl, + ); + + factory IssueComment.fromJson(Map json) => IssueComment( + json['id'] as int?, + json['user'] == null + ? null + : GithubUser.fromJson(json['user'] as Map), + json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + json['author_association'] as String?, + json['body'] as String?, + json['body_html'] as String?, + json['event'] as String?, + json['html_url'] as String?, + ); + + Map toJson() => { + 'id': id, + 'user': user, + 'created_at': createdAt?.toIso8601String(), + 'updated_at': updatedAt?.toIso8601String(), + 'author_association': authorAssociation, + 'body': body, + 'body_html': bodyHtml, + 'event': type, + 'html_url': htmlUrl, + }; +} diff --git a/modules/knowledge_system/artifact/lib/src/points/data/model/license.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/license.dart new file mode 100644 index 000000000..9e166fc3e --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/data/model/license.dart @@ -0,0 +1,24 @@ + + +class License { + + String? name; + String? spdxId; + + License(this.name,this.spdxId); + + String get type { + return spdxId ==null?"未知":spdxId!; + } + + + factory License.fromJson(Map json) => License( + json['name'] as String?, + json['spdx_id'] as String?, + ); + + Map toJson() => { + 'name': name, + 'spdx_id': spdxId, + }; +} diff --git a/modules/knowledge_system/artifact/lib/src/points/data/model/repository.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/repository.dart new file mode 100644 index 000000000..5cc97f35d --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/data/model/repository.dart @@ -0,0 +1,199 @@ +import 'github_user.dart'; +import 'license.dart'; +import 'repository_permissions.dart'; + +class Repository { + int? id; + + int? size; + + String? name; + + String? fullName; + + String? htmlUrl; + + String? description; + + String? language; + + String? defaultBranch; + + DateTime? createdAt; + + DateTime? updatedAt; + + DateTime? pushedAt; + + String? gitUrl; + + String? sshUrl; + + String? cloneUrl; + + String? svnUrl; + + int? stargazersCount; + + int? watchersCount; + + int? forksCount; + + int? openIssuesCount; + + int? subscribersCount; + + bool? private; + + bool? fork; + + bool? hasIssues; + + bool? hasProjects; + + bool? hasDownloads; + + bool? hasWiki; + + bool? hasPages; + + GithubUser? owner; + + License? license; + + Repository? parent; + + RepositoryPermissions? permissions; + + List? topics; + + int? allIssueCount; + + Repository( + this.id, + this.size, + this.name, + this.fullName, + this.htmlUrl, + this.description, + this.language, + this.license, + this.defaultBranch, + this.createdAt, + this.updatedAt, + this.pushedAt, + this.gitUrl, + this.sshUrl, + this.cloneUrl, + this.svnUrl, + this.stargazersCount, + this.watchersCount, + this.forksCount, + this.openIssuesCount, + this.subscribersCount, + this.private, + this.fork, + this.hasIssues, + this.hasProjects, + this.hasDownloads, + this.hasWiki, + this.hasPages, + this.owner, + this.parent, + this.permissions, + this.topics, + ); + + factory Repository.fromJson(Map json) => Repository( + json['id'] as int?, + json['size'] as int?, + json['name'] as String?, + json['full_name'] as String?, + json['html_url'] as String?, + json['description'] as String?, + json['language'] as String?, + json['license'] == null + ? null + : License.fromJson(json['license'] as Map), + json['default_branch'] as String?, + json['created_at'] == null + ? null + : DateTime.parse(json['created_at'] as String), + json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String), + json['pushed_at'] == null + ? null + : DateTime.parse(json['pushed_at'] as String), + json['git_url'] as String?, + json['ssh_url'] as String?, + json['clone_url'] as String?, + json['svn_url'] as String?, + json['stargazers_count'] as int?, + json['watchers_count'] as int?, + json['forks_count'] as int?, + json['open_issues_count'] as int?, + json['subscribers_count'] as int?, + json['private'] as bool?, + json['fork'] as bool?, + json['has_issues'] as bool?, + json['has_projects'] as bool?, + json['has_downloads'] as bool?, + json['has_wiki'] as bool?, + json['has_pages'] as bool?, + json['owner'] == null + ? null + : GithubUser.fromJson(json['owner'] as Map), + json['parent'] == null + ? null + : Repository.fromJson(json['parent'] as Map), + json['permissions'] == null + ? null + : RepositoryPermissions.fromJson( + json['permissions'] as Map), + (json['topics'] as List?)?.map((e) => e as String).toList(), + )..allIssueCount = json['allIssueCount'] as int?; + + @override + String toString() { + return 'Repository{id: $id, size: $size, name: $name, fullName: $fullName, htmlUrl: $htmlUrl, description: $description, language: $language, defaultBranch: $defaultBranch, createdAt: $createdAt, updatedAt: $updatedAt, pushedAt: $pushedAt, gitUrl: $gitUrl, sshUrl: $sshUrl, cloneUrl: $cloneUrl, svnUrl: $svnUrl, stargazersCount: $stargazersCount, watchersCount: $watchersCount, forksCount: $forksCount, openIssuesCount: $openIssuesCount, subscribersCount: $subscribersCount, private: $private, fork: $fork, hasIssues: $hasIssues, hasProjects: $hasProjects, hasDownloads: $hasDownloads, hasWiki: $hasWiki, hasPages: $hasPages, owner: $owner, license: $license, parent: $parent, permissions: $permissions, topics: $topics, allIssueCount: $allIssueCount}'; + } + + Map toJson() => { + 'id': id, + 'size': size, + 'name': name, + 'full_name': fullName, + 'html_url': htmlUrl, + 'description': description, + 'language': language, + 'default_branch': defaultBranch, + 'created_at': createdAt?.toIso8601String(), + 'updated_at': updatedAt?.toIso8601String(), + 'pushed_at': pushedAt?.toIso8601String(), + 'git_url': gitUrl, + 'ssh_url': sshUrl, + 'clone_url': cloneUrl, + 'svn_url': svnUrl, + 'stargazers_count': stargazersCount, + 'watchers_count': watchersCount, + 'forks_count': forksCount, + 'open_issues_count': openIssuesCount, + 'subscribers_count': subscribersCount, + 'private': private, + 'fork': fork, + 'has_issues': hasIssues, + 'has_projects': hasProjects, + 'has_downloads': hasDownloads, + 'has_wiki': hasWiki, + 'has_pages': hasPages, + 'owner': owner, + 'license': license, + 'parent': parent, + 'permissions': permissions, + 'topics': topics, + 'allIssueCount': allIssueCount, + }; + + Repository.empty(); +} diff --git a/modules/knowledge_system/artifact/lib/src/points/data/model/repository_permissions.dart b/modules/knowledge_system/artifact/lib/src/points/data/model/repository_permissions.dart new file mode 100644 index 000000000..dc8d0b4af --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/data/model/repository_permissions.dart @@ -0,0 +1,26 @@ + + +class RepositoryPermissions { + bool? admin; + bool? push; + bool? pull; + + RepositoryPermissions( + this.admin, + this.push, + this.pull, + ); + + factory RepositoryPermissions.fromJson(Map json) => + RepositoryPermissions( + json['admin'] as bool?, + json['push'] as bool?, + json['pull'] as bool?, + ); + + Map toJson() => { + 'admin': admin, + 'push': push, + 'pull': pull, + }; +} 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/modules/knowledge_system/artifact/lib/src/points/view/desk_ui/github_repo_panel.dart b/modules/knowledge_system/artifact/lib/src/points/view/desk_ui/github_repo_panel.dart new file mode 100644 index 000000000..910586e11 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/view/desk_ui/github_repo_panel.dart @@ -0,0 +1,154 @@ +import 'package:app/app.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:flutter/material.dart'; + +import '../../data/model/repository.dart'; + + +class GithubRepoPanel extends StatefulWidget { + final Repository repository; + + const GithubRepoPanel({Key? key, required this.repository}) : super(key: key); + + @override + State createState() => _GithubRepoPanelState(); +} + +class _GithubRepoPanelState extends State { + @override + Widget build(BuildContext context) { + return IconTheme( + data: IconThemeData(size: 18,color: Theme.of(context).primaryColor), + child: Align( + alignment: Alignment.topCenter, + child: Container( + // alignment: Alignment.topCenter, + width: 250, + padding: EdgeInsets.all(10), + margin: EdgeInsets.all(10), + decoration: BoxDecoration( + boxShadow: [ + BoxShadow(color: Colors.black.withOpacity(0.1), offset: Offset(0, .5), blurRadius: 3) + ], + color:Theme.of(context).listTileTheme.tileColor, + borderRadius: BorderRadius.circular(8)), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + widget.repository.fullName!, + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + const SizedBox(height: 4,), + Row( + children: [ + Text( + '创建时间', + style: const TextStyle(), + ), + const SizedBox(width: 8,), + Text( + '2020年04月15日', + style: const TextStyle(), + ), + ], + ), + const SizedBox(height: 4,), + Row( + children: [ + Text('开源协议'), + const SizedBox(width: 8,), + WrapColor( + padding: EdgeInsets.symmetric(vertical: 2,horizontal: 4), + child: Text( + '${widget.repository.license?.type}', + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + height: 1, + fontSize: 12), + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + widget.repository.description!, + style: const TextStyle(color: Colors.grey), + ), + ), + const Divider(), + const SizedBox(height: 8,), + DefaultTextStyle( + style: TextStyle(fontSize: 12,color: Theme.of(context).primaryColor), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const Icon(Icons.star_border), + Text(widget.repository.stargazersCount.toString(),style: TextStyle(fontSize: 12),), + ]), + SizedBox( + height: 15, + child: VerticalDivider( + width: 1, + color: Theme.of(context).primaryColor, + ), + ), + Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const Icon(TolyIcon.icon_show), + const SizedBox( + width: 5, + ), + Text(widget.repository.subscribersCount.toString(),style: TextStyle(fontSize: 12),), + ]), + SizedBox( + height: 15, + child: VerticalDivider( + width: 1, + color: Theme.of(context).primaryColor, + ), + ), + Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const Icon(TolyIcon.icon_fork), + Text(widget.repository.forksCount.toString(),style: TextStyle(fontSize: 12),), + ]), + SizedBox( + height: 15, + child: VerticalDivider( + width: 1, + color: Theme.of(context).primaryColor, + ), + ), + Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const Icon(TolyIcon.icon_issues), + const SizedBox( + width: 5, + ), + Text(widget.repository.openIssuesCount.toString(),style: TextStyle(fontSize: 12),), + ]), + ], + ), + ) + ], + ), + ), + ), + ); + } +} \ No newline at end of file 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/modules/knowledge_system/artifact/lib/src/points/view/issues_point/issues_detail.dart b/modules/knowledge_system/artifact/lib/src/points/view/issues_point/issues_detail.dart new file mode 100644 index 000000000..0338d57e2 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/view/issues_point/issues_detail.dart @@ -0,0 +1,196 @@ +import 'package:app/app.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'; + +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 +/// 说明: + +class IssuesDetailPage extends StatelessWidget { + const IssuesDetailPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: PreferredSize( + preferredSize: Size(0,kToolbarHeight), + child: DragToMoveWrapper( + child: AppBar( + centerTitle: false, + title: const Text('Flutter 要点集录'), + ), + ), + ), + body: BlocBuilder( + builder: _buildContent), + ); + } + + Widget _buildContent(BuildContext context, PointCommentState state) { + if (state is PointCommentLoading) { + return IssueTitle(issue: state.issue); + } + + if (state is PointCommentLoaded) { + return CustomScrollView( + slivers: [ + SliverToBoxAdapter(child: IssueTitle(issue: state.issue)), + SliverList( + delegate: SliverChildBuilderDelegate( + (ctx, int index) => IssueCommentWidget( + comment: state.comments[index], + ), + childCount: state.comments.length), + ) + ], + ); + } + + return Container(); + } +} + +class IssueTitle extends StatelessWidget { + final Issue issue; + + const IssueTitle({Key? key, required this.issue}) : super(key: key); + + String get issueDesHtml => issue.bodyHtml != null + ? issue.bodyHtml! + : (issue.body != null) + ? issue.body! + : ""; + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return SingleChildScrollView( + child: Column( + children: [ + Row( + children: [ + Flexible( + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 8), + child: Panel( + color: isDark?Colors.transparent:null, + child: Text( + + '${issue.title}', + maxLines: 1, + style: const TextStyle( + fontSize: 18, fontWeight: FontWeight.bold), + )), + ), + ), + WrapColor( + child: Text( + '#${issue.number}', + style: const TextStyle(color: Colors.white), + ), + ) + ], + ), + + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0,vertical: 8), + child: WrapColor( + color: Colors.blue.withAlpha(22), + child: ListTile( + dense: true, + leading: CircleImage( + size: 40, + borderSize: 1, + image: NetworkImage(issue.user!.avatarUrl!), + ), + title: Text(issue.user!.login!), + subtitle: Row( + children: [ + Text('创建于:${ConvertMan.time2string(issue.createdAt!)}'), + const Spacer(), + WrapColor( + color: Colors.green, + child: Text( + '更新于:${ConvertMan.time2string(issue.updatedAt!)}', + style: const TextStyle(color: Colors.white), + )), + ], + ), + ), + ), + ), + + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0,horizontal: 20), + child: MarkdownWidget( + markdownData: issueDesHtml, + style: isDark?MarkdownWidget.kDarkLight:MarkdownWidget.kWhite), + ), + const Divider( + thickness: 2, + ) + ], + ), + ); + } +} + +class IssueCommentWidget extends StatelessWidget { + final IssueComment comment; + + const IssueCommentWidget({Key? key, required this.comment}) : super(key: key); + + String get issueDesHtml => comment.bodyHtml != null + ? comment.bodyHtml! + : (comment.body != null) + ? comment.body! + : ""; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: WrapColor( + color: Colors.blue.withAlpha(22), + child: ListTile( + dense: true, + leading: CircleImage( + size: 40, + borderSize: 1, + image: NetworkImage(comment.user!.avatarUrl!), + ), + title: Text(comment.user!.login!), + subtitle: Row( + children: [ + Text('创建于:${ConvertMan.time2string(comment.createdAt!)}'), + const Spacer(), + WrapColor( + color: Colors.green, + child: Text( + '更新于:${ConvertMan.time2string(comment.updatedAt!)}', + style: const TextStyle(color: Colors.white), + )), + ], + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: MarkdownWidget( + markdownData: issueDesHtml, style: MarkdownWidget.kWhite), + ), + const Divider(thickness: 2) + ], + ); + } +} 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/modules/knowledge_system/artifact/lib/src/points/view/issues_point/repo_widget.dart b/modules/knowledge_system/artifact/lib/src/points/view/issues_point/repo_widget.dart new file mode 100644 index 000000000..36849e1b3 --- /dev/null +++ b/modules/knowledge_system/artifact/lib/src/points/view/issues_point/repo_widget.dart @@ -0,0 +1,132 @@ +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/repository.dart'; + +/// create by 张风捷特烈 on 2020/9/3 +/// contact me by email 1981462002@qq.com +/// 说明: + + +class RepoWidget extends StatelessWidget { + final Repository repository; + + const RepoWidget({Key? key, required this.repository}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + margin: const EdgeInsets.only(top: 56 + 32.0, bottom: 5), + padding: const EdgeInsets.only(top:20,right: 10,left: 10), + // padding: EdgeInsets.all(10), + decoration: const BoxDecoration( + boxShadow: [ + BoxShadow(color: Colors.grey, offset: Offset(0, .5), blurRadius: 3) + ], + color: Colors.white, + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(10), + bottomRight: Radius.circular(10), + )), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + children: [ + Text( + repository.fullName!, + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + const SizedBox(width: 10,), + WrapColor( + child: Text( + '${repository.license?.type}', + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + fontSize: 12), + ), + ), + const Spacer(), + Text( + "创建:${ConvertMan.time2string(repository.createdAt!, just: true)}", + style: const TextStyle(color: Colors.grey), + ), + ], + ), + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Text( + repository.description!, + style: const TextStyle(color: Colors.grey), + ), + ), + const Divider(), + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const Icon(Icons.star_border), + Text(repository.stargazersCount.toString()), + ]), + const Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Text( + "|", + style: TextStyle(fontSize: 20, color: Colors.blue), + ), + ), + Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const Icon(TolyIcon.icon_show), + const SizedBox( + width: 5, + ), + Text(repository.subscribersCount.toString()), + ]), + const Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Text( + "|", + style: TextStyle(fontSize: 20, color: Colors.blue), + ), + ), + Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const Icon(TolyIcon.icon_fork), + Text(repository.forksCount.toString()), + ]), + const Padding( + padding: EdgeInsets.symmetric(vertical: 8.0), + child: Text( + "|", + style: TextStyle(fontSize: 20, color: Colors.blue), + ), + ), + Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const Icon(TolyIcon.icon_issues), + const SizedBox( + width: 5, + ), + Text(repository.openIssuesCount.toString()), + ]), + ], + ) + ], + ), + ); + } +} \ No newline at end of file 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/modules/knowledge_system/artifact/test/utils_test.dart b/modules/knowledge_system/artifact/test/utils_test.dart new file mode 100644 index 000000000..ba04b36ea --- /dev/null +++ b/modules/knowledge_system/artifact/test/utils_test.dart @@ -0,0 +1,5 @@ +import 'package:flutter_test/flutter_test.dart'; + +void main() { + +} 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/modules/knowledge_system/awesome/CHANGELOG.md b/modules/knowledge_system/awesome/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/modules/knowledge_system/awesome/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/modules/knowledge_system/awesome/LICENSE b/modules/knowledge_system/awesome/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/modules/knowledge_system/awesome/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/modules/knowledge_system/awesome/README.md b/modules/knowledge_system/awesome/README.md new file mode 100644 index 000000000..02fe8ecab --- /dev/null +++ b/modules/knowledge_system/awesome/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/awesome/analysis_options.yaml b/modules/knowledge_system/awesome/analysis_options.yaml new file mode 100644 index 000000000..a5744c1cf --- /dev/null +++ b/modules/knowledge_system/awesome/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/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/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/main.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/main.dart new file mode 100644 index 000000000..23c687f4b --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/main.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +import 'page/home/home_page.dart'; + +void main(){ + runApp(const MyApp()); +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + theme: ThemeData( + colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), + useMaterial3: true, + ), + home: const ChangeNotifierHome01(), + ); + } +} + + + + + + + diff --git a/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/notifier/progress_value_notifier.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/notifier/progress_value_notifier.dart new file mode 100644 index 000000000..97f902607 --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/notifier/progress_value_notifier.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +ProgressValueNotifier progress = ProgressValueNotifier(); + +class ProgressValueNotifier with ChangeNotifier{ + + double _value = 0; + + double get value =>_value; + + String get valueStr => '${(value*100).toStringAsFixed(1)}%'; + + set value(double value){ + _value = value.clamp(0, 1); + notifyListeners(); + } +} \ No newline at end of file diff --git a/modules/knowledge_system/awesome/lib/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 new file mode 100644 index 000000000..39cd92204 --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/detail/detail_progress_view.dart @@ -0,0 +1,49 @@ + +import 'package:flutter/material.dart'; + +import '../../notifier/progress_value_notifier.dart'; + +class DetailProgressView extends StatefulWidget { + + const DetailProgressView({super.key}); + + @override + State createState() => _DetailProgressViewState(); +} + +class _DetailProgressViewState extends State { + + @override + void initState() { + super.initState(); + progress.addListener(_update); + } + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.center, + children:[ + SizedBox( + width: 200, + height: 50, + child: LinearProgressIndicator( + value: progress.value, + backgroundColor: Colors.grey, + ), + ), + Text(progress.valueStr,style: TextStyle(color: Colors.white),) + ], + ); + } + + void _update() { + setState(() {}); + } + + @override + void dispose() { + progress.removeListener(_update); + super.dispose(); + } +} diff --git a/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/detail/download_detail.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/detail/download_detail.dart new file mode 100644 index 000000000..212edbc16 --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/detail/download_detail.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +import 'detail_progress_view.dart'; + +class DownloadDetailPage extends StatelessWidget { + const DownloadDetailPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('下载详情页'), + ), + body: Center(child: DetailProgressView()), + ); + } +} diff --git a/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/home/home_page.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/home/home_page.dart new file mode 100644 index 000000000..e2fc4ab38 --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/home/home_page.dart @@ -0,0 +1,58 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import '../../notifier/progress_value_notifier.dart'; +import '../detail/download_detail.dart'; +import 'home_progress_view.dart'; + +class ChangeNotifierHome01 extends StatefulWidget { + const ChangeNotifierHome01({super.key}); + + @override + State createState() => _ChangeNotifierHome01State(); +} + +class _ChangeNotifierHome01State extends State { + + TextEditingController c = TextEditingController(); + @override + Widget build(BuildContext context) { + return Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: _startTimer, + child: const Icon(Icons.restart_alt), + ), + appBar: AppBar( + title: const Text('下载进度通知模拟'), + actions: [ + IconButton(onPressed: (){ + Navigator.of(context).push(MaterialPageRoute(builder: (_)=>const DownloadDetailPage())); + }, icon: const Icon(Icons.info_outline)) + ], + ), + body: const Center(child: HomeProgressView()), + ); + } + + Timer? _timer; + + void _startTimer(){ + if(_timer!=null) return; + if(progress.value==1.0){ + progress.value=0; + } + print("======${progress.value==1.0}======"); + _timer = Timer.periodic(const Duration(milliseconds: 200),_updateProgress); + } + + + void _updateProgress(Timer timer) { + if(progress.value>=1.0){ + timer.cancel(); + _timer = null; + return; + } + progress.value += 0.01; + } +} + diff --git a/modules/knowledge_system/awesome/lib/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 new file mode 100644 index 000000000..555ef7409 --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_01/page/home/home_progress_view.dart @@ -0,0 +1,49 @@ + +import 'package:flutter/material.dart'; + +import '../../notifier/progress_value_notifier.dart'; + +class HomeProgressView extends StatefulWidget { + + const HomeProgressView({super.key}); + + @override + State createState() => _HomeProgressViewState(); +} + +class _HomeProgressViewState extends State { + + @override + void initState() { + super.initState(); + progress.addListener(_update); + } + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.center, + children:[ + SizedBox( + width: 100, + height: 100, + child: CircularProgressIndicator( + value: progress.value, + backgroundColor: Colors.grey, + ), + ), + Text(progress.valueStr) + ], + ); + } + + void _update() { + setState(() {}); + } + + @override + void dispose() { + progress.removeListener(_update); + super.dispose(); + } +} 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/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/notifier/download_data_scope.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/notifier/download_data_scope.dart new file mode 100644 index 000000000..770756cf6 --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/notifier/download_data_scope.dart @@ -0,0 +1,16 @@ +import 'package:flutter/cupertino.dart'; + +import 'progress_value_notifier.dart'; + +class DownloadDataScope extends InheritedNotifier{ + + const DownloadDataScope({super.key, required super.child,super.notifier}); + + static ProgressValueNotifier of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType()!.notifier!; + } + + static ProgressValueNotifier read(BuildContext context) { + return context.getInheritedWidgetOfExactType()!.notifier!; + } +} \ No newline at end of file diff --git a/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/notifier/progress_value_notifier.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/notifier/progress_value_notifier.dart new file mode 100644 index 000000000..113b6894f --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/notifier/progress_value_notifier.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +class ProgressValueNotifier with ChangeNotifier{ + + double _value = 0; + + double get value =>_value; + + String get valueStr => '${(value*100).toStringAsFixed(1)}%'; + + set value(double value){ + _value = value.clamp(0, 1); + notifyListeners(); + } +} \ No newline at end of file diff --git a/modules/knowledge_system/awesome/lib/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 new file mode 100644 index 000000000..6395b1ff7 --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/detail/detail_progress_view.dart @@ -0,0 +1,28 @@ + +import 'package:flutter/material.dart'; + +import '../../notifier/download_data_scope.dart'; +import '../../notifier/progress_value_notifier.dart'; + +class DetailProgressView extends StatelessWidget{ + const DetailProgressView({super.key}); + + @override + Widget build(BuildContext context) { + ProgressValueNotifier progress = DownloadDataScope.of(context); + return Stack( + alignment: Alignment.center, + children:[ + SizedBox( + width: 200, + height: 50, + child: LinearProgressIndicator( + value: progress.value, + backgroundColor: Colors.grey, + ), + ), + Text(progress.valueStr,style: TextStyle(color: Colors.white),) + ], + ); + } +} diff --git a/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/detail/download_detail.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/detail/download_detail.dart new file mode 100644 index 000000000..212edbc16 --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/detail/download_detail.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +import 'detail_progress_view.dart'; + +class DownloadDetailPage extends StatelessWidget { + const DownloadDetailPage({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('下载详情页'), + ), + body: Center(child: DetailProgressView()), + ); + } +} diff --git a/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/home/home_page.dart b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/home/home_page.dart new file mode 100644 index 000000000..dd17c9379 --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/home/home_page.dart @@ -0,0 +1,61 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import '../../notifier/download_data_scope.dart'; +import '../../notifier/progress_value_notifier.dart'; +import '../detail/download_detail.dart'; +import 'home_progress_view.dart'; + +class ChangeNotifierHome02 extends StatefulWidget { + const ChangeNotifierHome02({super.key}); + + @override + State createState() => _ChangeNotifierHome02State(); +} + +class _ChangeNotifierHome02State extends State { + + TextEditingController c = TextEditingController(); + @override + Widget build(BuildContext context) { + print("=========_ChangeNotifierHome02State#build==========="); + return Scaffold( + floatingActionButton: FloatingActionButton( + onPressed: _startTimer, + child: const Icon(Icons.restart_alt), + ), + appBar: AppBar( + title: const Text('下载进度通知模拟'), + actions: [ + IconButton(onPressed: (){ + Navigator.of(context).push(MaterialPageRoute(builder: (_)=>const DownloadDetailPage())); + }, icon: const Icon(Icons.info_outline)) + ], + ), + body: const Center(child: HomeProgressView()), + ); + } + + Timer? _timer; + + ProgressValueNotifier get progress => DownloadDataScope.read(context); + + void _startTimer(){ + if(_timer!=null) return; + if(progress.value==1.0){ + progress.value=0; + } + _timer = Timer.periodic(const Duration(milliseconds: 200),_updateProgress); + } + + + void _updateProgress(Timer timer) { + if(progress.value>=1.0){ + timer.cancel(); + _timer = null; + return; + } + progress.value += 0.01; + } +} + diff --git a/modules/knowledge_system/awesome/lib/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 new file mode 100644 index 000000000..96d73da54 --- /dev/null +++ b/modules/knowledge_system/awesome/lib/listenable/change_notifier_02/page/home/home_progress_view.dart @@ -0,0 +1,32 @@ + +import 'package:flutter/material.dart'; + +import '../../notifier/download_data_scope.dart'; +import '../../notifier/progress_value_notifier.dart'; + +class HomeProgressView extends StatelessWidget{ + const HomeProgressView({super.key}); + + + @override + Widget build(BuildContext context) { + ProgressValueNotifier progress = DownloadDataScope.of(context); + return Stack( + alignment: Alignment.center, + children:[ + SizedBox( + width: 100, + height: 100, + child: CircularProgressIndicator( + value: progress.value, + backgroundColor: Colors.grey, + ), + ), + Text(progress.valueStr) + ], + ); + } + + + +} 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/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/fade_page_route.dart b/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/fade_page_route.dart new file mode 100644 index 000000000..01b5015f6 --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/fade_page_route.dart @@ -0,0 +1,56 @@ +//渐变透明路由动画 +import 'dart:io'; + +import 'package:flutter/material.dart'; + +import '../slide_transition/cupertino_back_gesture_detector.dart'; + + +class FadePageRoute extends MaterialPageRoute { + final Widget child; + final Duration duration; + final Curve? curve; + + FadePageRoute({ + required this.child, + this.duration = const Duration(milliseconds: 300), + this.curve, + }) : super(builder: (_) => child); + + @override + Widget buildTransitions( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + if (Platform.isIOS) { + child = CupertinoBackGestureDetector( + enabledCallback: () => isPopGestureEnabled(this), + onStartPopGesture: () => startPopGesture(this), + child: child, + ); + } + + if (curve != null) { + animation = CurvedAnimation( + parent: animation, + curve: curve!, + ); + } + + return FadeTransition( + opacity: Tween(begin: 0.1, end: 1.0).animate(animation), + child: child, + ); + } + + @override + Duration get transitionDuration => duration; + + @override + @protected + bool get hasScopedWillPopCallback { + return false; + } +} diff --git a/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/slide_page_route.dart b/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/slide_page_route.dart new file mode 100644 index 000000000..2cb92871a --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/slide_page_route.dart @@ -0,0 +1,50 @@ +//右--->左 +import 'dart:io'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import '../slide_transition/cupertino_back_gesture_detector.dart'; + + + +class SlidePageRoute extends MaterialPageRoute { + final Widget child; + final Duration duration; + + SlidePageRoute({ + required this.child, + this.duration = const Duration(milliseconds: 300), + }) : super(builder: (_) => child); + + @override + Widget buildTransitions( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + if (Platform.isIOS) { + child = CupertinoBackGestureDetector( + enabledCallback: () => isPopGestureEnabled(this), + onStartPopGesture: () => startPopGesture(this), + child: child); + } + final bool linearTransition = isPopGestureInProgress(this); + return CupertinoPageTransition( + primaryRouteAnimation: animation, + secondaryRouteAnimation: secondaryAnimation, + linearTransition: linearTransition, + child: child, + ); + } + + @override + Duration get transitionDuration => duration; + + @override + @protected + bool get hasScopedWillPopCallback { + return false; + } +} diff --git a/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/zero_page_route.dart b/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/zero_page_route.dart new file mode 100644 index 000000000..a6c0ca8bf --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/router/transition/page_route/zero_page_route.dart @@ -0,0 +1,36 @@ + +import 'dart:io'; + +import 'package:flutter/material.dart'; + +import '../slide_transition/cupertino_back_gesture_detector.dart'; + + +class ZeroPageRoute extends MaterialPageRoute { + final Widget child; + + + ZeroPageRoute({ + required this.child, + }) : super(builder: (_) => child); + + @override + Widget buildTransitions( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + if (Platform.isIOS) { + child = CupertinoBackGestureDetector( + enabledCallback: () => isPopGestureEnabled(this), + onStartPopGesture: () => startPopGesture(this), + child: child, + ); + } + return child; + } + + @override + Duration get transitionDuration => Duration.zero; +} 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/modules/knowledge_system/layout/lib/src/navigation/router/transition/slide_transition/cupertino_back_gesture_detector.dart b/modules/knowledge_system/layout/lib/src/navigation/router/transition/slide_transition/cupertino_back_gesture_detector.dart new file mode 100644 index 000000000..53bec7e3e --- /dev/null +++ b/modules/knowledge_system/layout/lib/src/navigation/router/transition/slide_transition/cupertino_back_gesture_detector.dart @@ -0,0 +1,277 @@ +import 'dart:math'; +import 'dart:ui'; + +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; + +// The maximum time for a page to get reset to it's original position if the +// user releases a page mid swipe. +const int _kMaxPageBackAnimationTime = 300; // Milliseconds. +const double _kBackGestureWidth = 20.0; +const double _kMinFlingVelocity = 1.0; // Screen widths per second. + +// An eyeballed value for the maximum time it takes for a page to animate forward +// if the user releases a page mid swipe. +const int _kMaxDroppedSwipePageForwardAnimationTime = 800; // Milliseconds. + +/// This is the widget side of [CupertinoBackGestureController]. +/// +/// This widget provides a gesture recognizer which, when it determines the +/// route can be closed with a back gesture, creates the controller and +/// feeds it the input from the gesture recognizer. +/// +/// The gesture data is converted from absolute coordinates to logical +/// coordinates by this widget. +/// +/// The type `T` specifies the return type of the route with which this gesture +/// detector is associated. +class CupertinoBackGestureDetector extends StatefulWidget { + const CupertinoBackGestureDetector({ + super.key, + required this.enabledCallback, + required this.onStartPopGesture, + required this.child, + }); + + final Widget child; + + final ValueGetter enabledCallback; + + final ValueGetter> onStartPopGesture; + + @override + _CupertinoBackGestureDetectorState createState() => _CupertinoBackGestureDetectorState(); +} + +class _CupertinoBackGestureDetectorState extends State> { + CupertinoBackGestureController? _backGestureController; + + late HorizontalDragGestureRecognizer _recognizer; + + @override + void initState() { + super.initState(); + _recognizer = HorizontalDragGestureRecognizer(debugOwner: this) + ..onStart = _handleDragStart + ..onUpdate = _handleDragUpdate + ..onEnd = _handleDragEnd + ..onCancel = _handleDragCancel; + } + + @override + void dispose() { + _recognizer.dispose(); + super.dispose(); + } + + void _handleDragStart(DragStartDetails details) { + assert(mounted); + assert(_backGestureController == null); + _backGestureController = widget.onStartPopGesture(); + } + + void _handleDragUpdate(DragUpdateDetails details) { + assert(mounted); + assert(_backGestureController != null); + _backGestureController!.dragUpdate(_convertToLogical(details.primaryDelta! / context.size!.width)); + } + + void _handleDragEnd(DragEndDetails details) { + assert(mounted); + assert(_backGestureController != null); + _backGestureController!.dragEnd(_convertToLogical(details.velocity.pixelsPerSecond.dx / context.size!.width)); + _backGestureController = null; + } + + void _handleDragCancel() { + assert(mounted); + // This can be called even if start is not called, paired with the "down" event + // that we don't consider here. + _backGestureController?.dragEnd(0.0); + _backGestureController = null; + } + + void _handlePointerDown(PointerDownEvent event) { + if (widget.enabledCallback()) { + _recognizer.addPointer(event); + } + } + + double _convertToLogical(double value) { + switch (Directionality.of(context)) { + case TextDirection.rtl: + return -value; + case TextDirection.ltr: + return value; + } + } + + @override + Widget build(BuildContext context) { + assert(debugCheckHasDirectionality(context)); + // For devices with notches, the drag area needs to be larger on the side + // that has the notch. + double dragAreaWidth = Directionality.of(context) == TextDirection.ltr ? + MediaQuery.paddingOf(context).left : + MediaQuery.paddingOf(context).right; + dragAreaWidth = max(dragAreaWidth, _kBackGestureWidth); + return Stack( + fit: StackFit.passthrough, + children: [ + widget.child, + PositionedDirectional( + start: 0.0, + width: dragAreaWidth, + top: 0.0, + bottom: 0.0, + child: Listener( + onPointerDown: _handlePointerDown, + behavior: HitTestBehavior.translucent, + ), + ), + ], + ); + } +} + + +class CupertinoBackGestureController { + /// Creates a controller for an iOS-style back gesture. + /// + /// The [navigator] and [controller] arguments must not be null. + CupertinoBackGestureController({ + required this.navigator, + required this.controller, + }) { + navigator.didStartUserGesture(); + } + + final AnimationController controller; + final NavigatorState navigator; + + /// The drag gesture has changed by [fractionalDelta]. The total range of the + /// drag should be 0.0 to 1.0. + void dragUpdate(double delta) { + controller.value -= delta; + } + + /// The drag gesture has ended with a horizontal motion of + /// [fractionalVelocity] as a fraction of screen width per second. + void dragEnd(double velocity) { + // Fling in the appropriate direction. + // AnimationController.fling is guaranteed to + // take at least one frame. + // + // This curve has been determined through rigorously eyeballing native iOS + // animations. + const Curve animationCurve = Curves.fastLinearToSlowEaseIn; + final bool animateForward; + + // If the user releases the page before mid screen with sufficient velocity, + // or after mid screen, we should animate the page out. Otherwise, the page + // should be animated back in. + if (velocity.abs() >= _kMinFlingVelocity) { + animateForward = velocity <= 0; + } else { + animateForward = controller.value > 0.5; + } + + if (animateForward) { + // The closer the panel is to dismissing, the shorter the animation is. + // We want to cap the animation time, but we want to use a linear curve + // to determine it. + final int droppedPageForwardAnimationTime = min( + lerpDouble(_kMaxDroppedSwipePageForwardAnimationTime, 0, controller.value)!.floor(), + _kMaxPageBackAnimationTime, + ); + controller.animateTo(1.0, duration: Duration(milliseconds: droppedPageForwardAnimationTime), curve: animationCurve); + } else { + // This route is destined to pop at this point. Reuse navigator's pop. + navigator.pop(); + + // The popping may have finished inline if already at the target destination. + if (controller.isAnimating) { + // Otherwise, use a custom popping animation duration and curve. + final int droppedPageBackAnimationTime = lerpDouble(0, _kMaxDroppedSwipePageForwardAnimationTime, controller.value)!.floor(); + controller.animateBack(0.0, duration: Duration(milliseconds: droppedPageBackAnimationTime), curve: animationCurve); + } + } + + if (controller.isAnimating) { + // Keep the userGestureInProgress in true state so we don't change the + // curve of the page transition mid-flight since CupertinoPageTransition + // depends on userGestureInProgress. + late AnimationStatusListener animationStatusCallback; + animationStatusCallback = (AnimationStatus status) { + navigator.didStopUserGesture(); + controller.removeStatusListener(animationStatusCallback); + }; + controller.addStatusListener(animationStatusCallback); + } else { + navigator.didStopUserGesture(); + } + } +} + +// Called by _CupertinoBackGestureDetector when a pop ("back") drag start +// gesture is detected. The returned controller handles all of the subsequent +// drag events. +CupertinoBackGestureController startPopGesture(PageRoute route) { + return CupertinoBackGestureController( + navigator: route.navigator!, + controller: route.controller!, // protected access + ); +} + +bool isPopGestureEnabled(PageRoute route) { + print( + "======_isPopGestureEnabled:${route.hasScopedWillPopCallback}========="); +// If there's nothing to go back to, then obviously we don't support +// the back gesture. + if (route.isFirst) { + return false; + } +// If the route wouldn't actually pop if we popped it, then the gesture +// would be really confusing (or would skip internal routes), so disallow it. + if (route.willHandlePopInternally) { + return false; + } +// If attempts to dismiss this route might be vetoed such as in a page +// with forms, then do not allow the user to dismiss the route with a swipe. + if (route.hasScopedWillPopCallback) { + return false; + } +// Fullscreen dialogs aren't dismissible by back swipe. + if (route.fullscreenDialog) { + return false; + } +// If we're in an animation already, we cannot be manually swiped. + if (route.animation!.status != AnimationStatus.completed) { + return false; + } +// If we're being popped into, we also cannot be swiped until the pop above +// it completes. This translates to our secondary animation being +// dismissed. + if (route.secondaryAnimation!.status != AnimationStatus.dismissed) { + return false; + } +// If we're in a gesture already, we cannot start another. + if (isPopGestureInProgress(route)) { + return false; + } + +// Looks like a back gesture would be welcome! + return true; +} + +/// True if an iOS-style back swipe pop gesture is currently underway for [route]. +/// +/// This just check the route's [NavigatorState.userGestureInProgress]. +/// +/// See also: +/// +/// * [popGestureEnabled], which returns true if a user-triggered pop gesture +/// would be allowed. +bool isPopGestureInProgress(PageRoute route) { + return route.navigator!.userGestureInProgress; +} 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 92% 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 index dc535a510..4d952e58f 100644 --- a/lib/painter_system/anim/bezier3_player/bezier3_palyer.dart +++ b/modules/painting_system/draw_system/lib/src/anim/bezier3_player/bezier3_palyer.dart @@ -9,6 +9,8 @@ import 'touch_info.dart'; /// 说明: class Bezier3Player extends StatefulWidget { + const Bezier3Player({Key? key}) : super(key: key); + @override _Bezier3PlayerState createState() => _Bezier3PlayerState(); } @@ -76,15 +78,15 @@ class _Bezier3PlayerState extends State { class PaperPainter extends CustomPainter { final Coordinate coordinate = Coordinate(); - Paint _helpPaint = Paint() + final Paint _helpPaint = Paint() ..color = Colors.purple ..style = PaintingStyle.stroke ..strokeCap = StrokeCap.round; final TouchInfo repaint; - PaperPainter({this.repaint}) : super(repaint: repaint); - List pos; + PaperPainter({required this.repaint}) : super(repaint: repaint); + List pos=[]; @override void paint(Canvas canvas, Size size) { @@ -112,13 +114,13 @@ class PaperPainter extends CustomPainter { } void _drawHelp(Canvas canvas) { - _helpPaint..color = Colors.purple; + _helpPaint.color = Colors.purple; canvas.drawPoints(PointMode.polygon, pos, _helpPaint..strokeWidth = 1); canvas.drawPoints(PointMode.points, pos, _helpPaint..strokeWidth = 8); } void _drawSelectPos(Canvas canvas,Size size) { - Offset selectPos = repaint.selectPoint; + Offset? selectPos = repaint.selectPoint; if (selectPos == null) return; selectPos = selectPos.translate(-size.width / 2, -size.height / 2); canvas.drawCircle( 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 89% 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 index 3dc7f8913..68184e1b9 100644 --- a/lib/painter_system/anim/bezier3_player/touch_info.dart +++ b/modules/painting_system/draw_system/lib/src/anim/bezier3_player/touch_info.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; /// 说明: class TouchInfo extends ChangeNotifier { - List _points = []; + final List _points = []; int _selectIndex = -1; int get selectIndex => _selectIndex; @@ -13,7 +13,6 @@ class TouchInfo extends ChangeNotifier { List get points => _points; set selectIndex(int value) { - assert(value != null); if (_selectIndex == value) return; _selectIndex = value; @@ -36,6 +35,6 @@ class TouchInfo extends ChangeNotifier { notifyListeners(); } - Offset get selectPoint => + Offset? get selectPoint => _selectIndex == -1 ? null : _points[_selectIndex]; } diff --git a/lib/painter_system/anim/circle_halo.dart b/modules/painting_system/draw_system/lib/src/anim/circle_halo.dart similarity index 80% rename from lib/painter_system/anim/circle_halo.dart rename to modules/painting_system/draw_system/lib/src/anim/circle_halo.dart index a077fd6ed..24b3ab869 100644 --- a/lib/painter_system/anim/circle_halo.dart +++ b/modules/painting_system/draw_system/lib/src/anim/circle_halo.dart @@ -4,7 +4,7 @@ import 'dart:ui' as ui; import 'package:flutter/material.dart'; class CircleHalo extends StatefulWidget { - const CircleHalo({Key key}) : super(key: key); + const CircleHalo({Key? key}) : super(key: key); @override _CircleHaloState createState() => _CircleHaloState(); @@ -12,14 +12,14 @@ class CircleHalo extends StatefulWidget { class _CircleHaloState extends State with SingleTickerProviderStateMixin { - AnimationController _ctrl; + late AnimationController _ctrl; @override void initState() { super.initState(); _ctrl = AnimationController( vsync: this, - duration: Duration(seconds: 2), + duration: const Duration(seconds: 2), ); _ctrl.repeat(); @@ -34,7 +34,7 @@ class _CircleHaloState extends State @override Widget build(BuildContext context) { return CustomPaint( - size: Size(200, 200), + size: const Size(200, 200), painter: CircleHaloPainter(_ctrl), ); } @@ -69,21 +69,21 @@ class CircleHaloPainter extends CustomPainter { ..style = PaintingStyle.stroke; Path circlePath = Path() - ..addOval(Rect.fromCenter(center: Offset(0, 0), width: 100, height: 100)); + ..addOval(Rect.fromCenter(center: const Offset(0, 0), width: 100, height: 100)); Path circlePath2 = Path() ..addOval( - Rect.fromCenter(center: Offset(-1, 0), width: 100, height: 100)); + Rect.fromCenter(center: const Offset(-1, 0), width: 100, height: 100)); Path result = Path.combine(PathOperation.difference, circlePath, circlePath2); List colors = [ - Color(0xFFF60C0C), - Color(0xFFF3B913), - Color(0xFFE7F716), - Color(0xFF3DF30B), - Color(0xFF0DF6EF), - Color(0xFF0829FB), - Color(0xFFB709F4), + const Color(0xFFF60C0C), + const Color(0xFFF3B913), + const Color(0xFFE7F716), + const Color(0xFF3DF30B), + const Color(0xFF0DF6EF), + const Color(0xFF0829FB), + const Color(0xFFB709F4), ]; colors.addAll(colors.reversed.toList()); final List pos = @@ -100,7 +100,7 @@ class CircleHaloPainter extends CustomPainter { canvas.rotate(animation.value * 2 * pi); paint ..style = PaintingStyle.fill - ..color = Color(0xff00abf2); + ..color = const Color(0xff00abf2); paint.shader=null; canvas.drawPath(result, paint); canvas.restore(); 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 93% 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 index 5df1ac119..eae1ea224 100644 --- a/lib/painter_system/anim/curve_shower/anim_painter.dart +++ b/modules/painting_system/draw_system/lib/src/anim/curve_shower/anim_painter.dart @@ -22,6 +22,7 @@ class AnimPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { + canvas.translate(0, size.height); _drawAxis(canvas,size); @@ -32,7 +33,7 @@ class AnimPainter extends CustomPainter { fps_60.moveTo(3.0 * 60, 0); fps_60.relativeLineTo(0, -size.height); canvas.drawPath(fps_60, fpsPaint); - textPainter.text = TextSpan( + textPainter.text = const TextSpan( text: '60 帧', style: TextStyle(fontSize: 12, color: Colors.green)); textPainter.layout(); // 进行布局 textPainter.paint(canvas, Offset(3.0 * 61 + 5, -size.height)); @@ -54,12 +55,12 @@ class AnimPainter extends CustomPainter { axisPath.relativeLineTo(4, 10); canvas.drawPath(axisPath, axisPaint); - textPainter.text = TextSpan( + textPainter.text = const TextSpan( text: '帧数/f', style: TextStyle(fontSize: 12, color: Colors.black)); textPainter.layout(); // 进行布局 Size textSize = textPainter.size; // 尺寸必须在布局后获取 textPainter.paint(canvas, Offset(size.width - textSize.width, 5)); - textPainter.text = TextSpan( + textPainter.text = const TextSpan( text: '数值/y', style: TextStyle(fontSize: 12, color: Colors.black)); textPainter.layout(); // 进行布局 Size textSize2 = textPainter.size; // 尺寸必须在布局后获取 @@ -71,7 +72,7 @@ class AnimPainter extends CustomPainter { double step = size.height / 11; - if(points.values.length>0){ + if(points.values.isNotEmpty){ canvas.drawLine(Offset(0, -points.values.last*step*10), Offset(280, -points.values.last*step*10), Paint()..color=Colors.purple); canvas.drawCircle(Offset(230, -points.values.last*step*10), 10, Paint()..color=Colors.orange); } @@ -84,7 +85,7 @@ class AnimPainter extends CustomPainter { textPainter.text = TextSpan( text: '${i / 10}', - style: TextStyle(fontSize: 12, color: Colors.black)); + style: const TextStyle(fontSize: 12, color: Colors.black)); textPainter.layout(); // 进行布局 Size textSize = textPainter.size; // 尺寸必须在布局后获取 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 91% 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 b1fda17e1..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,13 +1,11 @@ -import 'dart:ui'; - +import 'package:toly_ui/toly_ui.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/drop_selectable_widget.dart'; import 'anim_painter.dart'; import 'point_data.dart'; class CurveAnimShower extends StatefulWidget { - const CurveAnimShower(); + const CurveAnimShower({Key? key}) : super(key: key); @override _CurveAnimShowerState createState() => _CurveAnimShowerState(); @@ -17,11 +15,11 @@ class _CurveAnimShowerState extends State with SingleTickerProviderStateMixin { PointData points = PointData(); - AnimationController _ctrl; + late AnimationController _ctrl; final Duration animDuration = const Duration(milliseconds: 1000); - Animation curveAnim; + late Animation curveAnim; Map maps = { 'bounceOut': Curves.bounceOut, @@ -109,7 +107,7 @@ class _CurveAnimShowerState extends State iconSize: 20, height: 25, width: 180, - disableColor: Color(0xff1F425F), + disableColor: const Color(0xff1F425F), onDropSelected: (int index) async { curveAnim = CurvedAnimation( parent: _ctrl, curve: maps.values.toList()[index]); @@ -120,10 +118,7 @@ class _CurveAnimShowerState extends State padding: const EdgeInsets.all(30.0), child: CustomPaint( painter: AnimPainter(points), - size: const Size( - 200, - 200, - ), + size: const Size(200, 200), )), ], ), 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 85% rename from lib/painter_system/anim/draw_path.dart rename to modules/painting_system/draw_system/lib/src/anim/draw_path.dart index 682066caa..7cf0a56dc 100644 --- a/lib/painter_system/anim/draw_path.dart +++ b/modules/painting_system/draw_system/lib/src/anim/draw_path.dart @@ -2,7 +2,6 @@ import 'dart:math'; import 'dart:ui'; import 'dart:ui' as ui; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; /// create by 张风捷特烈 on 2020/5/1 @@ -10,12 +9,14 @@ import 'package:flutter/material.dart'; /// 说明: class DrawPath extends StatefulWidget { + const DrawPath({Key? key}) : super(key: key); + @override _DrawPathState createState() => _DrawPathState(); } class _DrawPathState extends State with SingleTickerProviderStateMixin { - AnimationController _controller; + late AnimationController _controller; @override void initState() { @@ -81,7 +82,7 @@ class PaperPainter extends CustomPainter { ..strokeWidth = 1.5 ..style = PaintingStyle.stroke; - var colors = [ + List colors = const [ Color(0xFFF60C0C), Color(0xFFF3B913), Color(0xFFE7F716), @@ -93,13 +94,17 @@ class PaperPainter extends CustomPainter { var pos = [1.0 / 7, 2.0 / 7, 3.0 / 7, 4.0 / 7, 5.0 / 7, 6.0 / 7, 1.0]; paint.shader = ui.Gradient.linear( - Offset(0, 0), Offset(100, 0), colors, pos, TileMode.mirror); + const Offset(0, 0), + const Offset(100, 0), + colors, + pos, + TileMode.mirror, + ); Offset p1 = points[0]; - path.reset(); - path..moveTo(p1.dx, p1.dy); + path.moveTo(p1.dx, p1.dy); for (var i = 1; i < points.length - 1; i++) { double xc = (points[i].dx + points[i + 1].dx) / 2; @@ -111,12 +116,12 @@ class PaperPainter extends CustomPainter { PathMetrics pms = path.computeMetrics(); - pms.forEach((pm) { - Tangent tangent = pm.getTangentForOffset(pm.length * repaint.value); + for (PathMetric pm in pms) { + Tangent? tangent = pm.getTangentForOffset(pm.length * repaint.value); canvas.drawPath(pm.extractPath(0, pm.length * repaint.value), paint); canvas.drawCircle( - tangent.position, 5, Paint()..color = Colors.blue); - }); + tangent?.position??Offset.zero, 5, Paint()..color = Colors.blue); + } } @override diff --git a/modules/painting_system/draw_system/lib/src/anim/dundun_path.dart b/modules/painting_system/draw_system/lib/src/anim/dundun_path.dart new file mode 100644 index 000000000..41b5fdc3c --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/anim/dundun_path.dart @@ -0,0 +1,264 @@ +import 'dart:math'; +import 'dart:ui' as ui; +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + + + +class DunDunPathPage extends StatefulWidget { + const DunDunPathPage({Key? key}) : super(key: key); + + @override + _DunDunPathPageState createState() => _DunDunPathPageState(); +} + +class _DunDunPathPageState extends Statewith SingleTickerProviderStateMixin { + ui.Image? logo2Image; + + late AnimationController _controller; + + + @override + void initState() { + super.initState(); + loadImage(); + _controller = AnimationController( + duration: const Duration(seconds: 3), + vsync: this, + )..forward(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + void loadImage() async { + ByteData data2 = await rootBundle.load('assets/images/logo1.webp'); + logo2Image = await decodeImageFromList(data2.buffer.asUint8List()); + if (mounted) setState(() {}); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: GestureDetector( + onTap: (){ + _controller.reset(); + _controller.forward(); + }, + child: CustomPaint( + painter: DunDunPainter(logo2Image,_controller), + size: const Size(200, 200), + ), + ), + ), + ); + } +} + +class DunDunPainter extends CustomPainter { + + final ui.Image? logo2Image; + final Animation repaint; + + DunDunPainter(this.logo2Image,this.repaint):super(repaint: repaint); + + final Paint helpPaint = Paint() + ..style = PaintingStyle.stroke + ..color = Colors.blue; + final Paint pathPaint = Paint()..style = PaintingStyle.stroke; + + @override + void paint(Canvas canvas, Size size) { + if (logo2Image!=null) { + Rect src2 = Rect.fromLTWH( + 0, 0, logo2Image!.width.toDouble(), logo2Image!.height.toDouble()); + Rect dst2 = const Rect.fromLTWH(85, 132, 899/27, 1066/27); + canvas.drawImageRect(logo2Image!, src2, dst2, Paint()); + } + + Path dundunPath = Path(); + canvas.translate(30, 80); + helpPaint.color = Colors.red; + + Path bodyPath = buildBodyPath(); + Path leftHandPath = buildLeftHandPath(); + Path rightHandPath = buildRightHandPath(); + + canvas.save(); + Path eyePath = Path(); + Matrix4 m = Matrix4.translationValues(46,-12, 0).multiplied( + Matrix4.rotationZ(45 / 180 * pi) + ); + eyePath + .addOval(Rect.fromCenter(center: const Offset(0, 0), width: 32, height: 49)); + eyePath = eyePath.transform(m.storage); + canvas.restore(); + + Path leftEyePath = Path(); + leftEyePath.addOval(Rect.fromCenter(center: const Offset(50, -13), width: 18, height: 18)); + + Path leftEyePath2 = Path(); + leftEyePath2.addOval(Rect.fromCenter(center: const Offset(50, -13), width: 7, height: 7)); + + Path leftEyePath3 = Path(); + leftEyePath3.addOval(Rect.fromCenter(center: const Offset(51, -19), width: 4, height: 4)); + + + Path rightEyePath = Path(); + rightEyePath.addOval(Rect.fromCenter(center: const Offset(98, -14), width: 17, height: 17)); + + Path rightEyePath2 = Path(); + rightEyePath2.addOval(Rect.fromCenter(center: const Offset(98, -14), width: 7, height: 7)); + + Path rightEyePath3 = Path(); + rightEyePath3.addOval(Rect.fromCenter(center: const Offset(98, -19), width: 4, height: 4)); + + Path nosePath = Path(); + nosePath.moveTo(79, -0,); + nosePath.relativeLineTo(14, -14,); + nosePath.relativeLineTo(-28, 0,); + nosePath.close(); + Path clipCirclePath =Path(); + clipCirclePath.addOval(Rect.fromCenter(center: const Offset(79, -10,), width: 14, height: 14)); + nosePath = Path.combine(PathOperation.intersect, nosePath, clipCirclePath); + Path smaliPath = Path(); + smaliPath.moveTo(65, -0,); + + smaliPath.quadraticBezierTo(78, 15, 90, 0); + smaliPath.quadraticBezierTo(78, 6, 65, 0,); + + Path colorfulPath = Path(); + colorfulPath.addOval(Rect.fromCenter(center: const Offset(72, -5,), width: 120, height: 110)); + colorfulPath.addOval(Rect.fromCenter(center: const Offset(72, -5,), width: 110, height: 100)); + colorfulPath.addOval(Rect.fromCenter(center: const Offset(72, -5,), width: 115, height: 110)); + colorfulPath.addOval(Rect.fromCenter(center: const Offset(72, -5,), width: 120, height: 105)); + colorfulPath.addOval(Rect.fromCenter(center: const Offset(72, -5,), width: 115, height: 105)); + + canvas.save(); + Path eyePath2 = Path(); + Matrix4 m2 = Matrix4.translationValues(105,-12,0).multiplied( + Matrix4.rotationZ(-40 / 180 * pi) + ); + eyePath2 + .addOval(Rect.fromCenter(center: const Offset(0, 0), width: 29, height: 48)); + eyePath2 = eyePath2.transform(m2.storage); + canvas.restore(); + Path leftFootPath = buildFootPath(); + Path erPath = buildErPath(); + + //爱心 + List arr = []; + int len = 50; + double a =1; + for (int i = 0; i < len; i++) { + double step = (i / len) * (pi * 2); //递增的θ + Offset offset = Offset( + a * (11 * pow(sin(step), 3)).toDouble() , + -a * + (9 * cos(step) - + 5 * cos(2 * step) - + 2 * cos(3 * step) - + cos(4 * step)), + ); + arr.add(offset); + } + Path starPath = Path(); + for (int i = 0; i < len; i++) { + starPath.lineTo(arr[i].dx, arr[i].dy); + } + starPath = starPath.shift(const Offset(152,-20)); + + dundunPath.addPath(bodyPath, Offset.zero); + dundunPath.addPath(leftHandPath, Offset.zero); + dundunPath.addPath(rightHandPath, Offset.zero); + dundunPath.addPath(leftFootPath, Offset.zero); + dundunPath.addPath(erPath, Offset.zero); + dundunPath.addPath(eyePath, Offset.zero); + dundunPath.addPath(eyePath2, Offset.zero); + dundunPath.addPath(leftEyePath, Offset.zero); + dundunPath.addPath(leftEyePath2, Offset.zero); + dundunPath.addPath(leftEyePath3, Offset.zero); + dundunPath.addPath(rightEyePath, Offset.zero); + dundunPath.addPath(rightEyePath2, Offset.zero); + dundunPath.addPath(rightEyePath3, Offset.zero); + dundunPath.addPath(nosePath, Offset.zero); + dundunPath.addPath(starPath, Offset.zero); + dundunPath.addPath(colorfulPath, Offset.zero); + dundunPath.addPath(smaliPath, Offset.zero); + + pathPaint + ..strokeWidth = 1 + ..color = Colors.cyanAccent; + PathMetrics pms = dundunPath.computeMetrics(); + for (PathMetric pm in pms) { + canvas.drawPath(pm.extractPath(0, pm.length * repaint.value), pathPaint); + } + } + + Path buildBodyPath() { + Path path = Path(); + path.quadraticBezierTo(10, -75, 75, -75); + path.quadraticBezierTo(135, -70, 138, 0); + path.quadraticBezierTo(130, 90, 65, 98); + path.quadraticBezierTo(-5, 85, 0, 0); + return path; + } + + Path buildLeftHandPath() { + Path path = Path(); + path.quadraticBezierTo( + -17, + 14, + -28, + 40, + ); + path.quadraticBezierTo(-32, 58, -15, 62); + path.quadraticBezierTo(10, 60, 0, 0); + return path; + } + + Path buildRightHandPath() { + Path path = Path(); + path.moveTo(135, -20); + path.quadraticBezierTo(140, -48, 165, -35); + path.quadraticBezierTo(180, -17, 135, 22); + path.quadraticBezierTo(125, 17, 135, -20); + return path; + } + + Path buildFootPath() { + Path path = Path(); + path.moveTo(18, 78); + path.quadraticBezierTo(18, 100, 22, 115); + path.quadraticBezierTo(60, 125, 55, 98); + path.quadraticBezierTo(35, 80, 18, 78); + + Path right = path + .transform(Matrix4.diagonal3Values(-1, 1, 1).storage) + .shift(const Offset(128, 0)); + + return Path.combine(PathOperation.union, path, right); + } + + Path buildErPath() { + Path path = Path(); + path.moveTo(13, -40); + path.quadraticBezierTo(8, -95, 40, -68); + path.quadraticBezierTo(40, -55, 13, -40); + + Path right = path + .transform(Matrix4.diagonal3Values(-1, 1, 1).storage) + .shift(const Offset(138, -5)); + + return Path.combine(PathOperation.union, path, right); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} diff --git a/modules/painting_system/draw_system/lib/src/anim/rotate_by_point/angle_panter.dart b/modules/painting_system/draw_system/lib/src/anim/rotate_by_point/angle_panter.dart new file mode 100644 index 000000000..eb614219b --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/anim/rotate_by_point/angle_panter.dart @@ -0,0 +1,108 @@ +import 'dart:math'; + +import 'package:dash_painter/dash_painter.dart'; +import 'package:flutter/material.dart'; + +import 'line.dart'; + +class AnglePainter extends CustomPainter { + final DashPainter dashPainter = const DashPainter(span: 4, step: 4); + + AnglePainter({required this.line}) : super(repaint: line); + + final Paint pointPaint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1; + + final Paint helpPaint = Paint() + ..style = PaintingStyle.stroke + ..color = Colors.lightBlue + ..strokeWidth = 1; + + final TextPainter textPainter = TextPainter( + textAlign: TextAlign.center, + textDirection: TextDirection.ltr, + ); + + final Line line; + + @override + void paint(Canvas canvas, Size size) { + canvas.translate(size.width / 2, size.height / 2); + drawHelp(canvas, size); + line.paint(canvas); + } + + void drawHelp(Canvas canvas, Size size) { + Path helpPath = Path() + ..moveTo(-size.width / 2, 0) + ..relativeLineTo(size.width, 0) + ..moveTo(0, -size.height / 2) + ..relativeLineTo(0, size.height); + dashPainter.paint(canvas, helpPath, helpPaint); + + drawHelpText('0°', canvas, Offset(size.width / 2 - 20, 0)); + drawHelpText('p0', canvas, line.start.translate(-20, 0)); + drawHelpText('p1', canvas, line.end.translate(-20, 0)); + + // drawHelpText('p2', canvas, Offset(60, 40).translate(10, 0)); + // drawAnchor(canvas, Offset(60, 40)); + drawAnchor(canvas, line.percent(0.2)); + // drawAnchor(canvas, line.percent(0.5)); + + // drawAnchor(canvas, line.percent(0.8)); + + drawHelpText( + '角度: ${(line.positiveRad * 180 / pi).toStringAsFixed(2)}°', + canvas, + Offset( + -size.width / 2 + 10, + -size.height / 2 + 10, + ), + ); + + + + // canvas.drawArc( + // Rect.fromCenter(center: line.start, width: 20, height: 20), + // 0, + // line.positiveRad, + // false, + // helpPaint, + // ); + + // canvas.save(); + // Offset center = const Offset(60, 60); + // canvas.translate(center.dx, center.dy); + // canvas.rotate(line.positiveRad); + // canvas.translate(-center.dx, -center.dy); + // canvas.drawCircle(center, 4, helpPaint); + // canvas.drawRect( + // Rect.fromCenter(center: center, width: 30, height: 60), helpPaint); + // canvas.restore(); + } + + void drawAnchor(Canvas canvas, Offset offset) { + canvas.drawCircle(offset, 4, pointPaint..style = PaintingStyle.stroke); + canvas.drawCircle(offset, 2, pointPaint..style = PaintingStyle.fill); + } + + void drawHelpText( + String text, + Canvas canvas, + Offset offset, { + Color color = Colors.lightBlue, + }) { + textPainter.text = TextSpan( + text: text, + style: TextStyle(fontSize: 12, color: color), + ); + textPainter.layout(maxWidth: 200); + textPainter.paint(canvas, offset); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; + } +} diff --git a/modules/painting_system/draw_system/lib/src/anim/rotate_by_point/line.dart b/modules/painting_system/draw_system/lib/src/anim/rotate_by_point/line.dart new file mode 100644 index 000000000..0d37c0530 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/anim/rotate_by_point/line.dart @@ -0,0 +1,71 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class Line with ChangeNotifier { + Line({ + this.start = Offset.zero, + this.end = Offset.zero, + }); + + Offset start; + Offset end; + + final Paint pointPaint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1; + + void paint(Canvas canvas) { + canvas.drawLine(start, end, pointPaint); + drawAnchor(canvas, start); + drawAnchor(canvas, end); + } + + double get rad => (end - start).direction; + + double get positiveRad => rad < 0 ? 2 * pi + rad : rad; + + double get length => (end - start).distance; + + void drawAnchor(Canvas canvas, Offset offset) { + canvas.drawCircle(offset, 4, pointPaint..style = PaintingStyle.stroke); + canvas.drawCircle(offset, 2, pointPaint..style = PaintingStyle.fill); + } + + double detaRotate = 0; + + void rotate(double rotate, {Offset? centre}) { + detaRotate = rotate - detaRotate; + centre = centre ?? start; + Line p2p0 = Line(start: centre, end: start); + Line p2p1 = Line(start: centre, end: end); + p2p0._rotateByStart(detaRotate); + p2p1._rotateByStart(detaRotate); + start = p2p0.end; + end = p2p1.end; + detaRotate = rotate; + notifyListeners(); + } + + + + Offset percent(double percent){ + return Offset( + length*percent*cos(rad), + length*percent*sin(rad), + )+start; + } + + void _rotateByStart(double rotate) { + end = Offset( + length * cos(rad + rotate), + length * sin(rad + rotate), + ) + + start; + } + + void tick() { + notifyListeners(); + } +} diff --git a/modules/painting_system/draw_system/lib/src/anim/rotate_by_point/rotate_by_point.dart b/modules/painting_system/draw_system/lib/src/anim/rotate_by_point/rotate_by_point.dart new file mode 100644 index 000000000..bf6a788e6 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/anim/rotate_by_point/rotate_by_point.dart @@ -0,0 +1,54 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; + +import 'angle_panter.dart'; +import 'line.dart'; + +class RotateByPointWidget extends StatefulWidget { + const RotateByPointWidget({Key? key}) : super(key: key); + + @override + State createState() => _RotateByPointWidgetState(); +} + +class _RotateByPointWidgetState extends State + with SingleTickerProviderStateMixin { + + Line line = Line(start: const Offset(20, 20), end: const Offset(50, 80)); + + late AnimationController ctrl; + + @override + void initState() { + super.initState(); + ctrl = AnimationController( + vsync: this, + duration: const Duration(seconds: 3), + )..addListener(_updateLine); + } + + @override + void dispose() { + line.dispose(); + ctrl.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + + return GestureDetector( + onTap: () => ctrl.forward(from: 0), + child: CustomPaint( + painter: AnglePainter(line: line), + size: const Size(200,200), + ), + ); + } + + void _updateLine() { + Offset center = line.percent(0.2); + line.rotate(ctrl.value * 2 * pi, centre: center); + } +} diff --git a/lib/painter_system/anim/spring_widget.dart b/modules/painting_system/draw_system/lib/src/anim/spring_widget.dart similarity index 93% rename from lib/painter_system/anim/spring_widget.dart rename to modules/painting_system/draw_system/lib/src/anim/spring_widget.dart index a4716ad38..95e318dc6 100644 --- a/lib/painter_system/anim/spring_widget.dart +++ b/modules/painting_system/draw_system/lib/src/anim/spring_widget.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; class SpringWidget extends StatefulWidget { - const SpringWidget(); + const SpringWidget({Key? key}) : super(key: key); @override _SpringWidgetState createState() => _SpringWidgetState(); @@ -17,12 +17,12 @@ const double _kK = 3; class _SpringWidgetState extends State with SingleTickerProviderStateMixin { ValueNotifier height = ValueNotifier(_kDefaultSpringHeight); - AnimationController _ctrl; + late AnimationController _ctrl; double s = 0; // 移动距离 double laseMoveLen = 0; - Animation animation; + late Animation animation; final Duration animDuration = const Duration(milliseconds: 500); @@ -98,9 +98,9 @@ class SpringPainter extends CustomPainter { final int count; final ValueListenable height; - SpringPainter({this.count = 20, this.height}):super(repaint: height); + SpringPainter({this.count = 20,required this.height}):super(repaint: height); - Paint _paint = Paint() + final Paint _paint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = 1; diff --git a/lib/painter_system/art/circle_packing.dart b/modules/painting_system/draw_system/lib/src/art/circle_packing.dart similarity index 90% rename from lib/painter_system/art/circle_packing.dart rename to modules/painting_system/draw_system/lib/src/art/circle_packing.dart index ee04b028a..33494653b 100644 --- a/lib/painter_system/art/circle_packing.dart +++ b/modules/painting_system/draw_system/lib/src/art/circle_packing.dart @@ -4,12 +4,14 @@ import 'package:flutter/material.dart'; import '../utils/colors.dart'; class Circle { - Point center; - double radius; - Color color; + Point center = const Point(0,0); + double radius = 10; + Color color = Colors.black; } class CirclePacking extends StatefulWidget { + const CirclePacking({Key? key}) : super(key: key); + @override _CirclePackingState createState() => _CirclePackingState(); } @@ -39,11 +41,12 @@ class CirclePackingPainter extends CustomPainter { Random random = Random(); void _createCircles(Canvas canvas, Size size) { - Circle circle; + Circle circle= Circle(); + bool circleSafeToDraw = false; for (int i = 0; i < createCircleAttemps; i++) { - circle = Circle() + circle ..radius = minRaidus ..center = Point( random.nextDouble() * size.width, @@ -82,7 +85,7 @@ class CirclePackingPainter extends CustomPainter { circles.asMap().forEach((key, circle) { paint.color = Colors.black; - Offset offset = Offset(circle.center.x, circle.center.y); + Offset offset = Offset(circle.center.x.toDouble(), circle.center.y.toDouble()); canvas.drawCircle(offset, circle.radius, paint); }); } diff --git a/lib/painter_system/art/cubic_disarray.dart b/modules/painting_system/draw_system/lib/src/art/cubic_disarray.dart similarity index 97% rename from lib/painter_system/art/cubic_disarray.dart rename to modules/painting_system/draw_system/lib/src/art/cubic_disarray.dart index 915abdd33..71c5ccf8e 100644 --- a/lib/painter_system/art/cubic_disarray.dart +++ b/modules/painting_system/draw_system/lib/src/art/cubic_disarray.dart @@ -3,6 +3,8 @@ import 'dart:math'; import 'package:flutter/widgets.dart'; class CubicDisarray extends StatefulWidget { + const CubicDisarray({Key? key}) : super(key: key); + @override _CubicDisarrayState createState() => _CubicDisarrayState(); } diff --git a/lib/painter_system/art/hypnotic_squares.dart b/modules/painting_system/draw_system/lib/src/art/hypnotic_squares.dart similarity index 96% rename from lib/painter_system/art/hypnotic_squares.dart rename to modules/painting_system/draw_system/lib/src/art/hypnotic_squares.dart index 08292c793..1b72f5318 100644 --- a/lib/painter_system/art/hypnotic_squares.dart +++ b/modules/painting_system/draw_system/lib/src/art/hypnotic_squares.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import '../utils/colors.dart'; class HypnoticSquares extends StatefulWidget { + const HypnoticSquares({Key? key}) : super(key: key); + @override _HypnoticSquaresState createState() => _HypnoticSquaresState(); } @@ -26,7 +28,7 @@ class _HypnoticSquaresState extends State { } class HypnoticSquaresPainter extends CustomPainter { - double startSteps; + double startSteps = 1; double offset = 4.0; double finalSize = 10.0; Random random = Random(); diff --git a/lib/painter_system/art/joy_division.dart b/modules/painting_system/draw_system/lib/src/art/joy_division.dart similarity index 89% rename from lib/painter_system/art/joy_division.dart rename to modules/painting_system/draw_system/lib/src/art/joy_division.dart index 7d7f8e858..2843f2aa9 100644 --- a/lib/painter_system/art/joy_division.dart +++ b/modules/painting_system/draw_system/lib/src/art/joy_division.dart @@ -1,9 +1,10 @@ import 'dart:math'; -import 'dart:ui'; import 'package:flutter/material.dart'; class JoyDivision extends StatefulWidget { + const JoyDivision({Key? key}) : super(key: key); + @override _JoyDivisionState createState() => _JoyDivisionState(); } @@ -53,14 +54,14 @@ class JoyDivisionPainter extends CustomPainter { for (int i = 5; i < lines.length; i++) { Point p1 = lines[i][0]; - Path path = Path()..moveTo(p1.x, p1.y); + Path path = Path()..moveTo(p1.x.toDouble(), p1.y.toDouble()); for (int j = 0; j < lines[i].length - 2; j++) { double xc = (lines[i][j].x + lines[i][j + 1].x) / 2; double yc = (lines[i][j].y + lines[i][j + 1].y) / 2; Point p2 = lines[i][j]; - path.quadraticBezierTo(p2.x, p2.y, xc, yc); + path.quadraticBezierTo(p2.x.toDouble(), p2.y.toDouble(), xc, yc); canvas.drawPath(path, paint); } } diff --git a/lib/painter_system/art/piet_mondrian.dart b/modules/painting_system/draw_system/lib/src/art/piet_mondrian.dart similarity index 91% rename from lib/painter_system/art/piet_mondrian.dart rename to modules/painting_system/draw_system/lib/src/art/piet_mondrian.dart index 323b2e596..47c0fcf34 100644 --- a/lib/painter_system/art/piet_mondrian.dart +++ b/modules/painting_system/draw_system/lib/src/art/piet_mondrian.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import '../utils/colors.dart'; class PietMondrian extends StatefulWidget { + const PietMondrian({Key? key}) : super(key: key); + @override _PietMondrianState createState() => _PietMondrianState(); } @@ -33,11 +35,11 @@ class Square { final Color color; Square({ - this.x, - this.y, - this.width, - this.height, - this.color, + required this.x, + required this.y, + required this.width, + required this.height, + required this.color, }); Map toJson() { @@ -85,20 +87,20 @@ class PietMondrianPainnter extends CustomPainter { } void _splitSquaresWith(Map coordinates, List squares) { - final double x = coordinates["x"]; - final double y = coordinates["y"]; + final double x = coordinates["x"]??0; + final double y = coordinates["y"]??0; for (int i = squares.length - 1; i >= 0; i--) { final Square square = squares[i]; - if (x != null && x > square.x && x < square.x + square.width) { + if (x > square.x && x < square.x + square.width) { if (Random().nextBool()) { squares.removeAt(i); _splitOnX(square, x, squares); } } - if (y != null && y > square.y && y < square.y + square.height) { + if (y > square.y && y < square.y + square.height) { if (Random().nextBool()) { squares.removeAt(i); _splitOnY(square, y, squares); diff --git a/lib/painter_system/art/tiled_lines.dart b/modules/painting_system/draw_system/lib/src/art/tiled_lines.dart similarity index 95% rename from lib/painter_system/art/tiled_lines.dart rename to modules/painting_system/draw_system/lib/src/art/tiled_lines.dart index 6b6d166b8..ddb47f202 100644 --- a/lib/painter_system/art/tiled_lines.dart +++ b/modules/painting_system/draw_system/lib/src/art/tiled_lines.dart @@ -2,6 +2,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; class TiledLines extends StatefulWidget { + const TiledLines({Key? key}) : super(key: key); + @override _TiledLinesState createState() => _TiledLinesState(); } @@ -64,5 +66,6 @@ class TiledLinesPainter extends CustomPainter { } } + @override bool shouldRepaint(TiledLinesPainter oldDelegate) => true; } diff --git a/lib/painter_system/art/triangular_mesh.dart b/modules/painting_system/draw_system/lib/src/art/triangular_mesh.dart similarity index 96% rename from lib/painter_system/art/triangular_mesh.dart rename to modules/painting_system/draw_system/lib/src/art/triangular_mesh.dart index fde74674e..4cde0bb8e 100644 --- a/lib/painter_system/art/triangular_mesh.dart +++ b/modules/painting_system/draw_system/lib/src/art/triangular_mesh.dart @@ -4,11 +4,13 @@ import 'package:flutter/material.dart'; import '../utils/colors.dart'; class Point { - double x; - double y; + double x=0; + double y=0; } class TriangularMesh extends StatefulWidget { + const TriangularMesh({Key? key}) : super(key: key); + @override _TriangularMeshState createState() => _TriangularMeshState(); } 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 97% rename from lib/painter_system/art/un_deux_trois.dart rename to modules/painting_system/draw_system/lib/src/art/un_deux_trois.dart index 9695e6af7..d2611a0b0 100644 --- a/lib/painter_system/art/un_deux_trois.dart +++ b/modules/painting_system/draw_system/lib/src/art/un_deux_trois.dart @@ -3,6 +3,8 @@ import 'dart:math'; import 'package:flutter/material.dart'; class UnDeuxTrois extends StatefulWidget { + const UnDeuxTrois({Key? key}) : super(key: key); + @override _UnDeuxTroisState createState() => _UnDeuxTroisState(); } diff --git a/modules/painting_system/draw_system/lib/src/base/clock_widget.dart b/modules/painting_system/draw_system/lib/src/base/clock_widget.dart new file mode 100644 index 000000000..dddbb8c5c --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/base/clock_widget.dart @@ -0,0 +1,337 @@ +import 'dart:math'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +/// create by 张风捷特烈 on 2021/2/7 +/// contact me by email 1981462002@qq.com +/// 说明: + +class ClockWidget extends StatefulWidget { + final double radius; + + const ClockWidget({Key? key, this.radius = 100}) : super(key: key); + + @override + _ClockWidgetState createState() => _ClockWidgetState(); +} + +class _ClockWidgetState extends State + with SingleTickerProviderStateMixin { + late Ticker _ticker; + ValueNotifier time = ValueNotifier(DateTime.now()); + + @override + void initState() { + super.initState(); + _ticker = createTicker(_tick)..start(); + } + + @override + void dispose() { + _ticker.dispose(); + time.dispose(); + super.dispose(); + } + + void _tick(Duration duration) { + if (time.value.second != DateTime.now().second) { + time.value = DateTime.now(); + } + } + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.center, + children: [ + CustomPaint( + size: Size(widget.radius * 2, widget.radius * 2), + painter: ClockBgPainter(radius: widget.radius), + ), + RepaintBoundary( + child: CustomPaint( + size: Size(widget.radius * 2, widget.radius * 2), + painter: ClockPainter(listenable: time, radius: widget.radius), + ), + ) + ], + ); + } +} + +class ClockBgPainter extends CustomPainter { + final Paint _paint = Paint()..style = PaintingStyle.stroke; + + final double radius; + + ClockBgPainter({this.radius = 100}); + + @override + void paint(Canvas canvas, Size size) { + + canvas.translate(size.width / 2, size.height / 2); + drawOuterCircle(canvas); + drawScale(canvas); + drawText(canvas); + } + + @override + bool shouldRepaint(covariant ClockBgPainter oldDelegate) { + return oldDelegate.radius != radius; + } + + void drawOuterCircle(Canvas canvas) { + _paint + ..strokeWidth = 4 + ..color = const Color(0xffD5D5D5); + for (int i = 0; i < 4; i++) { + _paintArc(canvas); + canvas.rotate(pi / 2); + } + } + + final Paint arcPaint = Paint() + ..style = PaintingStyle.fill + ..color = const Color(0xff00abf2); + + void _paintArc(Canvas canvas) { + arcPaint.maskFilter = MaskFilter.blur(BlurStyle.solid, logic1); + final Path circlePath = Path() + ..addArc( + Rect.fromCenter( + center: const Offset(0, 0), width: radius * 2, height: radius * 2), + 10 / 180 * pi, + pi / 2 - 20 / 180 * pi); + + Path circlePath2 = Path() + ..addArc( + Rect.fromCenter( + center: Offset(-logic1, 0), + width: radius * 2, + height: radius * 2), + 10 / 180 * pi, + pi / 2 - 20 / 180 * pi); + //联合路径 + Path result = + Path.combine(PathOperation.difference, circlePath, circlePath2); + canvas.drawPath(result, arcPaint); //绘制 + } + + void drawScale(Canvas canvas) { + _paint + ..strokeCap = StrokeCap.round + ..style = PaintingStyle.fill; + + double count = 60; + double perAngle = 2 * pi / count; + + for (int i = 0; i < count; i++) { + if (i % 5 == 0) { + _paint + ..strokeWidth = longLineWidth + ..color = Colors.blue; + canvas.drawLine(Offset(radius - scaleSpace, 0), + Offset(radius - scaleSpace - longScaleLen, 0), _paint); + canvas.drawCircle( + Offset(radius - scaleSpace - longScaleLen - logic1 * 5, 0), + longLineWidth, + _paint..color = Colors.orange); + } else { + _paint + ..strokeWidth = shortLenWidth + ..color = Colors.black; + canvas.drawLine(Offset(radius - scaleSpace, 0), + Offset(radius - scaleSpace - shortScaleLen, 0), _paint); + } + canvas.rotate(perAngle); + } + } + + double get logic1 => radius * 0.01; + + // 刻度与外圈的间隔 + double get scaleSpace => logic1 * 11; + + // 短刻度线长 + double get shortScaleLen => logic1 * 7; + + // 短刻度线长 + double get shortLenWidth => logic1; + + // 长刻度线长 + double get longScaleLen => logic1 * 11; + + // 长刻度线宽 + double get longLineWidth => logic1 * 2; + + final TextPainter _textPainter = TextPainter( + textAlign: TextAlign.center, textDirection: TextDirection.ltr); + + void drawText(Canvas canvas) { + _drawCircleText(canvas, 'Ⅸ', offsetX: -radius); + _drawCircleText(canvas, 'Ⅲ', offsetX: radius); + _drawCircleText(canvas, 'Ⅵ', offsetY: radius); + _drawCircleText(canvas, 'Ⅻ', offsetY: -radius); + _drawLogoText(canvas, offsetY: -radius * 0.5); + } + + _drawCircleText(Canvas canvas, String text, + {double offsetX = 0, double offsetY = 0}) { + _textPainter.text = TextSpan( + text: text, + style: TextStyle(fontSize: radius * 0.15, color: Colors.blue)); + _textPainter.layout(); + _textPainter.paint( + canvas, + Offset.zero.translate(-_textPainter.size.width / 2 + offsetX, + -_textPainter.height / 2 + offsetY)); + } + + _drawLogoText(Canvas canvas, {double offsetX = 0, double offsetY = 0}) { + _textPainter.text = TextSpan( + text: 'Toly', + style: TextStyle( + fontSize: radius * 0.2, color: Colors.blue, fontFamily: 'CHOPS')); + _textPainter.layout(); + _textPainter.paint( + canvas, + Offset.zero.translate(-_textPainter.size.width / 2 + offsetX, + -_textPainter.height / 2 + offsetY)); + } +} + +class ClockPainter extends CustomPainter { + final Paint _paint = Paint()..style = PaintingStyle.stroke; + + final double radius; + final ValueListenable listenable; + + ClockPainter({required this.listenable, this.radius = 100}) + : super(repaint: listenable); + + @override + void paint(Canvas canvas, Size size) { + + canvas.translate(size.width / 2, size.height / 2); + drawArrow(canvas, listenable.value); + } + + void drawArrow(Canvas canvas, DateTime time) { + int sec = time.second; + int min = time.minute; + int hour = time.hour; + + double perAngle = 360 / 60; + + double secondRad = (sec * perAngle) / 180 * pi; + double minusRad = ((min + sec / 60) * perAngle) / 180 * pi; + double hourRad = ((hour + min / 60 + sec / 3600) * perAngle * 5) / 180 * pi; + + canvas.save(); + canvas.rotate(-pi / 2); + canvas.save(); + canvas.rotate(minusRad); + drawMinus(canvas); + canvas.restore(); + + canvas.save(); + canvas.rotate(hourRad); + drawHour(canvas); + canvas.restore(); + + canvas.save(); + canvas.rotate(secondRad); + drawSecond(canvas); + canvas.restore(); + canvas.restore(); + } + + @override + bool shouldRepaint(covariant ClockPainter oldDelegate) { + return oldDelegate.radius != radius || oldDelegate.listenable != listenable; + } + + // 分针长 + double get minusLen => logic1 * 60; + + // 时针长 + double get hourLen => logic1 * 45; + + // 秒针长 + double get secondLen => logic1 * 68; + + // 时针线宽 + double get hourLineWidth => logic1 * 3; + + // 分针线宽 + double get minusLineWidth => logic1 * 2; + + // 秒针线宽 + double get logic1 => radius * 0.01; + + double get secondLineWidth => logic1; + + // 长刻度线宽 + double get longLineWidth => logic1 * 2; + + void drawHour(Canvas canvas) { + _paint + ..strokeWidth = hourLineWidth + ..color = const Color(0xff8FC552) + ..strokeCap = StrokeCap.round; + canvas.drawLine(Offset.zero, Offset(hourLen, 0), _paint); + } + + void drawMinus(Canvas canvas) { + _paint + ..strokeWidth = minusLineWidth + ..color = const Color(0xff87B953) + ..strokeCap = StrokeCap.round; + + canvas.drawLine( + Offset.zero, + Offset( + minusLen, + 0, + ), + _paint); + } + + void drawSecond(Canvas canvas) { + _paint + ..strokeWidth = logic1 * 2.5 + ..color = const Color(0xff6B6B6B) + ..strokeCap = StrokeCap.square + ..style = PaintingStyle.stroke; + Path path = Path(); + + canvas.save(); + canvas.rotate((360 - 270) / 2 / 180 * pi); + path.addArc( + Rect.fromPoints( + Offset(-logic1 * 9, -logic1 * 9), Offset(logic1 * 9, logic1 * 9)), + 0, + 270 / 180 * pi); + canvas.drawPath(path, _paint); + canvas.restore(); + + _paint.strokeCap = StrokeCap.round; + canvas.drawLine(Offset(-logic1 * 9, 0), Offset(-logic1 * 20, 0), _paint); + + _paint + ..strokeWidth = logic1 + ..color = Colors.black; + canvas.drawLine(Offset.zero, Offset(secondLen, 0), _paint); + + _paint + ..strokeWidth = logic1 * 3 + ..color = const Color(0xff6B6B6B); + canvas.drawCircle(Offset.zero, logic1 * 5, _paint); + + _paint + ..color = const Color(0xff8FC552) + ..style = PaintingStyle.fill; + canvas.drawCircle(Offset.zero, logic1 * 4, _paint); + } +} diff --git a/modules/painting_system/draw_system/lib/src/base/digital/digital_painter.dart b/modules/painting_system/draw_system/lib/src/base/digital/digital_painter.dart new file mode 100644 index 000000000..75a44f025 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/base/digital/digital_painter.dart @@ -0,0 +1,29 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import 'digital_path.dart'; + +class DigitalPainter extends CustomPainter { + final int value; + final Color color; + final DigitalPath digitalPath; + late Paint _mainPainter; + + DigitalPainter({required this.value,required this.digitalPath,required this.color}){ + _mainPainter = Paint()..style=PaintingStyle.fill..color=color; + } + + + @override + void paint(Canvas canvas, Size size) { + Path path = digitalPath.buildPath(value, size.width); + canvas.drawPath(path, _mainPainter); + } + + @override + bool shouldRepaint(covariant DigitalPainter oldDelegate) { + return oldDelegate.value != value; + } +} diff --git a/modules/painting_system/draw_system/lib/src/base/digital/digital_path.dart b/modules/painting_system/draw_system/lib/src/base/digital/digital_path.dart new file mode 100644 index 000000000..f7a55b87a --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/base/digital/digital_path.dart @@ -0,0 +1,113 @@ + +import 'dart:math'; + +import 'package:flutter/material.dart'; + +class DigitalPath { + + static const double kDigitalRate = 169/104; + + // + final Map _digitalPathMap = {}; + + DigitalPath(){ + _initDigitalPathMap(); + } + + Path buildPath(int value,double width){ + double rate = width/104; + Matrix4 matrix4 = Matrix4.identity(); + matrix4.scale(rate,rate,0.0); + return _digitalPathMap[value.toString()]!.transform(matrix4.storage); + } + + + Path combineAll(List paths, + {PathOperation operation = PathOperation.union}) { + if (paths.isEmpty) return Path(); + if (paths.length <= 1) return paths.first; + Path result = paths.first; + for (int i = 1; i < paths.length; i++) { + result = Path.combine(operation, paths[i], result); + } + return result; + } + + void _initDigitalPathMap(){ + Map map = {}; + + _digitalPathMap.clear(); + double strokeWidth = 26; + double width = 104; + double height = 169; + double gap = 5; + double angle = 43; + + Path path1 = Path() + ..moveTo(gap, 0) + ..relativeLineTo(width-gap*2 , 0) + ..relativeLineTo(-strokeWidth/tan(angle*pi/180), strokeWidth) + ..lineTo(strokeWidth/tan(angle*pi/180)+gap, strokeWidth)..close(); + + Path path2 = Path() + ..moveTo(0, 2) + ..lineTo(0 , 74) + ..lineTo(13 , 83) + ..lineTo(26 , 71) + ..lineTo(26 , 27) + ..close(); + + Matrix4 mirrorY = Matrix4.identity(); + mirrorY.translate(width/2,0.0); + mirrorY.scale(-1.0,1.0,0.0); + mirrorY.translate(-width/2,0.0); + Path path4 = path2.transform(mirrorY.storage); + + Matrix4 mirrorX = Matrix4.identity(); + mirrorX.translate(0.0,height/2); + mirrorX.scale(1.0,-1.0,0.0); + mirrorX.translate(0.0,-height/2); + + Path path5 = path2.transform(mirrorX.storage); + Path path7 = path5.transform(mirrorY.storage); + Path path6 = path1.transform(mirrorX.storage); + + Path path3 = Path() + ..moveTo(18, 84) + ..lineTo(31 , 97) + ..lineTo(73 , 97) + ..lineTo(86 , 85) + ..lineTo(75 , 74) + ..lineTo(31 , 74) + ..close(); + + map[1] = path1; + map[2] = path2; + map[3] = path3; + map[4] = path4; + map[5] = path5; + map[6] = path6; + map[7] = path7; + + digitalMap.forEach((key, v) { + List paths = v.map((value) => map[value]!).toList(); + Path path = combineAll(paths); + _digitalPathMap[key] = path; + }); + } + + +} + +Map> digitalMap = { + '0': [1,2,4,5,6,7], + '1': [4,7], + '2': [1,4,3,5,6], + '3': [1,4,3,6,7], + '4': [2,3,4,7], + '5': [1,2,3,6,7], + '6': [1,2,3,5,6,7], + '7': [1,4,7], + '8': [1,2,3,4,5,6,7], + '9': [1,2,3,4,6,7], +}; \ No newline at end of file diff --git a/modules/painting_system/draw_system/lib/src/base/digital/digital_shower.dart b/modules/painting_system/draw_system/lib/src/base/digital/digital_shower.dart new file mode 100644 index 000000000..41923fdb4 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/base/digital/digital_shower.dart @@ -0,0 +1,39 @@ + +import 'package:flutter/material.dart'; +import 'digital_path.dart'; +import 'digital_widget.dart'; + +class DigitalShower extends StatefulWidget { + const DigitalShower({Key? key}) : super(key: key); + + @override + State createState() => _DigitalShowerState(); +} + +class _DigitalShowerState extends State { + int _count = 0; + + final DigitalPath digitalPath = DigitalPath(); + + @override + Widget build(BuildContext context) { + return Scaffold( + floatingActionButton: FloatingActionButton( + child: const Icon(Icons.add), + onPressed: () { + _count++; + setState(() {}); + }, + ), + body: Center( + child: MultiDigitalWidget( + colors: [Colors.indigo,], + width: 50, + spacing: 16, + count: 4, + value: _count, + ) + ), + ); + } +} diff --git a/modules/painting_system/draw_system/lib/src/base/digital/digital_widget.dart b/modules/painting_system/draw_system/lib/src/base/digital/digital_widget.dart new file mode 100644 index 000000000..1b77bf8ce --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/base/digital/digital_widget.dart @@ -0,0 +1,82 @@ +import 'package:flutter/material.dart'; + +import 'digital_painter.dart'; +import 'digital_path.dart'; +import 'dart:math' as math; + +class SingleDigitalWidget extends StatelessWidget { + final double width; + final Color color; + final int value; + final DigitalPath digitalPath; + + SingleDigitalWidget( + {Key? key, + required this.width, + required this.value, + DigitalPath? digitalPath, + this.color = Colors.black}) + : digitalPath = digitalPath ?? DigitalPath(), + super(key: key); + + @override + Widget build(BuildContext context) { + return CustomPaint( + size: Size(width, width * DigitalPath.kDigitalRate), + painter: DigitalPainter( + color: color, + value: value, + digitalPath: digitalPath, + ), + ); + } +} + +// 展示若干位数字 +class MultiDigitalWidget extends StatelessWidget { + final int count; + final int value; + final DigitalPath digitalPath; + final double spacing; + final double runSpacing; + final double width; + final List colors; + + MultiDigitalWidget({ + Key? key, + required this.count, + required this.value, + this.spacing = 26, + this.runSpacing = 26, + required this.width, + this.colors = const [], + DigitalPath? digitalPath, + }) : digitalPath = digitalPath ?? DigitalPath(), + super(key: key); + + @override + Widget build(BuildContext context) { + int max = math.pow(10, count).toInt(); + String numStr = (value % max).toString().padLeft(count, "0"); + + Color color = Colors.black; + + return Wrap( + spacing: spacing, + runSpacing: runSpacing, + children: List.generate( + count, + (index) { + if(index colors = [ + final List colors = const[ Color(0xFFF60C0C), Color(0xFFF3B913), Color(0xFFE7F716), @@ -77,7 +76,7 @@ class PaperPainter extends CustomPainter { var pos = [1.0 / 7, 2.0 / 7, 3.0 / 7, 4.0 / 7, 5.0 / 7, 6.0 / 7, 1.0]; paint.shader = ui.Gradient.linear( - Offset(0, 0), Offset(100, 0), colors, pos, TileMode.mirror); + const Offset(0, 0), const Offset(100, 0), colors, pos, TileMode.mirror); Offset p1 = points[0]; Path path = Path()..moveTo(p1.dx, p1.dy); diff --git a/lib/painter_system/base/draw_picture.dart b/modules/painting_system/draw_system/lib/src/base/draw_picture.dart similarity index 83% rename from lib/painter_system/base/draw_picture.dart rename to modules/painting_system/draw_system/lib/src/base/draw_picture.dart index 55ab1ca46..e05191493 100644 --- a/lib/painter_system/base/draw_picture.dart +++ b/modules/painting_system/draw_system/lib/src/base/draw_picture.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'dart:ui' as ui; @@ -10,12 +11,14 @@ import 'package:flutter/services.dart'; /// 说明: class DrawPicture extends StatefulWidget { + const DrawPicture({Key? key}) : super(key: key); + @override _DrawPictureState createState() => _DrawPictureState(); } class _DrawPictureState extends State { - ui.Image _image; + ui.Image? _image; @override void initState() { @@ -33,7 +36,7 @@ class _DrawPictureState extends State { ByteData data = await rootBundle.load(path); List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); - return decodeImageFromList(bytes); + return decodeImageFromList(Uint8List.fromList(bytes)); } @override @@ -44,12 +47,12 @@ class _DrawPictureState extends State { } class PaperPainter extends CustomPainter { - Paint _paint; + final Paint _paint; final double strokeWidth = 0.5; final Color color = Colors.blue; - final ui.Image image; + final ui.Image? image; PaperPainter(this.image) : _paint = Paint() @@ -64,7 +67,7 @@ class PaperPainter extends CustomPainter { } void _drawLine(Size size, Canvas canvas) { - _paint..color = const Color(0xFFF0F0F0); + _paint.color = const Color(0xFFF0F0F0); double step = 10.0; for (int i = 1; i <= size.height / step; i++) { canvas.drawLine(Offset(step * i, 0), Offset(0, step * i), _paint); @@ -76,11 +79,11 @@ class PaperPainter extends CustomPainter { void _drawImage(Canvas canvas, Size size) { if (image != null) { canvas.drawImageRect( - image, + image!, Rect.fromCenter( - center: Offset(image.width / 2, image.height / 2), - width: image.width * 1.0, - height: image.width * 1.0), + center: Offset(image!.width / 2, image!.height / 2), + width: image!.width * 1.0, + height: image!.width * 1.0), Rect.fromLTRB(0, 0, size.width, size.height), _paint); } diff --git a/modules/painting_system/draw_system/lib/src/base/n_side/n_side_page.dart b/modules/painting_system/draw_system/lib/src/base/n_side/n_side_page.dart new file mode 100644 index 000000000..6f6e8415b --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/base/n_side/n_side_page.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; + +import 'shape_painter.dart'; + +class NSidePage extends StatefulWidget { + final int count; + + const NSidePage({Key? key, this.count = 9}) : super(key: key); + + @override + State createState() => _NSidePageState(); +} + +class _NSidePageState extends State { + int _count = 0; + + @override + void initState() { + super.initState(); + _count = widget.count; + } + + @override + Widget build(BuildContext context) { + return Stack( + fit: StackFit.expand, + children: [ + CustomPaint( + painter: ShapePainter(_count), + size: const Size(200, 200), + ), + Positioned( + left: 0, + top: 0, + child: IconButton( + onPressed: () { + setState(() { + if(_count<=3)return; + _count--; + }); + }, + icon: const Icon(Icons.remove), + )), + Positioned( + right: 0, + top: 0, + child: IconButton( + onPressed: () { + setState(() { + _count++; + }); + }, + icon: const Icon(Icons.add), + )) + ], + ); + } +} diff --git a/modules/painting_system/draw_system/lib/src/base/n_side/shape_painter.dart b/modules/painting_system/draw_system/lib/src/base/n_side/shape_painter.dart new file mode 100644 index 000000000..f0587d3c9 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/base/n_side/shape_painter.dart @@ -0,0 +1,121 @@ +import 'dart:math'; + +import 'package:dash_painter/dash_painter.dart'; +import 'package:flutter/material.dart'; + +class ShapePainter extends CustomPainter { + final int n; + + ShapePainter(this.n); + + @override + void paint(Canvas canvas, Size size) { + canvas.translate(size.width / 2, size.height / 2); + int count = n; + double radius = 140 / 2; + drawAxis(canvas, size); + List points = []; + double rotate = -pi / 2; + for (int i = 0; i < count; i++) { + double perRad = 2 * pi / count * i; + points.add(Offset( + radius * cos(perRad + rotate), + radius * sin(perRad + rotate), + )); + } + _drawShape(canvas, points); + _drawShapeHelper(canvas, points, radius); + } + + final Paint shapePaint = Paint()..style = PaintingStyle.stroke..color=Colors.blue..strokeWidth=2; + + void _drawShape(Canvas canvas, List points) { + Path shapePath = Path(); + shapePath.moveTo(points[0].dx, points[0].dy); + for (int i = 1; i < points.length; i++) { + shapePath.lineTo(points[i].dx, points[i].dy); + } + shapePath.close(); + canvas.drawPath(shapePath, shapePaint); + } + + void _drawShapeHelper(Canvas canvas, List points, double radius) { + Path helpPath = Path() + ..addOval(Rect.fromCenter( + center: Offset.zero, width: radius * 2, height: radius * 2)); + dashPainter.paint(canvas, helpPath, helpPaint); + for (Offset point in points) { + drawAnchor(canvas, point); + } + } + + final Paint pointPaint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 1; + + void drawAnchor(Canvas canvas, Offset offset) { + canvas.drawCircle(offset, 4, pointPaint..style = PaintingStyle.stroke); + canvas.drawCircle(offset, 2, pointPaint..style = PaintingStyle.fill); + } + + @override + bool shouldRepaint(covariant ShapePainter oldDelegate) { + return oldDelegate.n != n; + } + + final Paint helpPaint = Paint() + ..style = PaintingStyle.stroke + ..color = Colors.grey + ..strokeWidth = 1; + + final DashPainter dashPainter = const DashPainter(span: 4, step: 4); + + Paint axisPaint = Paint()..color = Colors.black; + + void drawAxis(Canvas canvas, Size size) { + axisPaint.style = PaintingStyle.stroke; + Path helpPath = Path() + ..moveTo(-size.width / 2, 0) + ..relativeLineTo(size.width, 0) + ..moveTo(0, -size.height / 2) + ..relativeLineTo(0, size.height); + dashPainter.paint(canvas, helpPath, axisPaint); + + axisPaint.style = PaintingStyle.fill; + Path arrowXPath = Path(); + arrowXPath.moveTo(size.width / 2, 0); + arrowXPath.relativeLineTo(-8, 4); + arrowXPath.relativeLineTo(0, -8); + arrowXPath.close(); + canvas.drawPath(arrowXPath, axisPaint); + + Path arrowYPath = Path(); + arrowYPath.moveTo(0, size.height / 2); + arrowYPath.relativeLineTo(4, -8); + arrowYPath.relativeLineTo(-8, 0); + arrowYPath.close(); + canvas.drawPath(arrowYPath, axisPaint); + + drawHelpText("x", canvas, Offset(size.width / 2 - 8, 2)); + drawHelpText("y", canvas, Offset(6, size.height / 2 - 15)); + } + + final TextPainter textPainter = TextPainter( + textAlign: TextAlign.center, + textDirection: TextDirection.ltr, + ); + + void drawHelpText( + String text, + Canvas canvas, + Offset offset, { + Color color = Colors.black, + }) { + textPainter.text = TextSpan( + text: text, + style: TextStyle(fontSize: 11, color: color,fontWeight: FontWeight.bold), + ); + textPainter.layout(maxWidth: 200); + textPainter.paint(canvas, offset); + } +} diff --git a/modules/painting_system/draw_system/lib/src/base/polar/angle_painter.dart b/modules/painting_system/draw_system/lib/src/base/polar/angle_painter.dart new file mode 100644 index 000000000..b07261fb8 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/base/polar/angle_painter.dart @@ -0,0 +1,86 @@ +import 'dart:math'; + +import 'package:dash_painter/dash_painter.dart'; +import 'package:flutter/material.dart'; + +import 'polar.dart'; + +class PolarPainter extends CustomPainter { + final DashPainter dashPainter = const DashPainter(span: 4, step: 4); + + final Paint helpPaint = Paint() + ..style = PaintingStyle.stroke + ..color = Colors.grey + ..strokeWidth = 1; + + Polar2D p1 = Polar2D(pi / 4, 75); + + final TextPainter textPainter = TextPainter( + textAlign: TextAlign.center, + textDirection: TextDirection.ltr, + ); + + + @override + void paint(Canvas canvas, Size size) { + Paint paint = Paint()..color=Colors.blue; + canvas.translate(size.width / 2, size.height / 2); + drawHelp(canvas, size); + collect().forEach((Polar2D point) { + canvas.drawCircle(point.offset, 1, paint); + }); + } + + List collect() { + List points = []; + for (int i = 0; i < 360; i++) { + double rad = i * pi / 180; + double length = 100 * cos(4 * rad); + points.add(Polar2D(rad, length)); + } + return points; + } + + void drawHelp(Canvas canvas, Size size) { + Path helpPath = Path() + ..moveTo(-size.width / 2, 0) + ..relativeLineTo(size.width, 0) + ..moveTo(0, -size.height / 2) + ..relativeLineTo(0, size.height); + + for (int i = 0; i < 5; i++) { + helpPath.addOval(Rect.fromCenter(center: Offset.zero, width: 50.0 * i, height: 50.0 * i)); + drawHelpText('${25 * i}', canvas, Offset(25.0 * i - 10, 0)); + } + + dashPainter.paint(canvas, helpPath, helpPaint); + + drawHelpText('90°', canvas, Offset(0 - 8, size.height*0.85 / 2), + color: Colors.black); + drawHelpText('180°', canvas, Offset(-size.width*0.8 / 2 - 30, 0 - 8), + color: Colors.black); + drawHelpText('270°', canvas, Offset(0 - 8, -size.height*0.85 / 2 - 16), + color: Colors.black); + drawHelpText('0°', canvas, Offset(size.width*0.85 / 2 + 10, 0 - 8), + color: Colors.black); + } + + void drawHelpText( + String text, + Canvas canvas, + Offset offset, { + Color color = Colors.black, + }) { + textPainter.text = TextSpan( + text: text, + style: TextStyle(fontSize: 11, color: color,fontWeight: FontWeight.bold), + ); + textPainter.layout(maxWidth: 200); + textPainter.paint(canvas, offset); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; + } +} diff --git a/modules/painting_system/draw_system/lib/src/base/polar/polar.dart b/modules/painting_system/draw_system/lib/src/base/polar/polar.dart new file mode 100644 index 000000000..93fc375da --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/base/polar/polar.dart @@ -0,0 +1,17 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; + +class Polar2D { + final double rad; + final double length; + + final Offset coo; + + Polar2D(this.rad, this.length,{this.coo=Offset.zero}); + + Offset get offset => Offset( + cos(rad) * length-coo.dx, + sin(rad) * length-coo.dy, + ); +} diff --git a/modules/painting_system/draw_system/lib/src/base/polar/polar_painter_widget.dart b/modules/painting_system/draw_system/lib/src/base/polar/polar_painter_widget.dart new file mode 100644 index 000000000..c5ce74f42 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/base/polar/polar_painter_widget.dart @@ -0,0 +1,15 @@ + +import 'package:flutter/material.dart'; +import 'angle_painter.dart'; + +class PolarPainterWidget extends StatelessWidget { + const PolarPainterWidget({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return CustomPaint( + painter: PolarPainter(), + size: const Size(200,200), + ); + } +} diff --git a/modules/painting_system/draw_system/lib/src/base/windmill.dart b/modules/painting_system/draw_system/lib/src/base/windmill.dart new file mode 100644 index 000000000..75de225f1 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/base/windmill.dart @@ -0,0 +1,92 @@ + +import 'dart:math'; + +import 'package:flutter/material.dart'; + +class WindmillWidget extends StatefulWidget { + const WindmillWidget({Key? key}) : super(key: key); + + @override + State createState() => _WindmillWidgetState(); +} + +class _WindmillWidgetState extends State + with SingleTickerProviderStateMixin { + late final AnimationController _ctrl = AnimationController( + vsync: this, + duration: Duration(seconds: (3 * 1.5).toInt()), + ); + late Animation rotate; + + @override + void initState() { + rotate = Tween(begin: 0, end: 3 * 2 * pi) + .animate(CurveTween(curve: Curves.easeIn).animate(_ctrl)); + super.initState(); + } + + @override + void dispose() { + _ctrl.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: () async { + await _ctrl.forward(from: 0); + }, + child: CustomPaint( + size: const Size(200, 200), + painter: WindmillPainter(rotate), + ), + ); + } +} + +const List kColors = [ + Color(0xffE74437), + Color(0xffFBBD19), + Color(0xff3482F0), + Color(0xff30A04C) +]; + +class WindmillPainter extends CustomPainter { + final Animation rotate; + + WindmillPainter(this.rotate) : super(repaint: rotate); + + @override + void paint(Canvas canvas, Size size) { + + canvas.translate(size.width / 2, size.height / 2); + double d = size.width * 0.4; + canvas.rotate(rotate.value); + Paint paint = Paint(); + for (Color color in kColors) { + Path path1 = Path() + ..moveTo(0, -d * 46 / 203) + ..lineTo(0, -d * 203 / 203) + ..lineTo(102 / 203 * d, -102 / 203 * d) + ..lineTo(12 / 203 * d, -12 / 203 * d) + ..close(); + canvas.drawPath(path1, paint..color = color); + + Path path2 = Path() + ..moveTo(12 / 203 * d, -12 / 203 * d) + ..lineTo(102 / 203 * d, -102 / 203 * d) + ..lineTo(102 / 203 * d, 0) + ..lineTo(46 / 203 * d, 0) + ..close(); + canvas.drawPath(path2, paint..color = color.withOpacity(0.2)); + canvas.rotate(pi / 2); + } + } + + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; + } +} \ No newline at end of file diff --git a/modules/painting_system/draw_system/lib/src/bloc/gallery_unit/bloc.dart b/modules/painting_system/draw_system/lib/src/bloc/gallery_unit/bloc.dart new file mode 100644 index 000000000..6e124b9b5 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/bloc/gallery_unit/bloc.dart @@ -0,0 +1,11 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class GalleryUnitBloc extends Cubit{ + GalleryUnitBloc() : super(''); + + void loadGalleryInfo() async{ + String result = await rootBundle.loadString('assets/data/gallery_info.json'); + emit(result); + } +} \ No newline at end of file diff --git a/modules/painting_system/draw_system/lib/src/desk_ui/desk_frame.dart b/modules/painting_system/draw_system/lib/src/desk_ui/desk_frame.dart new file mode 100644 index 000000000..143c8dbfb --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/desk_ui/desk_frame.dart @@ -0,0 +1,83 @@ +import 'package:flutter/material.dart'; + +import '../picture_frame.dart'; +import 'package:l10n/l10n.dart'; + +/// create by 张风捷特烈 on 2020/12/4 +/// contact me by email 1981462002@qq.com +/// 说明: + +class DeskFrameShower extends StatelessWidget { + final String title; + final String author; + final String srcUrl; + final String info; + final Widget content; + + const DeskFrameShower( + {Key? key, + this.title = "", + this.author = "", + this.srcUrl = "", + this.info = "", + required this.content}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + Expanded( + child: Align( + alignment: const Alignment(0,-0.7), + child: Wrap( + direction: Axis.vertical, + spacing: 5, + children: [ + Text( + title, + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + Text( + "作者: $author ", + style: const TextStyle(fontSize: 12, fontWeight: FontWeight.bold), + ), + Text( + "${context.l10n.srcPath} ", + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.bold, + color: Colors.blueAccent), + ), + ], + ), + ), + ), + + PictureFrame( + width: MediaQuery.of(context).size.shortestSide*0.6, + height: MediaQuery.of(context).size.shortestSide*0.6, + child: content, + ), + + Expanded( + child: Column( + children: [ + const Spacer(flex: 70,), + Padding( + padding: const EdgeInsets.all(20.0), + child: Text( + info, + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold,color: Colors.grey), + ), + ), + const Spacer(flex: 20,), + + ], + ), + ), + ], + ); + } +} diff --git a/modules/painting_system/draw_system/lib/src/desk_ui/desk_gallery_unit.dart b/modules/painting_system/draw_system/lib/src/desk_ui/desk_gallery_unit.dart new file mode 100644 index 000000000..fef1eb291 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/desk_ui/desk_gallery_unit.dart @@ -0,0 +1,102 @@ +import 'dart:convert'; + +import 'package:app/app.dart'; +import 'package:components/components.dart'; +import 'package:flutter/material.dart'; +import 'package:l10n/l10n.dart'; + +import 'package:toly_ui/toly_ui.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 +/// 说明: + +class DeskGalleryUnit extends StatefulWidget { + const DeskGalleryUnit({Key? key}) : super(key: key); + + @override + _DeskGalleryUnitState createState() => _DeskGalleryUnitState(); +} + +class _DeskGalleryUnitState extends State { + final ValueNotifier factor = ValueNotifier(0); + + @override + void dispose() { + factor.dispose(); + super.dispose(); + } + + final ScrollController controller = ScrollController(); + + Color get color => Colors.blue; + + Color get nextColor =>Colors.orangeAccent; + + BoxDecoration get boxDecoration => const BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + ), + ); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Column( + children: [ + SimpleDeskTopBar( + leading: Text( + 'Flutter ${context.l10n.paintCollection}', + style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), + ), + ), + Expanded(child: _buildContent()), + ], + ), + ); + } + + Widget _buildContent() { + final List widgets = + (json.decode(StrUnit.galleryDesc(context)) as List).map((e) { + GalleryInfo info = GalleryInfo.fromJson(e); + List children = GalleryFactory.getGalleryByName(info.type,context); + + return FeedbackWidget( + a: 0.95, + onPressed: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (ctx) => GalleryDetailPage( + galleryInfo: info, + children: children, + ))); + }, + child: GalleryCardItem( + galleryInfo: info, + count: children.length, + ), + ); + }).toList(); + + SliverGridDelegate gridDelegate = + const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 320, + mainAxisSpacing: 8, + mainAxisExtent: 340, + crossAxisSpacing: 8, + ); + + return GridView.builder( + controller: controller, + gridDelegate: gridDelegate, + padding: const EdgeInsets.all(12), + itemCount: widgets.length, + itemBuilder: (ctx, index) => widgets[index]); + } +} diff --git a/modules/painting_system/draw_system/lib/src/fun/bufeng/bufeng_panel.dart b/modules/painting_system/draw_system/lib/src/fun/bufeng/bufeng_panel.dart new file mode 100644 index 000000000..3a5341584 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/fun/bufeng/bufeng_panel.dart @@ -0,0 +1,65 @@ +import 'package:flutter/material.dart'; +import 'painter.dart'; +import 'config.dart'; + + +class BufengPanel extends StatefulWidget { + const BufengPanel({Key? key}) : super(key: key); + + @override + State createState() => _BufengPanelState(); +} + +class _BufengPanelState extends State { + final Config config = Config( + size: const Size(200, 200), + ); + + void addNeedle({int count = 1}) { + for (int i = 0; i < count; i++) { + config.addNeedle(); + } + } + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.topCenter, + children: [ + Padding( + padding: const EdgeInsets.only(top: 10), + child: CustomPaint( + painter: PiPainter(config), + size: config.size, + ), + ), + Positioned( + right: 0, + top: 10, + child: Row( + children: [ + IconButton(onPressed: addNeedle, icon: const Icon(Icons.add)), + IconButton(onPressed: () => addNeedle(count: 100), icon: const Icon(Icons.add_chart)), + IconButton(onPressed: config.clear, icon: const Icon(Icons.refresh)), + ], + ), + ) + ], + ); + + // Scaffold( + // appBar: AppBar( + // title: const Text('蒲丰投针试验'), + // actions: + // [ + // IconButton(onPressed: addNeedle, icon: const Icon(Icons.add)), + // IconButton(onPressed: () => addNeedle(count: 100), icon: const Icon(Icons.add_chart)), + // IconButton(onPressed: config.clear, icon: const Icon(Icons.refresh)), + // ], + // ), + // body: Center( + // child: , + // ), + // ); + } +} diff --git a/modules/painting_system/draw_system/lib/src/fun/bufeng/config.dart b/modules/painting_system/draw_system/lib/src/fun/bufeng/config.dart new file mode 100644 index 000000000..c5dcf9c0c --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/fun/bufeng/config.dart @@ -0,0 +1,67 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; + +class Line { + final Offset p0; + final Offset p1; + + Line({ + required this.p0, + required this.p1, + }); + + bool isActive(double gap) { + int gapIndex0 = p0.dy ~/ gap; + int gapIndex1 = p1.dy < 0 ? -1 : p1.dy ~/ gap; + return gapIndex0 != gapIndex1 || + p0.dy % gap < 0.000000001 || + p1.dy % gap < 0.000000001; + } +} + +class Config extends ChangeNotifier { + int lineCount; + Size size; + List lines = []; + + Config({ + this.lineCount = 8, + this.size = const Size(200, 200), + }); + + double get gap => size.height / lineCount; + + @override + String toString() { + int n = lines.length; + int m = lines.where((line) => line.isActive(gap)).length; + double parserPi = m == 0 ? 0 : 2 * n / m; + return '投针个数: ${lines.length}\n命中个数: $m \n' + '估算圆周率 : $parserPi'; + } + + void addNeedle() { + Line line = _createNeedle(); + lines.add(line); + notifyListeners(); + } + + final Random _random = Random(); + + Line _createNeedle() { + double x = _random.nextDouble() * size.width; + double y = _random.nextDouble() * size.height; + + double rad = 2 * pi * _random.nextDouble(); + double dx = gap * cos(rad); + double dy = gap * sin(rad); + Line line = Line(p0: Offset(x, y), p1: Offset(x + dx, y + dy)); + return line; + } + + void clear() { + lines.clear(); + notifyListeners(); + } +} diff --git a/modules/painting_system/draw_system/lib/src/fun/bufeng/painter.dart b/modules/painting_system/draw_system/lib/src/fun/bufeng/painter.dart new file mode 100644 index 000000000..3abe7fba7 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/fun/bufeng/painter.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'config.dart'; + +class PiPainter extends CustomPainter { + final Config config; + + PiPainter(this.config) : super(repaint: config); + + @override + void paint(Canvas canvas, Size size) { + print(size); + Paint paint = Paint(); + paint.style = PaintingStyle.stroke; + final double span = config.gap; + drawHelpText( + '$config', + canvas, + Offset.zero, + ); + canvas.translate(0, size.height*0.3); + + canvas.save(); + for (int i = 0; i <= config.lineCount; i++) { + canvas.drawLine(Offset.zero, Offset(size.width, 0), paint); + print(span*i); + canvas.translate(0, span); + } + canvas.restore(); + + Paint needlePaint = Paint(); + needlePaint.style = PaintingStyle.stroke; + needlePaint.strokeWidth = 1; + + for (Line line in config.lines) { + if (line.isActive(config.gap)) { + needlePaint.color = Colors.red; + } else { + needlePaint.color = Colors.green; + } + canvas.drawLine(line.p0, line.p1, needlePaint); + } + + + } + + @override + bool shouldRepaint(covariant PiPainter oldDelegate) { + return config != oldDelegate.config; + } + + final TextPainter textPainter = TextPainter( + textAlign: TextAlign.start, + textDirection: TextDirection.ltr, + ); + + void drawHelpText( + String text, + Canvas canvas, + Offset offset, { + Color color = Colors.lightBlue, + }) { + textPainter.text = TextSpan( + text: text, + style: TextStyle(fontSize: 14, color: color), + ); + textPainter.layout(maxWidth: 250); + textPainter.paint(canvas, offset); + } +} diff --git a/modules/painting_system/draw_system/lib/src/fun/dundun_view.dart b/modules/painting_system/draw_system/lib/src/fun/dundun_view.dart new file mode 100644 index 000000000..28759c62c --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/fun/dundun_view.dart @@ -0,0 +1,449 @@ +import 'dart:math'; +import 'dart:ui' as ui; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class DunDunView extends StatefulWidget { + const DunDunView({Key? key}) : super(key: key); + + @override + _DunDunViewState createState() => _DunDunViewState(); +} + +class _DunDunViewState extends State { + ui.Image? image; + + @override + void initState() { + super.initState(); + loadImage(); + } + + void loadImage() async { + ByteData data2 = await rootBundle.load('assets/images/logo1.webp'); + image = await decodeImageFromList(data2.buffer.asUint8List()); + if (mounted) setState(() {}); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: CustomPaint( + painter: DunDunPainter( + image, + ), + size: const Size(200, 200), + ), + ), + ); + } +} + +class DunDunPainter extends CustomPainter { + final ui.Image? image; + + DunDunPainter(this.image); + + final Paint helpPaint = Paint() + ..style = PaintingStyle.stroke + ..color = Colors.blue; + final Paint pathPaint = Paint()..style = PaintingStyle.stroke; + + @override + void paint(Canvas canvas, Size size) { + + canvas.translate(30, 80); + helpPaint.color = Colors.red; + + pathPaint.style = PaintingStyle.fill; + + Path leftHandPath = buildLeftHandPath(); + pathPaint.color = Colors.black; + canvas.drawPath(leftHandPath, pathPaint); + + Path erPath = buildErPath(); + canvas.drawPath(erPath, pathPaint); + + Path rightHandPath = buildRightHandPath(); + pathPaint.color = Colors.black; + canvas.drawPath(rightHandPath, pathPaint); + + pathPaint.style = PaintingStyle.fill; + pathPaint.color = const Color(0xffF1F4F7); + + Path bodyPath = buildBodyPath(); + canvas.drawPath(bodyPath, pathPaint); + + canvas.save(); + Path eyePath = Path(); + Matrix4 m = Matrix4.translationValues(46, -12, 0) + .multiplied(Matrix4.rotationZ(45 / 180 * pi)); + eyePath + .addOval(Rect.fromCenter(center: const Offset(0, 0), width: 32, height: 49)); + eyePath = eyePath.transform(m.storage); + pathPaint.color = Colors.black; + canvas.drawPath(eyePath, pathPaint); + canvas.restore(); + + Path nosePath = Path(); + nosePath.moveTo(79, -0); + nosePath.relativeLineTo(12, -12); + nosePath.relativeLineTo(-24, 0); + nosePath.close(); + Path clipCirclePath = Path(); + clipCirclePath.addOval( + Rect.fromCenter(center: const Offset(79, -10), width: 12, height: 12)); + + nosePath = Path.combine(PathOperation.intersect, nosePath, clipCirclePath); + pathPaint.style = PaintingStyle.fill; + pathPaint.color = Colors.black; + canvas.drawPath(nosePath, pathPaint); + + Path smaliPath = Path(); + smaliPath.moveTo(65, -0); + + smaliPath.quadraticBezierTo(78, 15, 90, 0); + smaliPath.quadraticBezierTo(78, 6, 65, 0); + pathPaint.color = Colors.red; + canvas.drawPath(smaliPath, pathPaint); + canvas.drawPath( + smaliPath, + pathPaint + ..style = PaintingStyle.stroke + ..strokeWidth = 1 + ..color = Colors.black); + + Paint colorfulPaint = Paint()..style = PaintingStyle.stroke; + List colors = const [ + Color(0xFFF60C0C), + Color(0xFFF3B913), + Color(0xFFE7F716), + Color(0xFF3DF30B), + Color(0xFF0DF6EF), + Color(0xFF0829FB), + Color(0xFFB709F4), + ]; + final List pos = + List.generate(colors.length, (index) => index / colors.length); + colorfulPaint.shader = ui.Gradient.sweep( + const Offset(60, -5), colors, pos, TileMode.clamp, 0, 2 * pi); + colorfulPaint.maskFilter = const MaskFilter.blur(BlurStyle.solid, 2); + + Path colorfulPath = Path(); + colorfulPath.addOval( + Rect.fromCenter(center: const Offset(72, -5), width: 120, height: 110)); + colorfulPath.addOval( + Rect.fromCenter(center: const Offset(72, -5), width: 110, height: 100)); + colorfulPath.addOval( + Rect.fromCenter(center: const Offset(72, -5), width: 115, height: 110)); + colorfulPath.addOval( + Rect.fromCenter(center: const Offset(72, -5), width: 120, height: 105)); + colorfulPath.addOval( + Rect.fromCenter(center: const Offset(72, -5), width: 115, height: 105)); + colorfulPath.addOval( + Rect.fromCenter(center: const Offset(72, -5), width: 117, height: 103)); + canvas.drawPath(colorfulPath, colorfulPaint); + + canvas.save(); + Path eyePath2 = Path(); + Matrix4 m2 = Matrix4.translationValues(105, -12, 0) + .multiplied(Matrix4.rotationZ(-40 / 180 * pi)); + eyePath2 + .addOval(Rect.fromCenter(center: const Offset(0, 0), width: 29, height: 48)); + eyePath2 = eyePath2.transform(m2.storage); + pathPaint.style = PaintingStyle.fill; + pathPaint.color = Colors.black; + canvas.drawPath(eyePath2, pathPaint); + + canvas.restore(); + Path rightEyePath = Path(); + rightEyePath.addOval( + Rect.fromCenter(center: const Offset(98, -14), width: 17, height: 17)); + pathPaint.style = PaintingStyle.stroke; + pathPaint.color = Colors.white; + canvas.drawPath(rightEyePath, pathPaint..strokeWidth = 2); + + Path rightEyePath2 = Path(); + rightEyePath2 + .addOval(Rect.fromCenter(center: const Offset(98, -14), width: 7, height: 7)); + pathPaint.style = PaintingStyle.fill; + pathPaint.color = Colors.white.withOpacity(0.4); + canvas.drawPath(rightEyePath2, pathPaint); + + Path rightEyePath3 = Path(); + rightEyePath3 + .addOval(Rect.fromCenter(center: const Offset(98, -19), width: 4, height: 4)); + pathPaint.style = PaintingStyle.fill; + pathPaint.color = Colors.white; + canvas.drawPath(rightEyePath3, pathPaint); + + Path leftEyePath = Path(); + leftEyePath.addOval( + Rect.fromCenter(center: const Offset(50, -13), width: 18, height: 18)); + pathPaint.style = PaintingStyle.stroke; + pathPaint.color = Colors.white; + canvas.drawPath(leftEyePath, pathPaint..strokeWidth = 2); + + Path leftEyePath2 = Path(); + leftEyePath2 + .addOval(Rect.fromCenter(center: const Offset(50, -13), width: 7, height: 7)); + pathPaint.style = PaintingStyle.fill; + pathPaint.color = Colors.white.withOpacity(0.4); + canvas.drawPath(leftEyePath2, pathPaint); + + Path leftEyePath3 = Path(); + leftEyePath3 + .addOval(Rect.fromCenter(center: const Offset(51, -19), width: 4, height: 4)); + pathPaint.style = PaintingStyle.fill; + pathPaint.color = Colors.white; + canvas.drawPath(leftEyePath3, pathPaint); + + Path leftFootPath = buildFootPath(); + pathPaint.style = PaintingStyle.fill; + pathPaint.color = Colors.black; + canvas.drawPath(leftFootPath, pathPaint); + + //爱心 + List arr = []; + int len = 50; + double a = 1; + for (int i = 0; i < len; i++) { + double step = (i / len) * (pi * 2); //递增的θ + Offset offset = Offset( + a * (11 * pow(sin(step), 3)).toDouble(), + -a * + (9 * cos(step) - + 5 * cos(2 * step) - + 2 * cos(3 * step) - + cos(4 * step)), + ); + arr.add(offset); + } + + Path starPath = Path(); + for (int i = 0; i < len; i++) { + starPath.lineTo(arr[i].dx, arr[i].dy); + } + pathPaint.color = Colors.red; + starPath = starPath.shift(const Offset(152, -20)); + canvas.drawPath(starPath, pathPaint); + if (image != null) { + Rect src2 = Rect.fromLTWH( + 0, 0, image!.width.toDouble(), image!.height.toDouble()); + Rect dst2 = const Rect.fromLTWH(50, 55, 899 / 28, 1066 / 28); + + canvas.drawImageRect(image!, src2, dst2, Paint()); + } + + Path dundunOutLine = Path.combine( + PathOperation.union, + Path.combine( + PathOperation.union, + Path.combine( + PathOperation.union, + Path.combine(PathOperation.union, bodyPath, leftFootPath), + rightHandPath), + leftHandPath), + erPath); + Paint outLinePainter = Paint() + ..style = PaintingStyle.stroke + ..color = Colors.black + ..strokeWidth = 3; + outLinePainter.maskFilter = const MaskFilter.blur(BlurStyle.outer, 4); + canvas.drawPath(dundunOutLine, outLinePainter); + + Path p2 = Path() + ..addOval(Rect.fromCenter( + center: const Offset( + 72, + -5, + ), + width: 126, + height: 116)); + + outLinePainter.maskFilter = const MaskFilter.blur(BlurStyle.outer, 4); + canvas.drawPath( + p2, + outLinePainter + ..color = Colors.black + ..strokeWidth = 2); + } + + Path buildBodyPath() { + Path path = Path(); + path.quadraticBezierTo(10, -75, 75, -75); + path.quadraticBezierTo(135, -70, 138, 0); + path.quadraticBezierTo(130, 90, 65, 98); + path.quadraticBezierTo(-5, 85, 0, 0); + return path; + } + + Path buildLeftHandPath() { + Path path = Path(); + path.quadraticBezierTo( + -17, + 14, + -28, + 40, + ); + path.quadraticBezierTo(-32, 58, -15, 62); + path.quadraticBezierTo(10, 60, 0, 0); + return path; + } + + Path buildRightHandPath() { + Path path = Path(); + path.moveTo(135, -20); + path.quadraticBezierTo(140, -48, 165, -35); + path.quadraticBezierTo(180, -17, 135, 22); + path.quadraticBezierTo(125, 17, 135, -20); + return path; + } + + Path buildFootPath() { + Path path = Path(); + path.moveTo(18, 78); + path.quadraticBezierTo(18, 100, 22, 115); + path.quadraticBezierTo(60, 125, 55, 98); + path.quadraticBezierTo(35, 80, 18, 78); + + Path right = path + .transform(Matrix4.diagonal3Values(-1, 1, 1).storage) + .shift(const Offset(128, 0)); + + return Path.combine(PathOperation.union, path, right); + } + + Path buildErPath() { + Path path = Path(); + path.moveTo(13, -40); + path.quadraticBezierTo(8, -95, 40, -68); + path.quadraticBezierTo(40, -55, 13, -40); + + Path right = path + .transform(Matrix4.diagonal3Values(-1, 1, 1).storage) + .shift(const Offset(138, -5)); + + return Path.combine(PathOperation.union, path, right); + } + + void paintBodyPoints(ui.Canvas canvas) { + helpPaint.strokeWidth = 4; + canvas.drawPoints( + ui.PointMode.points, + [ + const Offset(10, -68), + const Offset(75, -75), + + const Offset(135, -70), + const Offset(138, 0), + + const Offset(130, 90), + const Offset(65, 98), + + // Offset(55,98), + // Offset(18,78), + + const Offset(-5, 85), + const Offset(0, 0), + ], + helpPaint); + } + + void paintErPoints(ui.Canvas canvas) { + helpPaint.strokeWidth = 4; + canvas.drawPoints( + ui.PointMode.points, + [ + const Offset( + 13, + -40, + ), + const Offset( + 40, + -68, + ), + const Offset(40, -55), + const Offset( + 8, + -95, + ), + // Offset(18, 78), + // Offset(22, 115), + // Offset(55, 98), + // Offset( + // 40, + // 80, + // ), + ], + helpPaint); + } + + void paintHandsHelpPoints(ui.Canvas canvas) { + helpPaint.strokeWidth = 4; + canvas.drawPoints( + ui.PointMode.points, + [ + const Offset(-0, 0), + const Offset(-17, 14), + const Offset(-28, 40), + const Offset(-32, 58), + const Offset(-15, 62), + const Offset( + 8, + 60, + ), + const Offset(-0, 0), + ], + helpPaint); + } + + void paintRightHandsHelpPoints(ui.Canvas canvas) { + helpPaint.strokeWidth = 4; + canvas.drawPoints( + ui.PointMode.points, + [ + // Offset(10,-68), + // Offset(75,-75), + // + const Offset(140, -48), + const Offset(165, -35), + + const Offset(180, -17), + const Offset(135, 22), + + const Offset(125, 17), + const Offset(135, -20), + + // Offset(55,98), + // Offset(18,78), + ], + helpPaint); + } + + void paintLeftFoodHelpPoints(ui.Canvas canvas) { + helpPaint.strokeWidth = 4; + canvas.drawPoints( + ui.PointMode.points, + [ + const Offset( + 18, + 100, + ), + const Offset(60, 125), + const Offset(18, 78), + const Offset(22, 115), + const Offset(55, 98), + const Offset( + 40, + 80, + ), + ], + helpPaint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; +} diff --git a/lib/painter_system/fun/random_portrait.dart b/modules/painting_system/draw_system/lib/src/fun/random_portrait.dart similarity index 95% rename from lib/painter_system/fun/random_portrait.dart rename to modules/painting_system/draw_system/lib/src/fun/random_portrait.dart index 4c7dd0b76..917387a08 100644 --- a/lib/painter_system/fun/random_portrait.dart +++ b/modules/painting_system/draw_system/lib/src/fun/random_portrait.dart @@ -18,6 +18,8 @@ class Position { } class RandomPortrait extends StatefulWidget { + const RandomPortrait({Key? key}) : super(key: key); + @override _RandomPortraitState createState() => _RandomPortraitState(); } @@ -59,7 +61,7 @@ class _RandomPortraitState extends State { } class PortraitPainter extends CustomPainter { - Paint _paint; + final Paint _paint; final int blockCount; final Color color; @@ -80,9 +82,9 @@ class PortraitPainter extends CustomPainter { final double perH = (size.height - pd * 2) / (blockCount); canvas.translate(pd, pd); - positions.forEach((element) { + for (Position element in positions) { _drawBlock(perW, perH, canvas, element); - }); + } } void _drawBlock(double dW, double dH, Canvas canvas, Position position) { 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 81% rename from lib/painter_system/fun/stemp/stamp_data.dart rename to modules/painting_system/draw_system/lib/src/fun/stemp/stamp_data.dart index 9b9a21f6b..732cb0c26 100644 --- a/lib/painter_system/fun/stemp/stamp_data.dart +++ b/modules/painting_system/draw_system/lib/src/fun/stemp/stamp_data.dart @@ -106,9 +106,9 @@ class Stamp { Offset center; double radius; - Stamp({this.color = Colors.blue, this.center, this.radius = 20}); + Stamp({this.color = Colors.blue, this.center = Offset.zero, this.radius = 20}); - Path _path; + Path? _path; Path get path { if (_path == null) { @@ -116,21 +116,21 @@ class Stamp { double r = radius; double rad = 30 / 180 * pi; - _path..moveTo(center.dx, center.dy); - _path.relativeMoveTo(r * cos(rad), -r * sin(rad)); - _path.relativeLineTo(-2 * r * cos(rad), 0); - _path.relativeLineTo(r * cos(rad), r + r * sin(rad)); - _path.relativeLineTo(r * cos(rad), -(r + r * sin(rad))); + _path!.moveTo(center.dx, center.dy); + _path!.relativeMoveTo(r * cos(rad), -r * sin(rad)); + _path!.relativeLineTo(-2 * r * cos(rad), 0); + _path!.relativeLineTo(r * cos(rad), r + r * sin(rad)); + _path!.relativeLineTo(r * cos(rad), -(r + r * sin(rad))); - _path..moveTo(center.dx, center.dy); - _path.relativeMoveTo(0, -r); - _path.relativeLineTo(-r * cos(rad), r + r * sin(rad)); - _path.relativeLineTo(2 * r * cos(rad), 0); - _path.relativeLineTo(-r * cos(rad), -(r + r * sin(rad))); + _path!.moveTo(center.dx, center.dy); + _path!.relativeMoveTo(0, -r); + _path!.relativeLineTo(-r * cos(rad), r + r * sin(rad)); + _path!.relativeLineTo(2 * r * cos(rad), 0); + _path!.relativeLineTo(-r * cos(rad), -(r + r * sin(rad))); - return _path; + return _path!; } else { - return _path; + return _path!; } } 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 91% 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 3739d9469..1be90b4ad 100644 --- a/lib/painter_system/fun/stemp/stamp_paper.dart +++ b/modules/painting_system/draw_system/lib/src/fun/stemp/stamp_paper.dart @@ -5,6 +5,8 @@ import 'package:flutter/material.dart'; import 'stamp_data.dart'; class StampPaper extends StatefulWidget { + const StampPaper({Key? key}) : super(key: key); + @override _StampPaperState createState() => _StampPaperState(); } @@ -20,7 +22,7 @@ class _StampPaperState extends State bool get gameOver => gameState != GameState.doing; // 定义动画器 - AnimationController _controller; + late AnimationController _controller; final Duration animDuration = const Duration(milliseconds: 200); @override @@ -40,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, @@ -92,7 +94,7 @@ class _StampPaperState extends State gameState = stamps.checkWin(width / gridCount); if (gameState == GameState.redWin) { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( + ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content: Text("红棋获胜!"), backgroundColor: Colors.red, )); @@ -100,7 +102,7 @@ class _StampPaperState extends State if (gameState == GameState.blueWin) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text("蓝棋获胜!"), backgroundColor: Colors.blue)); + const SnackBar(content: Text("蓝棋获胜!"), backgroundColor: Colors.blue)); } } @@ -193,14 +195,14 @@ class StampPainter extends CustomPainter { ..color = Colors.white ..style = PaintingStyle.stroke; - StampPainter({this.stamps, this.count = 3}) : super(repaint: stamps); + StampPainter({required this.stamps, this.count = 3}) : super(repaint: stamps); @override void paint(Canvas canvas, Size size) { Rect zone = Offset.zero & size; canvas.clipRect(zone); - stamps.stamps.forEach((stamp) { + for (Stamp stamp in stamps.stamps) { double length = size.width / count; int x = stamp.center.dx ~/ (size.width / count); int y = stamp.center.dy ~/ (size.width / count); @@ -217,11 +219,11 @@ class StampPainter extends CustomPainter { ..color = Colors.white); canvas.drawCircle(stamp.center, stamp.radius + strokeWidth * 1.5, _pathPaint..color = stamp.color); - }); + } } @override bool shouldRepaint(covariant StampPainter oldDelegate) { - return this.stamps != oldDelegate.stamps || this.count != oldDelegate.count; + return stamps != oldDelegate.stamps || count != oldDelegate.count; } } diff --git a/modules/painting_system/draw_system/lib/src/gallery_card_item.dart b/modules/painting_system/draw_system/lib/src/gallery_card_item.dart new file mode 100644 index 000000000..9bded3955 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/gallery_card_item.dart @@ -0,0 +1,148 @@ + +import 'package:flutter/material.dart'; +import 'package:toly_ui/toly_ui.dart'; +import 'package:l10n/l10n.dart'; +import 'gallery_factory.dart'; + + + +/// create by 张风捷特烈 on 2020/11/28 +/// contact me by email 1981462002@qq.com +/// 说明: + +class GalleryCardItem extends StatelessWidget { + final GalleryInfo galleryInfo; + final int count; + + const GalleryCardItem({ + Key? key, + required this.galleryInfo, + this.count = 0, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + ThemeData themeData = Theme.of(context); + bool isDark = themeData.brightness == Brightness.dark; + + return Card( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(20))), + child: Container( + height: double.maxFinite, + alignment: Alignment.topCenter, + child: Column( + children: [ + Hero( + tag: galleryInfo.name, + child: Container( + height: 150, + decoration: BoxDecoration( + image: DecorationImage( + fit: BoxFit.cover, + image: AssetImage(galleryInfo.image)), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(20), + topRight: Radius.circular(20), + )) + // color: Colors.green, + + ), + ), + Padding( + padding: const EdgeInsets.only( + top: 8, left: 15, right: 15, bottom: 8), + child: Row( + children: [ + Text( + galleryInfo.name, + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + shadows: [ + Shadow( + color: Theme.of(context).primaryColor, + offset: const Offset(.2, .2), + blurRadius: .5) + ]), + ), + const Spacer(), + WrapColor( + radius: 6, + padding: + const EdgeInsets.only(left: 6, right: 6, top: 2, bottom: 3), + child: Text( + "$count ${context.l10n.picture}", + style: const TextStyle( + fontWeight: FontWeight.bold, + color: Colors.white, + fontSize: 12), + ), + ), + ], + ), + ), + Padding( + padding: const EdgeInsets.only(left: 15, right: 15, top: 6), + child: Text( + galleryInfo.info, + 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), + blurRadius: .5) + ]), + ), + ) + ], + ), + decoration: BoxDecoration( + color: isDark?Colors.black87:Colors.white, + borderRadius: BorderRadius.all( + Radius.circular(20), + ))), + ); + } +} + +const Map galleryTypeMap = { + GalleryType.base: "基础绘制", + GalleryType.fun: "趣味绘制", + GalleryType.particle: "粒子绘制", + GalleryType.anim: "动画手势", + GalleryType.art: "艺术画廊", +}; + +class GalleryInfo { + final int count; + final String name; + final String info; + final String image; + final String router; + + GalleryType get type { + GalleryType galleryType = GalleryType.base; + galleryTypeMap.forEach((key, value) { + if (value == name) { + galleryType = key; + } + }); + return galleryType; + } + + const GalleryInfo( + {this.count = 0,required this.name,required this.info,required this.image,required this.router}); + + factory GalleryInfo.fromJson(Map map) { + return GalleryInfo( + count: map['count'] ?? 0, + name: map["name"] ?? "", + image: map["image"] ?? "assets/images/draw_bg4.webp", + router: map["router"] ?? "", + info: map["info"] ?? ""); + } +} diff --git a/lib/painter_system/gallery_detail_page.dart b/modules/painting_system/draw_system/lib/src/gallery_detail_page.dart similarity index 77% rename from lib/painter_system/gallery_detail_page.dart rename to modules/painting_system/draw_system/lib/src/gallery_detail_page.dart index 2c96a841d..9bf828084 100644 --- a/lib/painter_system/gallery_detail_page.dart +++ b/modules/painting_system/draw_system/lib/src/gallery_detail_page.dart @@ -1,6 +1,12 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_unit/views/components/permanent/feedback_widget.dart'; -import 'package:flutter_unit/views/components/project/items/gallery/gallery_card_item.dart'; +import 'package:flutter/services.dart'; +import 'package:toly_ui/toly_ui.dart'; + +import 'gallery_card_item.dart'; + /// create by 张风捷特烈 on 2020/12/4 @@ -11,7 +17,7 @@ class GalleryDetailPage extends StatefulWidget { final GalleryInfo galleryInfo; final List children; - GalleryDetailPage({Key key, this.galleryInfo, this.children = const []}) + const GalleryDetailPage({Key? key,required this.galleryInfo, this.children = const []}) : super(key: key); @override @@ -19,7 +25,7 @@ class GalleryDetailPage extends StatefulWidget { } class _GalleryDetailPageState extends State { - PageController _ctrl; + late PageController _ctrl; final ValueNotifier _currentIndex = ValueNotifier(0); @override @@ -70,20 +76,25 @@ class _GalleryDetailPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - body: Column( - children: [ - Expanded( - flex: 20, - child: buildTopBar(context), - ), - Expanded( - flex: 80, - child: PageView( - controller: _ctrl, - children: widget.children, - onPageChanged: _onPageChanged, - )) - ], + body: AnnotatedRegion( + value:const SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.light + ), + child: Column( + children: [ + Expanded( + flex: 20, + child: buildTopBar(context), + ), + Expanded( + flex: 80, + child: PageView( + controller: _ctrl, + children: widget.children, + onPageChanged: _onPageChanged, + )) + ], + ), ), ); } @@ -105,7 +116,7 @@ class _GalleryDetailPageState extends State { buildTitle(context), Expanded( child: Row( - children: [_buildPrevBtn(), Spacer(), _buildNextBtn()], + children: [_buildPrevBtn(), const Spacer(), _buildNextBtn()], ), ) ], @@ -128,7 +139,7 @@ class _GalleryDetailPageState extends State { width: 35, height: 35, decoration: BoxDecoration(color: btnColor, shape: BoxShape.circle), - child: Icon(Icons.navigate_before, color: Colors.white), + child: const Icon(Icons.navigate_before, color: Colors.white), ), // height: 60, ), @@ -156,7 +167,7 @@ class _GalleryDetailPageState extends State { // 跳转上一页 void _switchPrevPage() { - if (widget.children.length > 0) { + if (widget.children.isNotEmpty) { int page = (_currentIndex.value - 1) % widget.children.length; _ctrl.animateToPage(page, duration: const Duration(milliseconds: 500), curve: Curves.linear); @@ -165,18 +176,24 @@ class _GalleryDetailPageState extends State { // 跳转下一页 void _switchNextPage() { - if (widget.children.length > 0) { + if (widget.children.isNotEmpty) { int page = (_currentIndex.value + 1) % widget.children.length; _ctrl.animateToPage(page, duration: const Duration(milliseconds: 500), curve: Curves.linear); } } + bool isDesk = kIsWeb || Platform.isMacOS || Platform.isWindows || Platform.isLinux; + + Widget buildTitle(BuildContext context) { return Container( - padding: const EdgeInsets.only(top: 46, bottom: 10, left: 20, right: 10), + padding: EdgeInsets.only(top: isDesk?26:46, bottom: 10, left: isDesk?20:0, right: 10), child: Row( children: [ + // if(isDesk) + const BackButton(color: Colors.white,), + Text( widget.galleryInfo.name, style: TextStyle( @@ -190,7 +207,7 @@ class _GalleryDetailPageState extends State { blurRadius: .5) ]), ), - Spacer(), + const Spacer(), ValueListenableBuilder( valueListenable: _currentIndex, builder: _buildIndicatorText, @@ -200,9 +217,9 @@ class _GalleryDetailPageState extends State { ); } - Widget _buildIndicatorText(BuildContext context, int value, Widget child) { + Widget _buildIndicatorText(BuildContext context, int value, Widget? child) { String indicatorText = - "${widget.children.length != 0 ? (value + 1) : 0} / ${widget.children.length}"; + "${widget.children.isNotEmpty ? (value + 1) : 0} / ${widget.children.length}"; return Text( indicatorText, 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/modules/painting_system/draw_system/lib/src/gallery_unit.dart b/modules/painting_system/draw_system/lib/src/gallery_unit.dart new file mode 100644 index 000000000..156e91a99 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/gallery_unit.dart @@ -0,0 +1,246 @@ +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: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 +/// 说明: + +class GalleryUnit extends StatelessWidget { + const GalleryUnit({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (_,c){ + if(c.maxWidth>500){ + return const DeskGalleryUnit(); + } + return const PhoneGalleryUnit(); + }); + } +} + +class PhoneGalleryUnit extends StatefulWidget { + const PhoneGalleryUnit({Key? key}) : super(key: key); + + @override + _PhoneGalleryUnitState createState() => _PhoneGalleryUnitState(); +} + +class _PhoneGalleryUnitState 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) { + return Scaffold( + body: AnnotatedRegion( + value:const SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.light + ), + child:ValueListenableBuilder( + child: Column( + children: [ + _buildTitle(context), + Expanded( + child: Container( + margin: const EdgeInsets.only(left: 8, right: 8), + child: _buildContent(StrUnit.galleryDesc(context)), + decoration: boxDecoration, + )) + ], + ), + valueListenable: factor, + builder: (_,double value, child) => Container( + color: isDark?null:Color.lerp( + color, + nextColor, + value, + ), + child: child, + ), + ), + ), + ); + } + + Widget _buildTitle(BuildContext context) { + return Container( + alignment: const Alignment(0, 0.3), + height: MediaQuery.of(context).size.height * 0.2, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + FlutterLogo( + size: 40, + ), + SizedBox( + width: 10, + ), + Text( + context.l10n.paintCollection, + style: TextStyle(fontSize: 26, color: Colors.white), + ), + ], + ), + ); + } + + Widget _buildContentByState(BuildContext context, String state) { + if(state.isEmpty){ + return const LoadingShower(); + } + 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,context); + + return FeedbackWidget( + a: 0.95, + onPressed: () { + Navigator.of(context).push(MaterialPageRoute( + builder: (ctx) => GalleryDetailPage( + galleryInfo: info, + children: children, + ))); + }, + child: GalleryCardItem( + galleryInfo: info, + count: children.length, + ), + ); + }).toList(); + + return Container( + padding: const EdgeInsets.only(bottom: 80, top: 40), + child: Column( + children: [ + Expanded( + child: PageView.builder( + controller: _ctrl, + itemBuilder: (_, index) { + return AnimatedBuilder( + child: Padding( + padding: const EdgeInsets.all(6.0), + child: widgets[ + _fixPosition(index, _firstOffset, widgets.length)], + ), + animation: _ctrl, + builder: (context, child) => + _buildAnimItemByIndex(context, child, index), + ); + }, + onPageChanged: (index) { + _position = index; + }, + ), + ), + _buildDiver(), + ], + )); + } + + 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()) * .5)).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(6.0), + child: child, + ), + ); + } + + Widget _buildDiver() => Container( + margin: const EdgeInsets.only(bottom: 12, left: 48, right: 48, top: 10), + height: 2, + child: ValueListenableBuilder( + valueListenable: factor, + builder: (context, value, widget) { + return LinearProgressIndicator( + backgroundColor: Colors.black, + value: factor.value, + valueColor: AlwaysStoppedAnimation( + Color.lerp( + color, + nextColor, + factor.value, + ), + ), + ); + }, + ), + ); + + int _fixPosition(int realPos, int initPos, int length) { + final int offset = realPos - initPos; + int result = offset % length; + return result < 0 ? length + result : result; + } + + +} 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 88% rename from lib/painter_system/particle/out/clock_fx.dart rename to modules/painting_system/draw_system/lib/src/particle/out/clock_fx.dart index 0a09c56d0..77c22bd87 100644 --- a/lib/painter_system/particle/out/clock_fx.dart +++ b/modules/painting_system/draw_system/lib/src/particle/out/clock_fx.dart @@ -6,42 +6,40 @@ import 'rnd.dart'; import 'particle.dart'; -final easingDelayDuration = Duration(seconds: 10); +const Duration easingDelayDuration = Duration(seconds: 10); /// Probabilities of Hour, Minute, Noise. // final particleDistributions = [2, 4, 100]; /// Number of "arms" to emit noise particles from center. -final int noiseAngles = 2000; +const int noiseAngles = 2000; /// Threshold for particles to go rouge. Lower = more particles. -final rougeDistributionLmt = 85; +const int rougeDistributionLmt = 85; /// Threshold for particles to go jelly. Lower = more particles. -final jellyDistributionLmt = 97; +const int jellyDistributionLmt = 97; class ClockFx with ChangeNotifier { - double width; //宽 - double height;//高 - double sizeMin; // 宽高最小值 - Offset center; //画布中心 - Rect spawnArea; // 粒子活动区域 + double width=0; //宽 + double height=0;//高 + double sizeMin=0; // 宽高最小值 + Offset center=Offset.zero; //画布中心 + Rect spawnArea=Rect.zero; // 粒子活动区域 - List particles; // 所有粒子 + List particles=[]; // 所有粒子 - int numParticles;// 最大粒子数 - - DateTime time; //时间 + int numParticles=0;// 最大粒子数 + DateTime time; //时间 ClockFx({ - @required Size size, - @required DateTime time, + required Size size, + required this.time, this.numParticles = 5000, }) { - this.time = time; - particles = List.filled(numParticles, null); + particles = List.filled(numParticles, Particle()); setSize(size); } diff --git a/modules/painting_system/draw_system/lib/src/particle/out/clock_widget.dart b/modules/painting_system/draw_system/lib/src/particle/out/clock_widget.dart new file mode 100644 index 000000000..025d95300 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/out/clock_widget.dart @@ -0,0 +1,85 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +import 'clock_fx.dart'; + +/// create by 张风捷特烈 on 2021/2/7 +/// contact me by email 1981462002@qq.com +/// 说明: + +class ClockWidget extends StatefulWidget { + final double radius; + + const ClockWidget({Key? key, this.radius = 100}) : super(key: key); + + @override + _ClockWidgetState createState() => _ClockWidgetState(); +} + +class _ClockWidgetState extends State + with SingleTickerProviderStateMixin { + late Ticker _ticker; + late ClockFx _fx; + + @override + void initState() { + super.initState(); + _ticker = createTicker(_tick)..start(); + _fx = ClockFx( + size: Size(widget.radius * 2, widget.radius * 2), + time: DateTime.now(), + ); + } + + @override + void dispose() { + _ticker.dispose(); + _fx.dispose(); + super.dispose(); + } + + void _tick(Duration duration) { + _fx.tick(duration); + if (_fx.time.second != DateTime.now().second) { + _fx.setTime(DateTime.now()); + } + } + + @override + Widget build(BuildContext context) { + return CustomPaint( + size: Size(widget.radius * 2, widget.radius * 2), + painter: ClockFxPainter(fx: _fx), + ); + } +} + +/// Alpha value for noise particles. +const double noiseAlpha = 160; + +class ClockFxPainter extends CustomPainter { + final ClockFx fx; + + ClockFxPainter({required this.fx}) : super(repaint: fx); + + @override + void paint(Canvas canvas, Size size) { + for (var p in fx.particles) { + double a; + a = max(0.0, (p.distFrac - .13) / p.distFrac) * 255; + a = min(a, min(noiseAlpha, p.lifeLeft * 3 * 255)); + int alpha = a.floor(); + + Paint circlePaint = Paint() + ..style = PaintingStyle.fill + ..color = p.color.withAlpha(alpha); + + canvas.drawCircle(Offset(p.x, p.y), p.size, circlePaint); + } + } + + @override + bool shouldRepaint(covariant ClockFxPainter oldDelegate) => + oldDelegate.fx != fx; +} diff --git a/modules/painting_system/draw_system/lib/src/particle/out/particle.dart b/modules/painting_system/draw_system/lib/src/particle/out/particle.dart new file mode 100644 index 000000000..261db7f14 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/out/particle.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +class Particle { + + double x; // x 坐标 + double y; // y 坐标 + double vx; // x 速度 + double vy; // y 速度 + double a; // 发射弧度 + double dist; // 距离画布中心的长度 + double distFrac;// 距离画布中心的百分比 + double size;// 粒子大小 + double life; // 粒子寿命 + double lifeLeft; // 粒子剩余寿命 + bool isFilled; // 是否填充 + Color color; // 颜色 + int distribution; // 分配情况 + + + Particle({ + this.x = 0, + this.y = 0, + this.a = 0, + this.vx = 0, + this.vy = 0, + this.dist = 0, + this.distFrac = 0, + this.size = 0, + this.life = 0, + this.lifeLeft = 0, + this.isFilled = false, + this.color = Colors.blueAccent, + this.distribution = 0, + }); +} + + 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/modules/painting_system/draw_system/lib/src/particle/random/particle.dart b/modules/painting_system/draw_system/lib/src/particle/random/particle.dart new file mode 100644 index 000000000..2930bbaca --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/random/particle.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +class Particle { + /// x 位移. + double x; + + /// y 位移. + double y; + + /// 粒子水平速度. + double vx; + + // 粒子水平加速度 + double ax; + + // 粒子竖直加速度 + double ay; + + ///粒子竖直速度. + double vy; + + + /// 粒子大小. + double size; + + /// 粒子颜色. + Color color; + + Particle({ + this.x = 0, + this.y = 0, + this.ax = 0, + this.ay = 0, + this.vx = 0, + this.vy = 0, + this.size = 0, + this.color = Colors.black, + }); +} diff --git a/modules/painting_system/draw_system/lib/src/particle/random/particle_manage.dart b/modules/painting_system/draw_system/lib/src/particle/random/particle_manage.dart new file mode 100644 index 000000000..b8bbfce36 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/random/particle_manage.dart @@ -0,0 +1,59 @@ +import 'package:flutter/cupertino.dart'; + +import 'particle.dart'; + +/// create by 张风捷特烈 on 2020/11/7 +/// contact me by email 1981462002@qq.com +/// 说明: + +class ParticleManage with ChangeNotifier { + List particles = []; + + Size size; + + ParticleManage({ this.size = Size.zero}); + + void addParticle(Particle particle) { + particles.add(particle); + notifyListeners(); + } + + void tick() { + particles.forEach(doUpdate); + notifyListeners(); + } + + void doUpdate(Particle p) { + p.vy += p.ay; // y加速度变化 + p.vx += p.ax; // x加速度变化 + p.x += p.vx; + p.y += p.vy; + + if(p.x>size.width-p.size){ + p.x = size.width-p.size; + p.vx = -p.vx; + } + + if(p.x < p.size){ + p.x = p.size; + p.vx = -p.vx; + } + + if(p.y > size.height-p.size){ + p.y = size.height-p.size; + p.vy = -p.vy; + } + if(p.y < p.size){ + p.y = p.size; + p.vy = -p.vy; + } + + } + + void reset() { + for (Particle p in particles) { + p.x = 0; + } + notifyListeners(); + } +} diff --git a/modules/painting_system/draw_system/lib/src/particle/random/random_particle.dart b/modules/painting_system/draw_system/lib/src/particle/random/random_particle.dart new file mode 100644 index 000000000..16276a6e2 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/random/random_particle.dart @@ -0,0 +1,92 @@ +import 'dart:async'; +import 'dart:math'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'particle.dart'; + +import 'particle_manage.dart'; +import 'world_render.dart'; + +/// create by 张风捷特烈 on 2020/11/5 +/// contact me by email 1981462002@qq.com +/// 说明: + +class RandomParticle extends StatefulWidget { + const RandomParticle({Key? key}) : super(key: key); + + @override + _RandomParticleState createState() => _RandomParticleState(); +} + +class _RandomParticleState extends State with SingleTickerProviderStateMixin { + late AnimationController _controller; + ParticleManage pm = ParticleManage(); + Random random = Random(); + Timer? _timer; + + @override + void initState() { + super.initState(); + + + _timer =Timer.periodic(const Duration(seconds: 1),addParticle); + + _controller = AnimationController( + duration: const Duration(seconds: 1), + vsync: this, + )..addListener(pm.tick) + ..repeat(); + } + + void addParticle(Timer timer){ + if(pm.particles.length>20){ + timer.cancel(); + } + pm.addParticle(Particle( + color: randomRGB(), + size: 5 + 4 * random.nextDouble(), + vx: 3 * random.nextDouble() * pow(-1, random.nextInt(20)), + vy: 3 * random.nextDouble() * pow(-1, random.nextInt(20)), + ay: 0.1, + x: 150, + y: 100)); + } + + Color randomRGB({int limitR = 0, int limitG = 0, int limitB = 0,}) { + var r = limitR + random.nextInt(256 - limitR); //红值 + var g = limitG + random.nextInt(256 - limitG); //绿值 + var b = limitB + random.nextInt(256 - limitB); //蓝值 + return Color.fromARGB(255, r, g, b); //生成argb模式的颜色 + } + + @override + void dispose() { + _controller.dispose(); + _timer?.cancel(); + super.dispose(); + } + + theWorld() { + if (_controller.isAnimating) { + _controller.stop(); + } else { + _controller.repeat(); + } + } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (_,constraints){ + pm.size = constraints.biggest; + return GestureDetector( + onTap: theWorld, + child: CustomPaint( + size: pm.size, + painter: WorldRender(manage: pm), + ), + ); + }, + ); + } +} diff --git a/modules/painting_system/draw_system/lib/src/particle/random/world_render.dart b/modules/painting_system/draw_system/lib/src/particle/random/world_render.dart new file mode 100644 index 000000000..d2d4545dd --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/random/world_render.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +import 'particle.dart'; +import 'particle_manage.dart'; + +/// create by 张风捷特烈 on 2020/11/7 +/// contact me by email 1981462002@qq.com +/// 说明: + +class WorldRender extends CustomPainter { + + final ParticleManage manage; + + Paint fillPaint = Paint(); + + Paint stokePaint = Paint() + ..strokeWidth = 0.5 + ..style = PaintingStyle.stroke; + + WorldRender({required this.manage}) : super(repaint: manage); + + @override + void paint(Canvas canvas, Size size) { + for (Particle particle in manage.particles) { + drawParticle(canvas, particle); + } + } + + void drawParticle(Canvas canvas, Particle particle) { + fillPaint.color = particle.color; + canvas.drawCircle(Offset(particle.x, particle.y), particle.size, fillPaint); + } + + @override + bool shouldRepaint(covariant WorldRender oldDelegate) => + oldDelegate.manage != manage; +} diff --git a/modules/painting_system/draw_system/lib/src/particle/split/particle.dart b/modules/painting_system/draw_system/lib/src/particle/split/particle.dart new file mode 100644 index 000000000..cffe5e831 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/split/particle.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; + +class Particle { + /// x 位移. + double x; + + /// y 位移. + double y; + + /// 粒子水平速度. + double vx; + + // 粒子水平加速度 + double ax; + + // 粒子竖直加速度 + double ay; + + ///粒子竖直速度. + double vy; + + /// 粒子大小. + double size; + + /// 粒子颜色. + Color color; + + Particle({ + this.x = 0, + this.y = 0, + this.ax = 0, + this.ay = 0, + this.vx = 0, + this.vy = 0, + this.size = 0, + this.color = Colors.black, + }); + + Particle copyWith( + { double? x, + double? y, + double? ax, + double? ay, + double? vx, + double? vy, + double? size, + Color? color + } + ) => + Particle( + x: x ?? this.x, + y: y ?? this.y, + ax: ax ?? this.ax, + ay: ay ?? this.ay, + vx: vx ?? this.vx, + vy: vy ?? this.vy, + size: size ?? this.size, + color: color ?? this.color); +} diff --git a/modules/painting_system/draw_system/lib/src/particle/split/particle_manage.dart b/modules/painting_system/draw_system/lib/src/particle/split/particle_manage.dart new file mode 100644 index 000000000..77a8ca806 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/split/particle_manage.dart @@ -0,0 +1,94 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; + +import 'particle.dart'; + +/// create by 张风捷特烈 on 2020/11/7 +/// contact me by email 1981462002@qq.com +/// 说明: + +class ParticleManage with ChangeNotifier { + List particles = []; + Random random = Random(); + + Size size; + + ParticleManage({this.size=Size.zero}); + + void setParticles(List particles) { + this.particles = particles; + } + + void addParticle(Particle particle) { + particles.add(particle); + notifyListeners(); + } + + void tick() { + for (int i = 0; i < particles.length; i++) { + doUpdate(particles[i]); + } + notifyListeners(); + } + + void doUpdate(Particle p) { + p.vy += p.ay; // y加速度变化 + p.vx += p.ax; // x加速度变化 + p.x += p.vx; + p.y += p.vy; + + if (p.x > size.width-p.size) { + p.x = size.width-p.size; + + var newSize = p.size / 2; + if (newSize > 1) {// 如果尺寸小于 1 ,执行分裂 + Particle p0 = + p.copyWith(size: newSize, vx: -p.vx, vy: -p.vy, color: randomRGB()); + particles.add(p0); + p.size = newSize; + p.vx = -p.vx; + } + } + + if (p.x < p.size) { + p.x = p.size; + p.vx = -p.vx; + } + + if (p.y > size.height-p.size) { + p.y = size.height-p.size; + + var newSize = p.size / 2; + if (newSize > 1) { + Particle p0 = + p.copyWith(size: newSize, vx: -p.vx, vy: -p.vy, color: randomRGB()); + particles.add(p0); + p.size = newSize; + p.vy = -p.vy; + } + } + if (p.y < p.size) { + p.y = p.size; + p.vy = -p.vy; + } + } + + void reset() { + for (Particle p in particles) { + p.x = 0; + } + notifyListeners(); + } + + Color randomRGB({ + int limitR = 0, + int limitG = 0, + int limitB = 0, + }) { + var r = limitR + random.nextInt(256 - limitR); //红值 + var g = limitG + random.nextInt(256 - limitG); //绿值 + var b = limitB + random.nextInt(256 - limitB); //蓝值 + return Color.fromARGB(255, r, g, b); //生成argb模式的颜色 + } +} diff --git a/modules/painting_system/draw_system/lib/src/particle/split/particle_split.dart b/modules/painting_system/draw_system/lib/src/particle/split/particle_split.dart new file mode 100644 index 000000000..6aa59189a --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/split/particle_split.dart @@ -0,0 +1,96 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'particle.dart'; + +import 'particle_manage.dart'; +import 'world_render.dart'; + +/// create by 张风捷特烈 on 2020/11/5 +/// contact me by email 1981462002@qq.com +/// 说明: + +class ParticleSplit extends StatefulWidget { + const ParticleSplit({Key? key}) : super(key: key); + + @override + _ParticleSplitState createState() => _ParticleSplitState(); +} + +class _ParticleSplitState extends State with SingleTickerProviderStateMixin { + late AnimationController _controller; + ParticleManage pm = ParticleManage(); + Random random = Random(); + + @override + void initState() { + super.initState(); + + _controller = AnimationController( + duration: const Duration(seconds: 1), + vsync: this, + )..addListener(pm.tick); + + initParticle(); + // Future.delayed(Duration(seconds: 1)).then((value){ + // + // }); + } + + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + // void theWorld() { + // if (_controller.isAnimating) { + // _controller.stop(); + // } else { + // _controller.repeat(); + // } + // } + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (_,constraints){ + pm.size = constraints.biggest; + return GestureDetector( + // onDoubleTap: (){ + // pm.addParticle(Particle( + // color: Colors.blue, + // size: 50, + // vx: 4 * random.nextDouble() * pow(-1, random.nextInt(20)), + // vy: 4 * random.nextDouble() * pow(-1, random.nextInt(20)), + // ay: 0.1, + // ax: 0.1, + // x: 150, + // y: 100)); + // }, + onTap: initParticle, + child: CustomPaint( + size: pm.size, + painter: WorldRender(manage: pm), + ), + ); + }, + ); + + } + + void initParticle(){ + pm.particles.clear(); + pm.addParticle(Particle( + color: Colors.blue, + size: 50, + vx: 4 * random.nextDouble() * pow(-1, random.nextInt(20)), + vy: 4 * random.nextDouble() * pow(-1, random.nextInt(20)), + ay: 0.1, + ax: 0.1, + x: 150, + y: 100)); + _controller.reset(); + _controller.repeat(); + } +} diff --git a/modules/painting_system/draw_system/lib/src/particle/split/world_render.dart b/modules/painting_system/draw_system/lib/src/particle/split/world_render.dart new file mode 100644 index 000000000..6c0d43771 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/split/world_render.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +import 'particle.dart'; +import 'particle_manage.dart'; + +/// create by 张风捷特烈 on 2020/11/7 +/// contact me by email 1981462002@qq.com +/// 说明: + +class WorldRender extends CustomPainter { + + final ParticleManage manage; + + Paint fillPaint = Paint(); + + Paint stokePaint = Paint() + ..strokeWidth = 0.5 + ..style = PaintingStyle.stroke; + + WorldRender({ required this.manage}) : super(repaint: manage); + + @override + void paint(Canvas canvas, Size size) { + for (Particle particle in manage.particles) { + drawParticle(canvas, particle); + } + } + + void drawParticle(Canvas canvas, Particle particle) { + fillPaint.color = particle.color; + canvas.drawCircle(Offset(particle.x, particle.y), particle.size, fillPaint); + } + + @override + bool shouldRepaint(covariant WorldRender oldDelegate) => + oldDelegate.manage != manage; +} diff --git a/modules/painting_system/draw_system/lib/src/particle/split_img/particle.dart b/modules/painting_system/draw_system/lib/src/particle/split_img/particle.dart new file mode 100644 index 000000000..2611c3bad --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/split_img/particle.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; + +class Particle { + /// x 位移. + double x; + + /// y 位移. + double y; + + /// 粒子水平速度. + double vx; + + // 粒子水平加速度 + double ax; + + // 粒子竖直加速度 + double ay; + + ///粒子竖直速度. + double vy; + + /// 粒子大小. + double size; + + /// 粒子颜色. + Color color; + + Particle({ + this.x = 0, + this.y = 0, + this.ax = 0, + this.ay = 0, + this.vx = 0, + this.vy = 0, + this.size = 0, + this.color = Colors.black, + }); + + Particle copyWith( + { double? x, + double? y, + double? ax, + double? ay, + double? vx, + double? vy, + double? size, + Color? color} + ) => + Particle( + x: x ?? this.x, + y: y ?? this.y, + ax: ax ?? this.ax, + ay: ay ?? this.ay, + vx: vx ?? this.vx, + vy: vy ?? this.vy, + size: size ?? this.size, + color: color ?? this.color); +} diff --git a/modules/painting_system/draw_system/lib/src/particle/split_img/particle_manage.dart b/modules/painting_system/draw_system/lib/src/particle/split_img/particle_manage.dart new file mode 100644 index 000000000..22695501b --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/split_img/particle_manage.dart @@ -0,0 +1,82 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; + +import 'particle.dart'; + +/// create by 张风捷特烈 on 2020/11/7 +/// contact me by email 1981462002@qq.com +/// 说明: + +class ParticleManage with ChangeNotifier { + List particles = []; + final VoidCallback? onEnd; + Random random = Random(); + + Size size; + + ParticleManage({this.size=Size.zero,this.onEnd}); + + void setParticles(List particles) { + this.particles = particles; + } + + void addParticle(Particle particle) { + particles.add(particle); + notifyListeners(); + } + + void tick({bool run=true}) { + if(run){ + for (int i = 0; i < particles.length; i++) { + doUpdate(particles[i]); + } + } + if(particles.isNotEmpty){ + notifyListeners(); + } + } + + void doUpdate(Particle p) { + p.vy += p.ay; // y加速度变化 + p.vx += p.ax; // x加速度变化 + p.x += p.vx; + p.y += p.vy; + + if (p.x > size.width) { + particles.remove(p); + } + + if (p.x < 0) { + particles.remove(p); + } + + if (p.y > size.height) { + particles.remove(p); + } + if (p.y < 0) { + particles.remove(p); + } + if(particles.isEmpty){ + onEnd?.call(); + } + } + + void reset() { + for (Particle p in particles) { + p.x = 0; + } + notifyListeners(); + } + + Color randomRGB({ + int limitR = 0, + int limitG = 0, + int limitB = 0, + }) { + var r = limitR + random.nextInt(256 - limitR); //红值 + var g = limitG + random.nextInt(256 - limitG); //绿值 + var b = limitB + random.nextInt(256 - limitB); //蓝值 + return Color.fromARGB(255, r, g, b); //生成argb模式的颜色 + } +} diff --git a/modules/painting_system/draw_system/lib/src/particle/split_img/split_image.dart b/modules/painting_system/draw_system/lib/src/particle/split_img/split_image.dart new file mode 100644 index 000000000..efd4fefb5 --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/split_img/split_image.dart @@ -0,0 +1,102 @@ +import 'dart:math'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'particle.dart'; +import 'package:image/image.dart' as image; +import 'particle_manage.dart'; +import 'world_render.dart'; + +/// create by 张风捷特烈 on 2020/11/5 +/// contact me by email 1981462002@qq.com +/// 说明: + +class SplitImage extends StatefulWidget { + const SplitImage({Key? key}) : super(key: key); + + @override + _SplitImageState createState() => _SplitImageState(); +} + +class _SplitImageState extends State with SingleTickerProviderStateMixin { + late AnimationController _controller; + late ParticleManage pm; + Random random = Random(); + + @override + void initState() { + super.initState(); + pm = ParticleManage( + onEnd: (){ + _controller.stop(); + } + ); + + initParticles(); + + _controller = AnimationController( + duration: const Duration(seconds: 1), + vsync: this, + )..addListener(pm.tick); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (_,constraints){ + pm.size = constraints.biggest; + return Stack( + children: [ + GestureDetector( + onTap: (){ + initParticles(); + _controller.reset(); + _controller.repeat(); + }, + child: CustomPaint( + size: pm.size, + painter: WorldRender(manage: pm), + ), + ), + ], + ); + }, + ); + + } + + void initParticles() async { + pm.particles.clear(); + ByteData data = await rootBundle.load("assets/images/flutter.png"); + List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); + image.Image? imageSrc = image.decodeImage(Uint8List.fromList(bytes)); + + if(imageSrc==null) return; + + double offsetX= (pm.size.width-imageSrc.width)/2; + double offsetY= (pm.size.height-imageSrc.height)/2; + + for (int i = 0; i < imageSrc.width; i++) { + for (int j = 0; j < imageSrc.height; j++) { + if (imageSrc.getPixel(i, j) == 0xff000000) { + Particle particle = Particle( + x: i * 1.0+ offsetX, + y: j * 1.0+ offsetY, + vx: 4 * random.nextDouble() * pow(-1, random.nextInt(20)), + vy: 4 * random.nextDouble() * pow(-1, random.nextInt(20)), + ay: 0.1, + size: 0.5, + color: Colors.blue); //产生粒子---每个粒子拥有随机的一些属性信息 + pm.particles.add(particle); + } + } + } + pm.tick(run: false); + } +} diff --git a/modules/painting_system/draw_system/lib/src/particle/split_img/world_render.dart b/modules/painting_system/draw_system/lib/src/particle/split_img/world_render.dart new file mode 100644 index 000000000..19c30adeb --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/particle/split_img/world_render.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +import 'particle.dart'; +import 'particle_manage.dart'; + +/// create by 张风捷特烈 on 2020/11/7 +/// contact me by email 1981462002@qq.com +/// 说明: + +class WorldRender extends CustomPainter { + + final ParticleManage manage; + + Paint fillPaint = Paint(); + + Paint stokePaint = Paint() + ..strokeWidth = 0.5 + ..style = PaintingStyle.stroke; + + WorldRender({required this.manage}) : super(repaint: manage); + + @override + void paint(Canvas canvas, Size size) { + for (Particle particle in manage.particles) { + drawParticle(canvas, particle); + } + } + + void drawParticle(Canvas canvas, Particle particle) { + + fillPaint.color = particle.color; + canvas.drawCircle(Offset(particle.x, particle.y), particle.size, fillPaint); + } + + @override + bool shouldRepaint(covariant WorldRender oldDelegate) => + oldDelegate.manage != manage; +} diff --git a/modules/painting_system/draw_system/lib/src/picture_frame.dart b/modules/painting_system/draw_system/lib/src/picture_frame.dart new file mode 100644 index 000000000..591c2e00b --- /dev/null +++ b/modules/painting_system/draw_system/lib/src/picture_frame.dart @@ -0,0 +1,194 @@ +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; + final double? height; + final Color? color; + final Alignment? alignment; + final EdgeInsetsGeometry? marge; + + const PictureFrame({Key? key, this.child, + this.width, + this.height, + this.alignment, + this.color = Colors.transparent, + this.marge}) : super(key: key); + + @override + Widget build(BuildContext context) { + double size = MediaQuery.of(context).size.shortestSide; + return Container( + alignment: alignment, + width: width ?? size, + height: height ?? size, + padding: marge ?? const EdgeInsets.all(20), + child: CustomPaint( + painter: FramePainter(), + child: Container( + margin: const EdgeInsets.all(14), + decoration: BoxDecoration( + color: color, + border: Border.all( + color: Colors.black12, + width: 1, + ), + ), + child: child, + ), + ), + ); + } +} + +class FramePainter extends CustomPainter { + @override + void paint(Canvas canvas, Size size) { + Path path = Path() + ..relativeLineTo(0, size.height)..relativeLineTo(size.width, 0)..relativeLineTo(0, -size.height) + ..close(); + Paint myPaint = Paint() + ..style = PaintingStyle.stroke + ..strokeWidth = 10; + canvas.drawPath(path, myPaint); + Path shadowPath = Path() + ..addRect(Rect.fromPoints(Offset.zero, Offset(size.width, size.height))); + // canvas.drawShadow(shadowPath, Colors.grey, 1, false); + + canvas.save(); + canvas.translate(size.width / 2, size.height / 2); + canvas.scale(-1, 1); + canvas.translate(-size.width / 2, -size.height / 2); + drawCorner(myPaint, canvas); + canvas.restore(); + + canvas.save(); + canvas.translate(size.width / 2, size.height / 2); + canvas.scale(1, -1); + canvas.translate(-size.width / 2, -size.height / 2); + drawCorner(myPaint, canvas); + canvas.restore(); + + canvas.save(); + canvas.translate(size.width, size.height); + canvas.scale(-1, -1); + drawCorner(myPaint, canvas); + canvas.restore(); + drawCorner(myPaint, canvas); + canvas.drawShadow(shadowPath, Colors.grey, 1, false); + } + + void drawCorner(Paint myPaint, Canvas canvas) { + myPaint + ..style = PaintingStyle.fill + ..color = Colors.white + ..strokeCap = StrokeCap.butt; + canvas.drawPoints( + PointMode.polygon, + [ + const Offset(0, 0), + const Offset(18, 0), + const Offset(0, 18), + const Offset(0, 0), + ], + myPaint); + canvas.drawCircle(const Offset(8, 8), 3, myPaint..color = Colors.black); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return false; + } +} + +class FrameShower extends StatelessWidget { + final String title; + final String author; + final String srcUrl; + final String info; + final Widget content; + + const FrameShower({Key? key, + this.title = "", + this.author = "", + this.srcUrl = "", + this.info = "", + required this.content}) + : super(key: key); + + @override + Widget build(BuildContext context) { + bool isDesk = kIsWeb||Platform.isMacOS || Platform.isWindows || Platform.isLinux; + if (isDesk) { + return DeskFrameShower( + content: content, + title: title, + author: author, + srcUrl: srcUrl, + info: info); + } + + return Align( + alignment: Alignment.topCenter, + child: Column( + children: [ + const SizedBox(height: 15), + Text( + title, + style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + PictureFrame(child: content,color: Colors.white,), + Container( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + Text( + "作者: $author ", + style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ), + const Spacer(), + GestureDetector( + onTap: _launch, + child: Text( + "${context.l10n.srcPath} ", + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: Colors.blueAccent), + ), + ), + ], + ), + ), + const SizedBox(height: 10), + Container( + padding: const EdgeInsets.symmetric(horizontal: 12), + alignment: Alignment.topLeft, + child: Text( + info, + style: const TextStyle( + fontSize: 14, + color: Colors.grey, + fontWeight: FontWeight.bold), + )), + ], + ), + ); + } + + void _launch() async { + String url = 'https://github.com/toly1994328/FlutterUnit/tree/master/lib/painter_system$srcUrl'; + if (await canLaunch(url)) { + await launch(url); + } else { + + } + } +} \ No newline at end of file diff --git a/lib/painter_system/utils/colors.dart b/modules/painting_system/draw_system/lib/src/utils/colors.dart similarity index 89% rename from lib/painter_system/utils/colors.dart rename to modules/painting_system/draw_system/lib/src/utils/colors.dart index 1991f282e..7af5f8d6a 100644 --- a/lib/painter_system/utils/colors.dart +++ b/modules/painting_system/draw_system/lib/src/utils/colors.dart @@ -1,6 +1,6 @@ import 'dart:ui'; -List colors = [ +const List colors = [ Color(0xff8e43e7), Color(0xffff4f81), Color(0xff1cc7d0), diff --git a/lib/painter_system/utils/coordinate.dart b/modules/painting_system/draw_system/lib/src/utils/coordinate.dart similarity index 97% rename from lib/painter_system/utils/coordinate.dart rename to modules/painting_system/draw_system/lib/src/utils/coordinate.dart index b996ecce4..0bd359751 100644 --- a/lib/painter_system/utils/coordinate.dart +++ b/modules/painting_system/draw_system/lib/src/utils/coordinate.dart @@ -40,7 +40,7 @@ class Coordinate { } _drawText(canvas, size); - TextSpan text = TextSpan( + TextSpan text = const TextSpan( text: "x", style: TextStyle( fontSize: 20, @@ -54,7 +54,7 @@ class Coordinate { _textPainter.paint(canvas, Offset(size.width / 2 - 20, -30)); - _textPainter.text = TextSpan( + _textPainter.text = const TextSpan( text: "y", style: TextStyle( fontSize: 20, @@ -120,7 +120,7 @@ class Coordinate { } void _drawAxisText(Canvas canvas, String str, - {Color color = Colors.black, bool x = false}) { + {Color color = Colors.black, bool? x = false}) { TextSpan text = TextSpan( text: str, style: TextStyle( 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/modules/tools_system/treasure_tools/lib/src/class_generator.dart b/modules/tools_system/treasure_tools/lib/src/class_generator.dart new file mode 100644 index 000000000..fffe080ec --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/class_generator.dart @@ -0,0 +1,36 @@ +import 'dart:io'; +import 'package:path/path.dart' as path; + +import 'model/class.dart'; +import 'model/field.dart'; +main() { + // Class clazz1 = Class( + // name: 'TaskResult', + // fields: [ + // Field(name: 'cost', type: 'int'), + // Field(name: 'taskName', type: 'String'), + // Field(name: 'count', type: 'int'), + // Field(name: 'taskCode', type: 'String'), + // Field(name: 'taskInfo', type: 'String', nullable: true, isRequired: false), + // ], + // ); + + // print(clazz1.buildClass()); + + // Class clazz2 = Class( + // name: 'User', + // fields: [ + // Field(name: 'age', type: int), + // Field(name: 'username', type: String), + // Field(name: 'roleId', type: int), + // Field(name: 'info', type: String,nullable: true), + // Field(name: 'height', type: double, nullable: true, isRequired: false), + // ], + // ); + // clazz1.write2File(Directory(r'E:\Projects\Flutter\Github\skeleton\lib\chatgpt\generator')); + // clazz2.write2File(Directory(r'E:\Projects\Flutter\Github\skeleton\lib\chatgpt\generator')); +} + + + + diff --git a/modules/tools_system/treasure_tools/lib/src/code_gen_page.dart b/modules/tools_system/treasure_tools/lib/src/code_gen_page.dart new file mode 100644 index 000000000..25a872bb2 --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/code_gen_page.dart @@ -0,0 +1,221 @@ +import 'dart:io'; +import 'package:flutter/material.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 {} + +class CodeGenPage extends StatefulWidget { + const CodeGenPage({Key? key}) : super(key: key); + + @override + State createState() => _CodeGenPageState(); +} + +class _CodeGenPageState extends State { + + TextEditingController _dirPath = TextEditingController(); + final PageController _ctrl = PageController(); + int selectIndex = 0; + + final List selectData = [ + "final", + "static", + 'static const', + ]; + + Class clazz1 = Class( + name: '', + fields: [ + ], + ); + + @override + void initState() { + super.initState(); + } + + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + DeskCodeGenTopBar( + onTapGen: _doGen, + onTabPressed: (int value) { + _ctrl.jumpToPage(value); + }, + ), + Expanded(child: PageView( + controller:_ctrl, + children: [ + IconFontGenPage(), + JsonAnalysisTool(), + Center( + child: Text( + '敬请期待' + ), + ), + Center( + child: Text( + '敬请期待' + ), + ) + ], + )), + if(false) + Padding( + padding: const EdgeInsets.only(left: 20,top: 20), + child: Row( + children: [ + GestureDetector( + onTap: () async{ + // final String? directoryPath = await getDirectoryPath(); + // if (directoryPath != null) { + // print("====$directoryPath========="); + // _dirPath.text = directoryPath; + // } + }, + child: Icon(Icons.file_copy_outlined)), + SizedBox(width: 20,), + Expanded(child: TextField( + controller: _dirPath, + )), + SizedBox(width: 20,), + ], + ), + ), + if(false) + Expanded( + child: Padding( + padding: const EdgeInsets.all(20.0), + child: ClassGenField( + clazz: clazz1, + ), + )) + ], + ), + ); + } + + void _onChange(int index) { + selectIndex = index; + setState(() {}); + } + + void _doGen() { + // String className = _classNameCtrl.text; + // String fieldType = _fieldTypeCtrl.text; + // String fieldName = _fieldNameCtrl.text; + // String modifier = selectData[selectIndex]; + // + // print( + // "===$className===$fieldType====$fieldName=======$modifier==================="); + // Class clazz = Class( + // name: className, + // fields: [ + // // Field(name: 'cost', type: int), + // // Field(name: 'taskName', type: String), + // // Field(name: 'count', type: int), + // // Field(name: 'taskCode', type: String), + // Field( + // name: fieldName, + // type: fieldType, + // nullable: true, + // isRequired: false), + // ], + // ); + print(clazz1.buildClass()); + if(_dirPath.text.isNotEmpty){ + clazz1.write2File(Directory(_dirPath.text)); + } + } + + +} + +class GenInput extends StatelessWidget { + final String hintText; + final String label; + final TextEditingController? controller; + + const GenInput( + {Key? key, required this.hintText, this.controller, required this.label}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Wrap( + direction: Axis.vertical, + children: [ + Padding( + padding: const EdgeInsets.all(6.0), + child: Text( + label, + style: TextStyle(fontSize: 12), + ), + ), + SizedBox( + width: 150, + height: 30, + child: TextField( + controller: controller, + style: TextStyle(fontSize: 14), + decoration: InputDecoration( + filled: true, + hoverColor: Colors.transparent, + contentPadding: EdgeInsets.only(top: 0, left: 15), + fillColor: Color(0xffF1F2F3), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Theme.of(context).primaryColor), + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + hintText: hintText, + hintStyle: TextStyle(fontSize: 12, color: Colors.grey)), + ), + ), + ], + ); + } +} + +class GenCheckBox extends StatelessWidget { + final String label; + final bool checked; + final ValueChanged? onChanged; + + const GenCheckBox({ + Key? key, + required this.label, + required this.checked, + this.onChanged, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + direction: Axis.vertical, + children: [ + Checkbox(value: checked, onChanged: onChanged), + Text( + label, + style: TextStyle(fontSize: 12), + ), + ], + ); + } +} diff --git a/modules/tools_system/treasure_tools/lib/src/data/task_result.dart b/modules/tools_system/treasure_tools/lib/src/data/task_result.dart new file mode 100644 index 000000000..3e9b3fd8f --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/data/task_result.dart @@ -0,0 +1,46 @@ +class TaskResult { + int cost; + String taskName; + int count; + String taskCode; + String taskInfo; + + TaskResult({ + required this.cost, + required this.taskName, + required this.count, + required this.taskCode, + required this.taskInfo, + }); + + TaskResult copyWith({ + int? cost, + String? taskName, + int? count, + String? taskCode, + String? taskInfo, + }) => + TaskResult( + cost: cost ?? this.cost, + taskName: taskName ?? this.taskName, + count: count ?? this.count, + taskCode: taskCode ?? this.taskCode, + taskInfo: taskInfo ?? this.taskInfo, + ); + + static TaskResult fromJson(Map map) => TaskResult( + cost: map["cost"] , + taskName: map["taskName"] , + count: map["count"] , + taskCode: map["taskCode"] , + taskInfo: map["taskInfo"] , + ); + + Map toJson()=>{ + "cost": cost, + "taskName": taskName, + "count": count, + "taskCode": taskCode, + "taskInfo": taskInfo, + }; +} 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/modules/tools_system/treasure_tools/lib/src/icon_font_gen/gen_message_action.dart b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/gen_message_action.dart new file mode 100644 index 000000000..78095d468 --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/gen_message_action.dart @@ -0,0 +1,41 @@ +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; + +class GenMessageAction extends StatelessWidget { + final VoidCallback onGen; + const GenMessageAction({Key? key, required this.onGen}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Text( + '使用方式:\n1. 在 iconfont.cn 挑选图标,加入项目,下载压缩包。\n2. 选择 Flutter 项目地址,配置资源、产物文件位置。\n3. 点击生成代码按钮,即可生成相关代码。', + style: TextStyle( + color: Theme.of(context).primaryColor, + fontWeight: FontWeight.bold), + )), + ElevatedButton( + style: ElevatedButton.styleFrom( + elevation: 0, shape: const StadiumBorder()), + onPressed: onGen, + child: Wrap( + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 4, + children: [ + Icon( + TolyIcon.icon_fast, + size: 16, + ), + const Text( + '生成代码', + style: TextStyle(height: 1.1, fontSize: 12), + ), + ], + )), + ], + ); + } +} diff --git a/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_class_parser.dart b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_class_parser.dart new file mode 100644 index 000000000..224bd81e1 --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_class_parser.dart @@ -0,0 +1,110 @@ +import 'dart:convert'; +import 'dart:io'; +import 'package:archive/archive_io.dart'; + +import 'icon_font_gen_config.dart'; +import 'package:path/path.dart' as path; + +class IconFontClassParser{ + + void gen(IconFontGenConfig config){ + final inputStream = InputFileStream(config.srcZip); + // 将压缩包有用资源解压到目标文件 + final archive = ZipDecoder().decodeStream(inputStream); + for (var file in archive.files) { + if (file.isFile) { + if (file.name.endsWith('.ttf')) { + final outputStream = OutputFileStream(config.ttfDistPath); + file.writeContent(outputStream); + outputStream.close(); + } + if (file.name.endsWith('.json')) { + dynamic data = file.content; + String jsonContent = utf8.decode(data); + String resultCode = parser(jsonContent,config.fontFamily); + File distFile = File(config.distFilePath); + if(!distFile.existsSync()){ + distFile.createSync(recursive: true); + } + distFile.writeAsStringSync(resultCode); + setYaml(config); + } + } + } + } + + String parser(String input,String fontFamily){ + dynamic map = json.decode(input); + List glyphs = map['glyphs'] as List; + String code = ''; + for(int i=0;i 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: $familyName', + ' fonts:', + ' - asset: $fontAssetsDist', + ]; + + lines.insertAll(index+1, fonts); + 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: $familyName', + ' fonts:', + ' - asset: $fontAssetsDist', + ]; + lines.insertAll(index+1, fonts); + pubspecFile.writeAsStringSync(lines.join('\n')); + } + } +} \ No newline at end of file diff --git a/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_gen_config.dart b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_gen_config.dart new file mode 100644 index 000000000..dc058ed40 --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_gen_config.dart @@ -0,0 +1,47 @@ +import 'package:path/path.dart' as path; + +class IconFontGenConfig { + final String srcZip; + final String projectPath; + final String assetsDist; + final String fileDist; + + IconFontGenConfig({ + this.srcZip = '', + this.projectPath = '', + String? assetsDist, + String? fileDist, + }) : fileDist = fileDist ?? 'lib${spa}app${spa}gen${spa}toly_icon.dart', + assetsDist = assetsDist ?? 'assets${spa}iconfont'; + + static String get spa => path.separator; + + String get distFilePath => path.join(projectPath, fileDist); + + String get distAssetsDir => path.join(projectPath, assetsDist); + + String get ttfDistPath => path.join(distAssetsDir, path.basenameWithoutExtension(fileDist)+".ttf"); + + String get yamlAssetDist => assetsDist.replaceAll('\\', '/')+"/"+path.basename(ttfDistPath); + + String get fontFamily => path + .basenameWithoutExtension(fileDist) + .split('_') + .map((e) => e[0].toUpperCase() + e.substring(1,)) + .join(''); + + factory IconFontGenConfig.fromJson(Map map) { + return IconFontGenConfig( + srcZip: map['srcZip'] ?? '', + projectPath: map['projectPath'] ?? '', + assetsDist: map["assetsDist"] ?? '', + fileDist: map["fileDist"] ?? ''); + } + + Map toJson() => { + 'srcZip': srcZip, + 'projectPath': projectPath, + 'assetsDist': assetsDist, + 'fileDist': fileDist, + }; +} diff --git a/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_gen_page.dart b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_gen_page.dart new file mode 100644 index 000000000..39b7a0e4a --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/icon_font_gen/icon_font_gen_page.dart @@ -0,0 +1,294 @@ +import 'dart:convert'; +import 'package:app/app.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:path/path.dart' as path; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:utils/utils.dart'; +import 'gen_message_action.dart'; +import 'icon_font_class_parser.dart'; +import 'icon_font_gen_config.dart'; + +class IconFontGenPage extends StatefulWidget { + const IconFontGenPage({Key? key}) : super(key: key); + + @override + State createState() => _IconFontGenPageState(); +} + +class _IconFontGenPageState extends State + with AutomaticKeepAliveClientMixin { + + final TextEditingController _projectCtrl = TextEditingController(); + final TextEditingController _iconFontCtrl = TextEditingController(); + final TextEditingController _iconFontAssetsCtrl = TextEditingController(); + final TextEditingController _resultFileCtrl = TextEditingController(); + +// + + String get spa => path.separator; + late SharedPreferences _sp; + SharedPreferences get sp => _sp; + @override + void initState() { + super.initState(); + _initData(); + } + + IconFontGenConfig config = IconFontGenConfig(); + + void _initData() async{ + _sp = await SharedPreferences.getInstance(); + String? configStr = sp.getString(SpKey.iconFontGenConfig); + if(configStr!=null){ + config = IconFontGenConfig.fromJson(json.decode(configStr)); + } + _iconFontAssetsCtrl.text = config.assetsDist; + _resultFileCtrl.text = config.fileDist; + _projectCtrl.text = config.projectPath; + _iconFontCtrl.text = config.srcZip; + } + + + @override + Widget build(BuildContext context) { + super.build(context); + return Center( + child: SizedBox( + width: 600, + child: Column( + children: [ + const SizedBox( + height: 8, + ), + FileSelectorInput( + controller: _iconFontCtrl, + label: 'Iconfont 压缩包路径', + // controller: clazz.nameCtrl, + hintText: '请选择或输入 iconfont 下载的压缩包路径', + ), + const SizedBox(height: 10), + + FileSelectorInput( + pickerDir: true, + controller: _projectCtrl, + label: '项目路径', + // controller: clazz.nameCtrl, + hintText: '请选择或输入项目地址', + ), + const SizedBox( + height: 10, + ), + Row( + children: [ + Expanded( + child: LabelInputInput( + controller: _iconFontAssetsCtrl, + label: '资源目录', + hintText: 'iconfont 资源存放位置', + ), + ), + const SizedBox(width: 20), + Expanded( + child: LabelInputInput( + controller: _resultFileCtrl, + label: '产物位置', + hintText: '代码类存放位置', + ), + ), + ], + ), + + Expanded( + child: Align( + alignment: Alignment(1, -0.8), + child: GenMessageAction( + onGen: doGen, + ), + )) + ], + ), + ), + ); + } + + final IconFontClassParser parser = IconFontClassParser(); + + void doGen() { + if (_iconFontCtrl.text.isEmpty) return; + if (_projectCtrl.text.isEmpty) return; + + IconFontGenConfig config = IconFontGenConfig( + assetsDist: _iconFontAssetsCtrl.text, + fileDist: _resultFileCtrl.text, + projectPath: _projectCtrl.text, + srcZip: _iconFontCtrl.text, + ); + parser.gen(config); + Toast.success(context, '生成代码成功!'); + + sp.setString(SpKey.iconFontGenConfig,json.encode(config)); + } + + + + @override + bool get wantKeepAlive => true; +} + +class LabelInputInput extends StatelessWidget { + final String hintText; + final String label; + final TextEditingController? controller; + + const LabelInputInput({ + Key? key, + required this.hintText, + this.controller, + required this.label, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + // direction: Axis.vertical, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 6), + child: Text( + label, + style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ), + ), + Expanded( + child: SizedBox( + height: 35, + child: TextField( + controller: controller, + style: TextStyle(fontSize: 14), + decoration: InputDecoration( + filled: true, + hoverColor: Colors.transparent, + contentPadding: EdgeInsets.only(top: 0, left: 15), + fillColor: isDark?null:Color(0xffF1F2F3), + focusedBorder: OutlineInputBorder( + borderSide: + BorderSide(color: Theme.of(context).primaryColor), + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: isDark?Color(0xff2C3036):Color(0xffE2E7EE)), + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + hintText: hintText, + hintStyle: TextStyle(fontSize: 12, color: Colors.grey)), + ), + ), + ), + ], + ); + } +} + +class FileSelectorInput extends StatelessWidget { + final String hintText; + final String label; + final bool pickerDir; + final TextEditingController? controller; + + const FileSelectorInput( + {Key? key, + required this.hintText, + this.controller, + required this.label, + this.pickerDir = false}) + : super(key: key); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + // direction: Axis.vertical, + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 6), + child: Text( + label, + style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold), + ), + ), + SizedBox( + height: 35, + child: TextField( + controller: controller, + style: TextStyle(fontSize: 14), + decoration: InputDecoration( + suffixIconConstraints: BoxConstraints(maxWidth: 80), + suffixIcon: Row( + children: [ + VerticalDivider( + width: 1, + ), + Expanded( + child: MouseRegion( + cursor: SystemMouseCursors.click, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: _showSelectFile, + child: Center( + child: Text( + "选择", + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).primaryColor), + )), + ), + )), + ], + ), + filled: true, + hoverColor: Colors.transparent, + contentPadding: EdgeInsets.only(top: 0, left: 15), + fillColor: isDark?null:Color(0xffF1F2F3), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Theme.of(context).primaryColor), + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: isDark?Color(0xff2C3036):Color(0xffE2E7EE)), + borderRadius: BorderRadius.all(Radius.circular(6)), + ), + hintText: hintText, + hintStyle: TextStyle(fontSize: 12, color: Colors.grey)), + ), + ), + ], + ); + } + + void _showSelectFile() async { + String? path; + if (pickerDir) { + path = await FilePicker.platform.getDirectoryPath(); + } else { + FilePickerResult? result = await FilePicker.platform.pickFiles(); + if (result != null) { + path = result.files.single.path; + } else { + // User canceled the picker + } + } + if (path != null) { + controller?.text = path; + } else { + // User canceled the picker + } + } +} diff --git a/modules/tools_system/treasure_tools/lib/src/model/class.dart b/modules/tools_system/treasure_tools/lib/src/model/class.dart new file mode 100644 index 000000000..c9881c85b --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/model/class.dart @@ -0,0 +1,101 @@ +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:path/path.dart' as path; + +import 'field.dart'; + +class Class { + final List fields; + final String name; + final bool toJson; + final bool fromJson; + final bool copyWith; + + Class({ + required this.fields, + this.toJson = true, + this.fromJson = true, + this.copyWith = true, + required this.name, + }); + + Class copy({ + List? fields, + String? name, + bool? toJson, + bool? fromJson, + bool? copyWith, + }) { + return Class( + fields: fields ?? this.fields, + name: name ?? this.name, + toJson: toJson ?? this.toJson, + fromJson: fromJson ?? this.fromJson, + copyWith: copyWith ?? this.copyWith, + ); + } + + Future write2File(Directory directory) { + File file = File(path.join(directory.path, "${fileName}.dart")); + return file.writeAsString(buildClass()); + } + + RegExp regExp = RegExp(r'[A-Z].*?(?=([A-Z]|\b))'); + + String get fileName { + return regExp + .allMatches(name) + .map((e) => e.group(0)?.toLowerCase()) + .join("_"); + } + + String buildClass() { + String defines = ''; + String constructParams = ''; + String toJsonStatements = ''; + String fromJsonStatements = ''; + String copyWithParamStatements = ''; + String copyWithStatements = ''; + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + defines += "${field.defineStatement}\n"; + constructParams += field.paramStatement; + toJsonStatements += field.toJsonStatement; + fromJsonStatements += field.fromJsonStatement; + copyWithParamStatements += field.copyWithParamStatement; + copyWithStatements += field.copyWithStatement; + if (i != fields.length - 1) { + constructParams += '\n'; + toJsonStatements += '\n'; + fromJsonStatements += '\n'; + copyWithParamStatements += '\n'; + copyWithStatements += '\n'; + } + } + + String result = """ +class $name { +$defines + $name({ +$constructParams + }); + + $name copyWith({ +$copyWithParamStatements + }) => + $name( +$copyWithStatements + ); + + static $name fromJson(Map map) => $name( +$fromJsonStatements + ); + + Map toJson()=>{ +$toJsonStatements + }; +} +"""; + return result; + } +} diff --git a/modules/tools_system/treasure_tools/lib/src/model/field.dart b/modules/tools_system/treasure_tools/lib/src/model/field.dart new file mode 100644 index 000000000..dc95067a2 --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/model/field.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class Field { + final String modifier; + final String name; + final bool nullable; + final bool isRequired; + final String type ; + + Field({ + this.nullable = false, + this.isRequired = true, + this.modifier = '', + required this.type, + required this.name, + }); + + String get defineStatement { + String nullableArg = nullable ? '?' : ''; + return ' $modifier $type$nullableArg $name;'; + } + + String get paramStatement { + String requiredArg = isRequired ? 'required' : ''; + return ' $requiredArg this.$name,'; + } + + String get toJsonStatement { + return ' "$name": $name,'; + } + + String get fromJsonStatement { + return ' $name: map["$name"] ,'; + } + + String get copyWithParamStatement { + return ' $type? $name,'; + } + + String get copyWithStatement { + return ' $name: $name ?? this.$name,'; + } +} \ No newline at end of file diff --git a/modules/tools_system/treasure_tools/lib/src/popable/class_gen_field.dart b/modules/tools_system/treasure_tools/lib/src/popable/class_gen_field.dart new file mode 100644 index 000000000..562c927be --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/popable/class_gen_field.dart @@ -0,0 +1,112 @@ +import 'package:flutter/material.dart'; + +import '../code_gen_page.dart'; +import '../model/class.dart'; +import '../model/field.dart'; +import 'toly_select.dart'; + +class ClassGenField extends StatelessWidget { + + final Class clazz; + + const ClassGenField({Key? key,required this.clazz}) : super(key: key); + + final List selectData = const [ + "final", + "static", + 'static const', + ]; + + @override + Widget build(BuildContext context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + GenInput( + label: '类名', + // controller: clazz.nameCtrl, + hintText: '输入类名', + ), + const SizedBox( + width: 20, + ), + Wrap( + spacing: 20, + children: [ + GenCheckBox( + label: 'toJson', checked: true,onChanged: (v){}, + ), + GenCheckBox( + label: 'fromJson', checked: true,onChanged: (v){}, + ), + GenCheckBox( + label: 'copyWith', checked: true,onChanged: (v){}, + ), + ], + ) + ], + ), + const SizedBox( + height: 20, + ), + Expanded( + child: ListView.separated( + itemCount: clazz.fields.length, + separatorBuilder: (_,__)=> const SizedBox(height: 10,), + itemBuilder: (_,index) { + Field field = clazz.fields[index]; + return Row( + children: [ + GenInput( + label: '类型', + // controller: field.typeCtrl, + hintText: '输入字段类型名', + ), + const SizedBox( + width: 20, + ), + GenInput( + label: '变量名', + // controller: field.nameCtrl, + hintText: '输入字段变量名', + ), + const SizedBox( + width: 20, + ), + Wrap( + direction: Axis.vertical, + children: [ + Padding( + padding: const EdgeInsets.all(6.0), + child: const Text('修饰',style: TextStyle(fontSize: 12),), + ), + TolySelect( + height: 30, + width: 120, + change: (int index){ + // field.typeCtrl.text = selectData[index]; + }, + activeIndex: selectData.indexOf(field.modifier)==-1?0:selectData.indexOf(field.modifier), + data: selectData, + ) + ], + ), + const SizedBox( + width: 20, + ), + + GenCheckBox( + label: 'nullable', checked: field.nullable,onChanged: (v){}, + ), + + ], + ); + }), + ) + , + ], + ); + } +} diff --git a/modules/tools_system/treasure_tools/lib/src/popable/toly_select.dart b/modules/tools_system/treasure_tools/lib/src/popable/toly_select.dart new file mode 100644 index 000000000..6a0a158dc --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/popable/toly_select.dart @@ -0,0 +1,251 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +import '../wrapper.dart'; + +typedef OnDropSelected = void Function(int index); + +class TolySelectTapRegion extends TapRegion { + /// Creates a const [TextFieldTapRegion]. + /// + /// The [child] field is required. + const TolySelectTapRegion({ + super.key, + required super.child, + super.enabled, + super.onTapOutside, + super.onTapInside, + super.debugLabel, + }) : super(groupId: TolySelect); +} + + +class TolySelect extends StatefulWidget { + final List data; + final int activeIndex; + final OnDropSelected change; + final Color disableColor; + final double iconSize; + final double height; + final double width; + final double fontSize; + + const TolySelect( + {Key? key, + this.data = const [], + required this.activeIndex, + required this.change, + this.disableColor = const Color(0xffdcdfe6), + this.iconSize = 20, + this.height = 40, + this.width = 240, + this.fontSize = 14, + }) + : super(key: key); + + @override + _TolySelectState createState() => _TolySelectState(); +} + +class _TolySelectState extends State + with SingleTickerProviderStateMixin { + late FocusNode _node; + bool _focused = false; + late FocusAttachment _nodeAttachment; + OverlayEntry? _overlayEntry; + late AnimationController _ctrl; + late Animation animation; + final LayerLink layerLink = LayerLink(); + + // int _selectedIndex = 0; + + + + @override + void initState() { + super.initState(); + + _ctrl = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 200), + ); + + animation = Tween(begin: 0, end: pi).animate(_ctrl); + _node = FocusNode() + ..addListener(() { + if (_node.hasFocus != _focused) { + if (!_focused) { + _ctrl.forward(); + _showOverlay(); + } else { + _hideOverlay(); + _ctrl.reverse(); + } + setState(() { + _focused = _node.hasFocus; + }); + } + }); + _nodeAttachment = _node.attach(context); + } + + @override + void dispose() { + _node.dispose(); + _ctrl.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + _nodeAttachment.reparent(); + return TolySelectTapRegion( + onTapOutside: (PointerDownEvent event){ + _node.unfocus(); + }, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (_focused) { + _node.unfocus(); + } else { + _node.requestFocus(); + } + }, + child: CompositedTransformTarget( + link: layerLink, + child: buildTarget(), + ), + ), + ); + } + + void _showOverlay() { + _overlayEntry = _createOverlayEntry(); + Overlay.of(context)?.insert(_overlayEntry!); + } + + void _hideOverlay() { + _overlayEntry?.remove(); + } + + Widget buildTarget() { + return Container( + width: widget.width, + height: widget.height, + padding: const EdgeInsets.only(left: 10, right: 10), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + border: Border.all( + color: _focused ? Color(0xff409eff) : widget.disableColor, + )), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.data.isNotEmpty ? widget.data[widget.activeIndex] : "暂无数据",style: TextStyle( + height: 1, + fontSize: widget.fontSize, + color: Color(0xff606266) + ),), + AnimatedBuilder( + animation: animation, + builder: (_, child) => Transform.rotate( + angle: -animation.value, + child: child, + ), + child: Icon( + Icons.keyboard_arrow_down, + color: Color(0xffc0c4cc), + size: widget.iconSize, + ), + ), + ], + ), + ); + } + + OverlayEntry _createOverlayEntry() => OverlayEntry( + builder: (BuildContext context) => UnconstrainedBox( + child: CompositedTransformFollower( + link: layerLink, + targetAnchor: Alignment.bottomCenter, + followerAnchor: Alignment.topCenter, + child: Padding( + padding: const EdgeInsets.only(top: 4.0), + child: Material( + color: Colors.transparent, + shape: const RoundedRectangleBorder( + side: BorderSide.none, + borderRadius: BorderRadius.all(Radius.circular(4))), + elevation: 0, + child: Wrapper( + padding: EdgeInsets.zero, + spineType: SpineType.top, + color: Colors.white, + strokeWidth: 1, + radius: 4, + shadows: [ + BoxShadow( + offset: Offset(0,2), + blurRadius: 8, + + color: Colors.black.withOpacity(0.1) + ) + ], + // elevation: 3, + // shadowColor: Colors.black.withOpacity(0.1), + child: Container( + height: min(34.0*widget.data.length+10,150), + // alignment: Alignment.center, + // decoration: const BoxDecoration( + // color: Color(0xffDAE3FF), + // ), + width: widget.width, + child: ListView.builder( + itemExtent: 34, + padding: EdgeInsets.symmetric(vertical: 5), + // shrinkWrap: true, + itemCount: widget.data.length, + itemBuilder: _buildItem), + ), + ), + ), + ), + ), + ), + ); + + Widget _buildItem(BuildContext context, int index) { + return Material( + // color: Colors.white, + color: Colors.transparent, + child: TolySelectTapRegion( + child: InkWell( + onTap: () { + if (widget.activeIndex != index) widget.change.call(index); + _overlayEntry?.markNeedsBuild(); + _node.unfocus(); + }, + hoverColor: Color(0xffF5F7FA), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8,horizontal: 15), + color: Colors.transparent, + // color: index == _selectedIndex + // ? Colors.blue.withOpacity(0.2) + // : Colors.transparent, + child: + Text(widget.data[index], + style: TextStyle(fontSize: widget.fontSize, + fontWeight: index == widget.activeIndex?FontWeight.bold:null, + color: index == widget.activeIndex?Theme.of(context).primaryColor:null + ), + + )), + ), + ), + ); + } +} \ No newline at end of file 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/modules/tools_system/treasure_tools/lib/src/wrapper.dart b/modules/tools_system/treasure_tools/lib/src/wrapper.dart new file mode 100644 index 000000000..43a3c46e3 --- /dev/null +++ b/modules/tools_system/treasure_tools/lib/src/wrapper.dart @@ -0,0 +1,335 @@ +import 'dart:math'; + +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +/// create by 张风捷特烈 on 2020/9/18 +/// contact me by email 1981462002@qq.com +/// 说明: + +/// spineHeight : 针尖高度 +/// angle : 针尖角度 (角度值) +/// radius : 圆角半径 +/// offset : 偏移 +/// spineType : 类型 +/// color : 颜色 +/// +/// 宽高由父容器指定: 如 +/// Container( +// height: 50, +// width: 100, +// child: Wrapper() +// ) + +typedef SpinePathBuilder = Path Function( + Canvas canvas, SpineType spineType, Rect range); + +class Wrapper extends StatelessWidget { + final double spineHeight; + final double angle; + + final double radius; + final double offset; + final SpineType spineType; + final Color color; + final Widget? child; + final SpinePathBuilder? spinePathBuilder; + + final double? strokeWidth; + + final bool formEnd; + final EdgeInsets padding; + + final double? elevation; + final Color shadowColor; + final List? shadows; + + Wrapper( + { this.spineHeight = 8.0, + this.angle = 75, + this.radius = 5.0, + this.offset = 15, + this.shadows, + this.strokeWidth, + this.child, + this.elevation, + this.shadowColor = Colors.grey, + this.formEnd = false, + this.color = Colors.green, + this.spinePathBuilder, + this.padding = const EdgeInsets.all(8), + this.spineType = SpineType.left}); + + Wrapper.just({ + this.radius = 5.0, + this.strokeWidth, + this.child, + this.elevation, + this.shadows, + this.shadowColor = Colors.grey, + this.color = Colors.green, + this.padding = const EdgeInsets.all(8), + }) : spineHeight = 0, + angle = 0, + offset = 0, + spineType = SpineType.bottom, + spinePathBuilder = null, + formEnd = false; + + @override + Widget build(BuildContext context) { + var _padding = padding; + switch (spineType) { + case SpineType.top: + _padding = padding + EdgeInsets.only(top: spineHeight); + break; + case SpineType.left: + _padding = padding + EdgeInsets.only(left: spineHeight); + break; + case SpineType.right: + _padding = padding + EdgeInsets.only(right: spineHeight); + break; + case SpineType.bottom: + _padding = padding + EdgeInsets.only(bottom: spineHeight); + break; + } + + return CustomPaint( + child: Padding( + padding: _padding, + child: child, + ), + painter: WrapperPainter( + spineHeight: spineHeight, + angle: angle, + radius: radius, + offset: offset, + shadows: shadows, + strokeWidth: strokeWidth, + color: color, + shadowColor: shadowColor, + elevation: elevation, + spineType: spineType, + formBottom: formEnd, + spinePathBuilder: spinePathBuilder), + ); + } +} + +enum SpineType { top, left, right, bottom } + +class WrapperPainter extends CustomPainter { + final Paint mPaint; + + var path = Path(); + + final double? strokeWidth; + final SpinePathBuilder? spinePathBuilder; + final List? shadows; + + final double? elevation; + final Color shadowColor; + final double spineHeight; + final double angle; + final bool formBottom; + final double radius; + final double offset; + final SpineType spineType; + final Color color; + + WrapperPainter( + {this.spineHeight = 10.0, + this.angle = 75, + this.spinePathBuilder, + this.radius = 5.0, + this.offset = 15, + this.elevation, + this.shadows, + this.strokeWidth, + this.shadowColor = Colors.grey, + this.color = Colors.green, + this.formBottom = false, + this.spineType = SpineType.left}) + : mPaint = Paint() + ..color = color + ..style = + strokeWidth == null ? PaintingStyle.fill : PaintingStyle.stroke + ..strokeWidth = strokeWidth == null ? 1 : strokeWidth; + + @override + void paint(Canvas canvas, Size size) { + path = buildBoxBySpineType( + canvas, + spineType, + size.width, + size.height, + ); + + Path? spinePath; + + if (spinePathBuilder == null) { + spinePath = buildDefaultSpinePath(canvas, spineHeight, spineType, size); + } else { + + Rect range ; + switch(spineType){ + case SpineType.top: + range = Rect.fromLTRB(0, -spineHeight, size.width, 0); + break; + case SpineType.left: + range = Rect.fromLTRB(-spineHeight, 0, 0, size.height); + break; + case SpineType.right: + range = Rect.fromLTRB(-spineHeight, 0, 0, size.height).translate(size.width, 0); + break; + case SpineType.bottom: + range = Rect.fromLTRB(0, 0, size.width, spineHeight).translate(0, size.height-spineHeight); + break; + } + if(spinePathBuilder !=null){ + spinePath = spinePathBuilder!(canvas, spineType, range); + } + } + + if (spinePath != null) { + path = Path.combine(PathOperation.union, spinePath, path); + if(shadows!=null){ + drawShadows(canvas,path, shadows!); + } + // if (elevation != null) { + // canvas.drawShadow(path, shadowColor, elevation!, true); + // return; + // } + canvas.drawPath(path, mPaint..color=Color(0xffe4e7ed)); + } + + } + + void drawShadows(Canvas canvas, Path path, List shadows) { + for (final BoxShadow shadow in shadows) { + final Paint shadowPainter = shadow.toPaint(); + if (shadow.spreadRadius == 0) { + canvas.drawPath(path.shift(shadow.offset), shadowPainter); + } else { + Rect zone = path.getBounds(); + double xScale = (zone.width + shadow.spreadRadius) / zone.width; + double yScale = (zone.height + shadow.spreadRadius) / zone.height; + Matrix4 m4 = Matrix4.identity(); + m4.translate(zone.width / 2, zone.height / 2); + m4.scale(xScale, yScale); + m4.translate(-zone.width / 2, -zone.height / 2); + canvas.drawPath(path.shift(shadow.offset).transform(m4.storage), shadowPainter); + } + } + Paint whitePaint = Paint()..color = Colors.white; + canvas.drawPath(path, whitePaint); + } + + buildDefaultSpinePath( + Canvas canvas, double spineHeight, SpineType spineType, Size size) { + switch (spineType) { + case SpineType.top: return _drawTop(size.width, size.height, canvas); + case SpineType.left: + return _drawLeft(size.width, size.height, canvas); + case SpineType.right: + return _drawRight(size.width, size.height, canvas); + case SpineType.bottom: + return _drawBottom(size.width, size.height, canvas); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => false; + + Path _drawTop(double width, double height, Canvas canvas) { + var angleRad = pi / 180 * angle; + var spineMoveX = spineHeight * tan(angleRad / 2); + var spineMoveY = spineHeight; + if (spineHeight != 0) { + return Path() + ..moveTo(!formBottom ? offset : width - offset - spineHeight, 0) + ..relativeLineTo(spineMoveX, -spineMoveY) + ..relativeLineTo(spineMoveX, spineMoveY); + } + return Path(); + } + + Path _drawBottom(double width, double height, Canvas canvas) { + var lineHeight = height - spineHeight; + var angleRad = pi / 180 * angle; + var spineMoveX = spineHeight * tan(angleRad / 2); + var spineMoveY = spineHeight; + if (spineHeight != 0) { + return Path() + ..moveTo( + !formBottom ? offset : width - offset - spineHeight, lineHeight) + ..relativeLineTo(spineMoveX, spineMoveY) + ..relativeLineTo(spineMoveX, -spineMoveY); + } + return Path(); + } + + Path _drawLeft(double width, double height, Canvas canvas) { + var angleRad = pi / 180 * angle; + var spineMoveX = spineHeight; + var spineMoveY = spineHeight * tan(angleRad / 2); + if (spineHeight != 0) { + return Path() + ..moveTo(0, !formBottom ? offset : height - offset - spineHeight) + ..relativeLineTo(-spineMoveX, spineMoveY) + ..relativeLineTo(spineMoveX, spineMoveY); + } + return Path(); + } + + Path _drawRight(double width, double height, Canvas canvas) { + var lineWidth = width - spineHeight; + var angleRad = pi / 180 * angle; + var spineMoveX = spineHeight; + var spineMoveY = spineHeight * tan(angleRad / 2); + if (spineHeight != 0) { + return Path() + ..moveTo(lineWidth, !formBottom ? offset : height - offset - spineHeight) + ..relativeLineTo(spineMoveX, spineMoveY) + ..relativeLineTo(-spineMoveX, spineMoveY); + } + return Path(); + } + + Path buildBoxBySpineType( + Canvas canvas, + SpineType spineType, + double width, + double height, + ) { + double lineHeight, lineWidth; + + switch (spineType) { + case SpineType.top: + lineHeight = height - spineHeight; + canvas.translate(0, spineHeight); + lineWidth = width; + break; + case SpineType.left: + lineWidth = width - spineHeight; + lineHeight = height; + canvas.translate(spineHeight, 0); + break; + case SpineType.right: + lineWidth = width - spineHeight; + lineHeight = height; + break; + case SpineType.bottom: + lineHeight = height - spineHeight; + lineWidth = width; + break; + } + + Rect box = Rect.fromCenter( + center: Offset(lineWidth / 2, lineHeight / 2), + width: lineWidth, + height: lineHeight); + + return Path()..addRRect(RRect.fromRectXY(box, radius, radius)); + } +} 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/modules/tools_system/treasure_tools/test/iconfont_parser_test.dart b/modules/tools_system/treasure_tools/test/iconfont_parser_test.dart new file mode 100644 index 000000000..ae20198dc --- /dev/null +++ b/modules/tools_system/treasure_tools/test/iconfont_parser_test.dart @@ -0,0 +1,16 @@ + +import 'dart:convert'; +import 'dart:io'; + +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'; + File jsonFile = File(filePath); + final String jsonContent = jsonFile.readAsStringSync(); + IconFontClassParser parser = IconFontClassParser(); + String result = parser.parser(jsonContent,'TolyIcon'); + print(result); + +} \ No newline at end of file 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/modules/widget_system/widget_module/.gitignore b/modules/widget_system/widget_module/.gitignore new file mode 100644 index 000000000..96486fd93 --- /dev/null +++ b/modules/widget_system/widget_module/.gitignore @@ -0,0 +1,30 @@ +# 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/modules/widget_system/widget_module/.metadata b/modules/widget_system/widget_module/.metadata new file mode 100644 index 000000000..fbfa6dfba --- /dev/null +++ b/modules/widget_system/widget_module/.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: b8f7f1f9869bb2d116aa6a70dbeac61000b52849 + channel: stable + +project_type: package 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/modules/widget_system/widget_module/lib/blocs/category_bloc/category_bloc.dart b/modules/widget_system/widget_module/lib/blocs/category_bloc/category_bloc.dart new file mode 100644 index 000000000..cd4f03c9a --- /dev/null +++ b/modules/widget_system/widget_module/lib/blocs/category_bloc/category_bloc.dart @@ -0,0 +1,87 @@ +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'; + +/// create by 张风捷特烈 on 2020-04-21 +/// contact me by email 1981462002@qq.com +/// 说明: + +class CategoryBloc extends Bloc { + final CategoryRepository repository; + + CategoryBloc({required this.repository}) : super(const CategoryLoadingState()){ + on(_onCategoryEvent); + } + + void _onCategoryEvent(CategoryEvent event,Emitter emit) async { + if (event is EventLoadCategory) { + emit (const CategoryLoadingState()); + // 使用 repository 加载 收藏集数据 + final category = await repository.loadCategories(); + category.isEmpty + ? emit (const CategoryEmptyState()) + : emit (CategoryLoadedState(category)); + } + + if (event is EventDeleteCategory) { + await repository.deleteCategory(event.id); + add(const EventLoadCategory()); + } + + if (event is EventToggleWidget) { + await repository.toggleCategory(event.categoryId, event.widgetId); + add(const EventLoadCategory()); + } + + if (event is EventAddCategory) { + CategoryPo categoryPo = CategoryPo( + name: event.name, + color: event.color ?? '#FFF2F2F2', + info: event.info ?? '这里什么都没有...', + created: DateTime.now(), + updated: DateTime.now()); + + final success = await repository.addCategory(categoryPo); + + if (success) { + emit(const AddCategorySuccess()) ; + add(const EventLoadCategory()); + } else { + emit(const AddCategoryFailed()) ; + } + } + + if (event is EventUpdateCategory) { + CategoryPo categoryPo = CategoryPo( + id: event.id, + name: event.name, + priority: event.priority ?? 0, + image: event.image ?? '', + color: event.color ??'#FFF2F2F2', + info: event.info ?? '这里什么都没有...', + updated: DateTime.now()); + + final success = await repository.updateCategory(categoryPo); + + if (success) { +// yield AddCategorySuccess(); + add(const EventLoadCategory()); + } else { +// yield AddCategoryFailed(); + } + } + + } + + List get categories { + if(state is CategoryLoadedState){ + return (state as CategoryLoadedState).categories; + }else{ + return []; + } + } +} diff --git a/modules/widget_system/widget_module/lib/blocs/category_bloc/category_event.dart b/modules/widget_system/widget_module/lib/blocs/category_bloc/category_event.dart new file mode 100644 index 000000000..19b325d41 --- /dev/null +++ b/modules/widget_system/widget_module/lib/blocs/category_bloc/category_event.dart @@ -0,0 +1,77 @@ +part of 'category_bloc.dart'; +/// create by 张风捷特烈 on 2020-04-21 +/// contact me by email 1981462002@qq.com +/// 说明: 收藏集相关事件 + +abstract class CategoryEvent extends Equatable{ + const CategoryEvent(); + @override + List get props => []; +} +// 加载 收藏集 事件 +class EventLoadCategory extends CategoryEvent{ + const EventLoadCategory(); + @override + List get props => []; +} + +// 将一个 widget 添加/移除 收藏集 +class EventToggleWidget extends CategoryEvent{ + final int widgetId; + final int categoryId; + const EventToggleWidget({ + required this.widgetId, + required this.categoryId, + }); + + @override + List get props => [widgetId,categoryId]; +} + +// 删除 收藏集 +class EventDeleteCategory extends CategoryEvent{ + final int id; + + const EventDeleteCategory({required this.id}); + + @override + List get props => [id]; +} + +// 添加 收藏集 +class EventAddCategory extends CategoryEvent{ + final String name; + final String? info; + final String? color; + + const EventAddCategory({ + required this.name, + required this.info, + required this.color, + }); + + @override + List get props => [name, info, color]; +} + +// 更新 收藏集 +class EventUpdateCategory extends CategoryEvent { + final int id; + final String name; + final String? info; + final String? color; + final int? priority; + final String? image; + + const EventUpdateCategory({ + required this.name, + required this.info, + required this.color, + this.priority, + this.image, + required this.id, + }); + + @override + List get props => [name, info, color, priority, image, id]; +} \ No newline at end of file diff --git a/lib/blocs/category/category_state.dart b/modules/widget_system/widget_module/lib/blocs/category_bloc/category_state.dart similarity index 88% rename from lib/blocs/category/category_state.dart rename to modules/widget_system/widget_module/lib/blocs/category_bloc/category_state.dart index 84c493e49..d1e0b29a9 100644 --- a/lib/blocs/category/category_state.dart +++ b/modules/widget_system/widget_module/lib/blocs/category_bloc/category_state.dart @@ -1,5 +1,4 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/model/category_model.dart'; +part of 'category_bloc.dart'; /// create by 张风捷特烈 on 2020-04-21 /// contact me by email 1981462002@qq.com @@ -17,12 +16,14 @@ class CategoryLoadedState extends CategoryState { const CategoryLoadedState(this.categories); + @override List get props => [categories]; } class CategoryLoadingState extends CategoryState { const CategoryLoadingState(); + @override List get props => []; } @@ -30,6 +31,7 @@ class CategoryLoadingState extends CategoryState { class CategoryEmptyState extends CategoryState { const CategoryEmptyState(); + @override List get props => []; } 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/lib/blocs/category_widget/category_widget_state.dart b/modules/widget_system/widget_module/lib/blocs/category_widget_bloc/category_widget_state.dart similarity index 82% rename from lib/blocs/category_widget/category_widget_state.dart rename to modules/widget_system/widget_module/lib/blocs/category_widget_bloc/category_widget_state.dart index ae2f3e081..73636bf86 100644 --- a/lib/blocs/category_widget/category_widget_state.dart +++ b/modules/widget_system/widget_module/lib/blocs/category_widget_bloc/category_widget_state.dart @@ -1,5 +1,4 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter_unit/model/widget_model.dart'; +part of 'category_widget_bloc.dart'; /// create by 张风捷特烈 on 2020-04-21 /// contact me by email 1981462002@qq.com @@ -17,11 +16,13 @@ class CategoryWidgetLoadedState extends CategoryWidgetState { CategoryWidgetLoadedState(this.widgets); + @override List get props => [widgets]; } class CategoryWidgetEmptyState extends CategoryWidgetState{ + @override List get props => []; } 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/modules/widget_system/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 new file mode 100644 index 000000000..31376c33c --- /dev/null +++ b/modules/widget_system/widget_module/lib/blocs/widget_detail_bloc/widget_detail_state.dart @@ -0,0 +1,48 @@ +part of 'widget_detail_bloc.dart'; + +/// create by 张风捷特烈 on 2020-03-03 +/// contact me by email 1981462002@qq.com +/// 说明: 详情状态类 + +abstract class DetailState extends Equatable { + const DetailState(); + + @override + List get props => []; +} + +class DetailWithData extends DetailState { + final WidgetModel widgetModel; + final List links; + final List nodes; + + const DetailWithData({ + required this.widgetModel, + required this.nodes, + required this.links, + }); + + @override + List get props => [widgetModel, nodes]; + + @override + String toString() { + return 'DetailWithData{widget: $widgetModel, nodes: $nodes}'; + } + + DetailWithData copyWith({ + WidgetModel? widgetModel, + List? links, + List? nodes, + }) { + return DetailWithData( + widgetModel: widgetModel ?? this.widgetModel, + nodes: this.nodes, + links: this.links, + ); + } +} + +class DetailLoading 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/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_event.dart b/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_event.dart new file mode 100644 index 000000000..7dcdacbdd --- /dev/null +++ b/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_event.dart @@ -0,0 +1,40 @@ +part of 'widgets_bloc.dart'; + +/// create by 张风捷特烈 on 2020-03-03 +/// contact me by email 1981462002@qq.com +/// 说明: + +abstract class WidgetsEvent extends Equatable { + const WidgetsEvent(); + @override + List get props => []; +} + +class EventTabTap extends WidgetsEvent { + final WidgetFamily family; + final String? locale; + + const EventTabTap(this.family,{this.locale}); + + @override + List get props => [family]; +} + +class EventLoadMore extends WidgetsEvent { + + @override + List get props => []; +} + +class EventRefresh extends WidgetsEvent { + + @override + List get props => []; +} + + +class EventSearchWidget extends WidgetsEvent { + final WidgetFilter filter;//参数 + const EventSearchWidget({required this.filter}); +} + diff --git a/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_state.dart b/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_state.dart new file mode 100644 index 000000000..e9fd3efc2 --- /dev/null +++ b/modules/widget_system/widget_module/lib/blocs/widgets_bloc/widgets_state.dart @@ -0,0 +1,88 @@ +part of 'widgets_bloc.dart'; + +/// create by 张风捷特烈 on 2020-03-03 +/// contact me by email 1981462002@qq.com +/// 说明: 主页 Widget 列表 状态类 + +/// 对于一个可查询、可分页的数据集 +/// 状态类持有过滤数据 Filter, 任何衍生状态都需要感知过滤的信息,比如加载异常,点击重试,可以基于最后的过滤状态。 +/// 分页加载 信息需要 了解 总数据、页数、每页含量 +/// + +sealed class WidgetsState extends Equatable { + final WidgetFilter filter; + final LoadOperate operate; + + const WidgetsState({required this.filter, required this.operate}); + + @override + List get props => [filter]; +} + +class WidgetsLoading extends WidgetsState { + const WidgetsLoading({ + super.filter = const WidgetFilter(), + super.operate = LoadOperate.load, + }); +} + +/// [full] 是否已满,用于加载更多到底的标识 +/// [widgets] 加载的数据 +class WidgetsLoaded extends WidgetsState { + final List widgets; + final bool full; + final int fetchTime; + + const WidgetsLoaded({ + this.widgets = const [], + required super.filter, + required super.operate, + this.full = false, + this.fetchTime = 0, + }); + + @override + List get props => [widgets, full, filter, operate, fetchTime]; + + @override + String toString() { + return 'WidgetsLoaded{widgets: $widgets}'; + } + + WidgetsLoaded copyWith({ + List? widgets, + bool? full, + 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, + ); + } +} + +class WidgetsLoadFailed extends WidgetsState { + final String error; + + const WidgetsLoadFailed(this.error, + {required super.filter, required super.operate}); + + @override + List get props => [error]; +} + +/// 加载类型 +/// [load] 加载第一页数据, 用于首次加载, +/// [refresh] 下拉刷新状态标识。之前加载的更多数据将会被移除 +/// [more] 加载更多状态标识。 +/// +enum LoadOperate { + load, + refresh, + more, +} 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/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/link_widget_buttons.dart b/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/link_widget_buttons.dart new file mode 100644 index 000000000..b357dc4c4 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/desk_ui/widget_detail/link_widget_buttons.dart @@ -0,0 +1,54 @@ +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; + + const LinkWidgetButtons( + {Key? key, required this.links, required this.onSelect}) + : super(key: key); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; + + Color? chipColor = isDark + ? Theme.of(context).floatingActionButtonTheme.backgroundColor + : Theme.of(context).primaryColor; + + if (links.isEmpty) { + return Padding( + padding: const EdgeInsets.only(left: 10), + child: Chip( + backgroundColor: Colors.grey.withAlpha(120), + labelStyle: const TextStyle(fontSize: 12, color: Colors.white), + label: const Text('暂无链接组件'), + )); + } else { + return Padding( + padding: const EdgeInsets.only(left: 10.0, top: 10), + child: Wrap( + spacing: 5, + runSpacing: 5, + children: links + .map((WidgetModel model) => ActionChip( + labelPadding: EdgeInsets.zero, + side: BorderSide.none, + onPressed: () => onSelect(model), + elevation: 1, + // shadowColor: chipColor, + backgroundColor: chipColor, + labelStyle: model.deprecated + ? UnitTextStyle.deprecatedChip + : UnitTextStyle.commonChip, + label: Text(model.name), + )) + .toList(), + ), + ); + } + } +} 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/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/desk_search_bar.dart b/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/desk_search_bar.dart new file mode 100644 index 000000000..f41f2f1a1 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/desk_ui/widget_panel/desk_search_bar.dart @@ -0,0 +1,156 @@ +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_repository/widget_repository.dart'; + +class DeskSearchBar extends StatefulWidget { + final ValueChanged? onChanged; + + const DeskSearchBar({Key? key, this.onChanged}) : super(key: key); + + @override + State createState() => _DeskSearchBarState(); +} + +class _DeskSearchBarState extends State { + late TextEditingController _controller; + + @override + Widget build(BuildContext context) { + return Autocomplete( + optionsBuilder: buildOptions, + onSelected: onSelected, + optionsViewBuilder: _buildOptionsView, + fieldViewBuilder: _buildFieldView, + ); + + } + + void onSelected(WidgetModel model) { + final FocusScopeNode focusScope = FocusScope.of(context); + if (focusScope.hasFocus) { + focusScope.unfocus(); + } + _controller.clear(); + context.push('/widget/detail/${model.name}',extra: model); + } + + Future> buildOptions(TextEditingValue textEditingValue) async { + if (textEditingValue.text == '') { + return const Iterable.empty(); + } + return searchByArgs(textEditingValue.text); + + } + + Future> searchByArgs(String text) { + WidgetRepository repository = context.read().repository; + return repository.searchWidgets(WidgetFilter( + name: text + )); + } + + Widget _buildOptionsView(BuildContext context, AutocompleteOnSelected onSelected, Iterable options) { + + return Align( + alignment: Alignment.topLeft, + child: Material( + elevation: 6, + color: Colors.white, + borderRadius: BorderRadius.circular(8), + shadowColor: Colors.black, + child: ConstrainedBox( + constraints: BoxConstraints(maxHeight: 350,maxWidth: 250), + child:ListView.builder( + itemCount:options.length , + padding: EdgeInsets.symmetric(vertical: 10), + itemBuilder: (_,index) { + WidgetModel model = options.elementAt(index); + return InkWell( + onTap: ()=>onSelected(model), + child: Ink( + padding: EdgeInsets.symmetric(vertical: 6, horizontal: 15), + child: Row(children: [ + Expanded(child: Text.rich(formSpan(model.name,_controller.text),maxLines:1, + overflow: TextOverflow.ellipsis, + style: TextStyle(fontSize: 12))), + // Spacer(), + const SizedBox(width: 10,), + Text(model.nameCN,style: TextStyle(fontSize: 12),), + ],), + ), + ); + }), + ), + ), + ); + } + + Widget _buildFieldView(BuildContext context, TextEditingController textEditingController, FocusNode focusNode, VoidCallback onFieldSubmitted) { + _controller = textEditingController; + bool isDark = Theme.of(context).brightness == Brightness.dark; + + return TextField( + controller: textEditingController, + onChanged: widget.onChanged, + style: const TextStyle(fontSize: 12), + maxLines: 1, + focusNode: focusNode, + decoration: InputDecoration( + prefixIconConstraints: BoxConstraints( + minWidth: 30, + ), + filled: true, + hoverColor: Colors.transparent, + contentPadding: EdgeInsets.only(top: 0), + fillColor: isDark?null:Color(0xffF1F2F3), + prefixIcon: Icon( + Icons.search, + size: 18, + color: Colors.grey, + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: Theme.of(context).primaryColor), + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + + enabledBorder : OutlineInputBorder( + + borderSide: BorderSide.none, + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + hintText: context.l10n.enterComponentName, + hintStyle: 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_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/modules/widget_system/widget_module/lib/views/mobile/category_page/category_detail.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/category_detail.dart new file mode 100644 index 000000000..70fd7c1fe --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/category_detail.dart @@ -0,0 +1,159 @@ + +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_module/blocs/blocs.dart'; + +import 'package:widget_repository/widget_repository.dart'; + + + +/// create by 张风捷特烈 on 2020-04-22 +/// contact me by email 1981462002@qq.com +/// 说明: + +class CategoryShow extends StatelessWidget { + final CategoryModel model; + + const CategoryShow({Key? key, required this.model}) : super(key: key); + + @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: child, + ); + } + + Widget _buildWidgetList(List widgets) { + return ListView.separated( + separatorBuilder: (_, index) => const Divider(height: 1), + itemBuilder: (context, index) => Dismissible( + direction: DismissDirection.endToStart, + key: ValueKey(widgets[index].id), + background: Container( + padding: const EdgeInsets.only(right: 20), + alignment: Alignment.centerRight, + color: Colors.red, + child: const Icon( + CupertinoIcons.delete_solid, + color: Colors.white, + size: 30, + ), + ), + onDismissed: (v) { + BlocProvider.of(context).add( + EventToggleCategoryWidget(model.id!, widgets[index].id)); + }, + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), + child: FeedbackWidget( + duration: const Duration(milliseconds: 200), + onPressed: () => _toDetailPage(context, widgets[index]), + child: + // Container(height: 60,) + SimpleWidgetItem( + data: widgets[index], + )), + ), + ), + itemCount: widgets.length); + } + + void _toDetailPage(BuildContext context, WidgetModel model) async { + // Navigator.pushNamed(context, UnitRouter.widget_detail, arguments: model); + context.push('/widget/detail/${model.name}',extra: model); + } +} + +class SimpleWidgetItem extends StatelessWidget { + final WidgetModel data; + + const SimpleWidgetItem({Key? key, required this.data}) : super(key: key); + + @override + Widget build(BuildContext context) { + bool isDark = Theme.of(context).brightness == Brightness.dark; +Color shadowColor = isDark?Colors.black:Colors.white; + return Container( + height: 64, + child: Row( + children: [ + _buildLeading(shadowColor), + const SizedBox( + width: 8, + ), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [_buildTitle(shadowColor), _buildSummary(shadowColor)], + ), + ) + ], + ), + ); + } + + Widget _buildTitle(Color shadowColor) { + return Row( + children: [ + Expanded( + child: Text(data.name, + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + shadows: [ + Shadow(color: shadowColor, offset: Offset(.3, .3)) + ])), + ), + const SizedBox(width: 15), + StarScore( + star: Star(emptyColor: shadowColor, size: 12, fillColor: data.color), + score: data.lever, + ) + ], + ); + } + + Widget _buildLeading(Color shadowColor) => Padding( + padding: const EdgeInsets.only(left: 5, right: 5), + child: data.image == null + ? Material( + color: Colors.transparent, + child: CircleText( + + text: data.name, + size: 50, + color: data.color, + ), + ) + : CircleImage( + image: data.image!, + size: 50, + ), + ); + + Widget _buildSummary(Color shadowColor) { + return Text( + data.info, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: Color(0xFF757575), + fontSize: 12, + shadows: [Shadow(color: shadowColor, offset: Offset(.5, .5))]), + ); + } +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/category_page/category_list_item.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/category_list_item.dart new file mode 100644 index 000000000..570900f4c --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/category_list_item.dart @@ -0,0 +1,109 @@ +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 +/// 说明: + +class CategoryListItem extends StatelessWidget { + final CategoryModel data; + final Function(CategoryModel)? onDeleteItemClick; + final Function(CategoryModel)? onEditItemClick; + + const CategoryListItem({Key? key, required this.data, this.onDeleteItemClick,this.onEditItemClick}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + child: _buildChild(context), + padding: const EdgeInsets.all(8), + + decoration: BoxDecoration( + color: Theme.of(context).listTileTheme.tileColor, + boxShadow: [ + BoxShadow(color: Colors.black.withOpacity(0.01),blurRadius: 6,offset: Offset(0,2)) + ], + borderRadius: BorderRadius.all(Radius.circular(8)), + ), + ); + + } + + _buildChild(BuildContext context) { + final Color themeColor = Theme.of(context).primaryColor; + return Column( + children: [ + _buildTitle(themeColor), + const SizedBox(height: 8,), + const Divider(), + Expanded( + child: Container( + padding: const EdgeInsets.only(top: 10, left: 10, right: 10), + child: Text(data.info, + maxLines: 4, + style: TextStyle( + color: Colors.grey, + fontSize: 14, + shadows: [ + Shadow(color: themeColor.withOpacity(0.4), offset:const Offset(.2,.2),blurRadius: .5) + ])), + ),), + const Divider(), + const SizedBox(height: 8,), + Text( + '创建于: ${data.createDate}', + style: const TextStyle(color: Colors.grey, fontSize: 12), + ), + ], + ); + } + + Row _buildTitle(Color themeColor) { + return Row( + children: [ + CircleText( + text: "${data.count}", + size: 35, + fontSize: 14, + backgroundColor: data.color, + ), + const SizedBox( + width: 10, + ), + Expanded( + child: Text( + data.name, + style: const TextStyle(fontWeight: FontWeight.bold), + )), + FeedbackWidget( + onPressed: () { + onEditItemClick?.call(data); + }, + child: Icon( + Icons.edit, + size: 20, + color: themeColor, + ), + ), + const SizedBox(width: 4,), + FeedbackWidget( + onPressed: () { + onDeleteItemClick?.call(data); + }, + child: const Icon( + CupertinoIcons.delete_solid, + color: Colors.red, + size: 20, + ), + ), + + + ], + ); + } +} \ No newline at end of file diff --git a/modules/widget_system/widget_module/lib/views/mobile/category_page/category_page.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/category_page.dart new file mode 100644 index 000000000..05e953fc1 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/category_page.dart @@ -0,0 +1,161 @@ +import 'package:app/app.dart'; +import 'package:components/project_ui/project_ui.dart'; +import 'package:toly_ui/toly_ui.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: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, + // crossAxisCount: 2, + + // childAspectRatio: 0.8, + ); + + final SliverGridDelegateWithFixedCrossAxisCount deskGridDelegate = + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + mainAxisSpacing: 10, + crossAxisSpacing: 10, + childAspectRatio: 0.9, + ); + + const CategoryPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return BlocBuilder(builder: (ctx, state) { + if (state is CategoryLoadedState) { + return CustomScrollView( + slivers: [ + SliverOverlapInjector( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor(ctx), + ), + _buildContent(context, state), + ], + ); + } + if (state is CategoryLoadingState) return const LoadingShower(); + return const EmptyCategory(); + }); + } + + _buildContent(BuildContext context, CategoryLoadedState state) { + 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( + delegate: SliverChildBuilderDelegate( + (_, index) => GestureDetector( + onTap: () => + _toDetailPage(context, state.categories[index]), + child: CategoryListItem( + data: state.categories[index], + onDeleteItemClick: (model) => + _deleteCollect(context, model), + onEditItemClick: (model) => _editCollect(context, model), + )), + childCount: state.categories.length), + gridDelegate: delegate); + }), + ); + } + + 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) { + 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/modules/widget_system/widget_module/lib/views/mobile/category_page/collect_page.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/collect_page.dart new file mode 100644 index 000000000..36b875403 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/collect_page.dart @@ -0,0 +1,175 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.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'; +import 'category_page.dart'; +import 'like_widget_page.dart'; +import 'sync/async_button.dart'; +import 'sync/upload_button.dart'; + + +class CollectPageAdapter extends StatelessWidget { + const CollectPageAdapter({Key? key,}) : super(key: key); + + @override + Widget build(BuildContext context) { + bool isDesk = Platform.isMacOS||Platform.isWindows||Platform.isLinux; + if(isDesk){ + return const DeskCategoryPage(); + }else{ + return const CollectPage(canPop:true); + } + } +} + + + +class CollectPage extends StatefulWidget { + final bool canPop; + const CollectPage({Key? key, this.canPop=false}) : super(key: key); + + @override + _CollectPageState createState() => _CollectPageState(); +} + +class _CollectPageState extends State + with AutomaticKeepAliveClientMixin { + late final List _tabs = [ + context.l10n.widgetsInn, + context.l10n.likedWidgets, + ]; + + @override + Widget build(BuildContext context) { + super.build(context); + BuildContext _topContext = context; + final Color color = Colors.blue.withAlpha(11); + + return Scaffold( + // backgroundColor: Colors.white, + body: Container( + color: color, + child: DefaultTabController( + length: _tabs.length, + child: NestedScrollView( + headerSliverBuilder: (context, innerBoxIsScrolled) => [ + SliverOverlapAbsorber( + handle: NestedScrollView.sliverOverlapAbsorberHandleFor( + context), + sliver: _buildAppBar(_topContext, innerBoxIsScrolled)), + ], + body: const TabBarView( + children: [ + CategoryPage(), + LikeWidgetPage(), + ], + ), + ), + ), + )); + } + + Widget _buildAppBar(BuildContext context, bool index) { + // final Color color = Colors.blue; + bool isDark = Theme.of(context).brightness == Brightness.dark; + String image = isDark?'draw_bg3.webp':'caver.webp'; + return SliverAppBar( + systemOverlayStyle: const SystemUiOverlayStyle( + statusBarIconBrightness: Brightness.light + ), + leading: widget.canPop?BackButton(color: Colors.white,):Container( + margin: const EdgeInsets.all(10), + child: FeedbackWidget( + onPressed: () { + // Navigator.of(context).pushNamed(UnitRouter.login); + }, + child: const CircleImage( + image: AssetImage('assets/images/icon_head.webp'), + borderSize: 1.5, + ), + )), + backgroundColor: Theme.of(context).primaryColor, + actionsIconTheme: IconThemeData(color: Colors.white), + actions: [ + SizedBox( + width: 32, + child: AuthenticWidget.just(const UploadCategoryButton())), + // SizedBox(width: 5,), + + SizedBox( + width: 32, + child: AuthenticWidget.just(const SyncCategoryButton())), + if(!widget.canPop) + SizedBox(child: _buildAddAction(context)) + ], + title: Text( + context.l10n.collectCollection, + style: TextStyle( + color: Colors.white, //标题 + fontSize: 18, + shadows: [ + Shadow(color: Colors.blue, offset:Offset(1, 1), blurRadius: 2) + ]), + ), + pinned: true, + expandedHeight: 150.0, + flexibleSpace: FlexibleSpaceBar( + collapseMode: CollapseMode.parallax, //视差效果 + background: Image.asset( + "assets/images/$image", + fit: BoxFit.cover, + ), + ), + forceElevated: index, + bottom: PreferredSize( + preferredSize: const Size.fromHeight(30), + child: TabBar( + indicatorColor: Colors.transparent, + unselectedLabelColor: Colors.white, + labelColor: Colors.black, + labelStyle: TextStyle(fontSize: 16, shadows: [ + Shadow( + color: Theme.of(context).primaryColor, + offset: const Offset(1, 1), + blurRadius: 10) + ]), + tabs: _buildTabs(), + ), + ), + ); + } + + Widget _buildAddAction(BuildContext context) => GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => Scaffold.of(context).openEndDrawer(), + child: SizedBox( + width: 32, + child: const Icon( + Icons.add, + size: 24, + ), + ), + ); + + List _buildTabs() => _tabs + .map( + (String name) => Container( + margin: const EdgeInsets.only(bottom: 5), + alignment: Alignment.center, + child: Text(name), + ), + ) + .toList(); + + @override + bool get wantKeepAlive => true; +} + + + + diff --git a/modules/widget_system/widget_module/lib/views/mobile/category_page/delete_category_dialog.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/delete_category_dialog.dart new file mode 100644 index 000000000..6c74675b9 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/delete_category_dialog.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import 'package:toly_ui/toly_ui.dart'; + + +/// create by 张风捷特烈 on 2020-04-21 +/// contact me by email 1981462002@qq.com +/// 说明: + +class DeleteCategoryDialog extends StatelessWidget { + final String title; + final String content; + final VoidCallback? onSubmit; + + + const DeleteCategoryDialog({Key? key, + this.title='', this.content ='',this.onSubmit + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + _buildBar(context), + _buildTitle(context), + _buildContent(), + _buildFooter(context), + ], + ); + } + + Widget _buildTitle(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + title, + style: const TextStyle(color: Colors.red, fontSize: 20), + ), + ], + ); + } + + Widget _buildContent() { + return Padding( + padding: const EdgeInsets.all(15.0), + child: Text(content, + style: const TextStyle(color: Colors.grey, 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: [ + FeedbackWidget( + onPressed: onSubmit, + child: Container( + alignment: Alignment.center, + height: 40, + width: 100, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(30)), + color: Theme.of(context).primaryColor), + child: const Text('确 定', + style: TextStyle(color: Colors.white, fontSize: 16)), + ), + ), + FeedbackWidget( + onPressed: ()=>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('取 消', + style: TextStyle(color: Colors.white, fontSize: 16)), + ), + ) + ], + ), + ); + } + + _buildBar(context) => Row( + children: [ + const Spacer(), + GestureDetector( + onTap: () => Navigator.of(context).pop(), + child: Container( + height: 30, + alignment: Alignment.centerRight, + padding: const EdgeInsets.only(right: 10, top: 5), + child: Icon( + Icons.close, + color:Theme.of(context).primaryColor, + ), + ), + ), + ], + ); +} \ No newline at end of file diff --git a/modules/widget_system/widget_module/lib/views/mobile/category_page/edit_category_panel.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/edit_category_panel.dart new file mode 100644 index 000000000..0ee7740e1 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/edit_category_panel.dart @@ -0,0 +1,101 @@ +import 'package:app/app.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'; +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 +/// 说明: + +enum EditType { add, update } + +class EditCategoryPanel extends StatefulWidget { + final CategoryModel? model; + final EditType type; + + const EditCategoryPanel({Key? key, this.model, this.type = EditType.add}) : super(key: key); + + @override + _EditCategoryPanelState createState() => _EditCategoryPanelState(); +} + +class _EditCategoryPanelState extends State { + String name=''; + String color=''; + String info=''; + + int get colorIndex => widget.model == null + ? 0 + : UnitColor.collectColorSupport + .map((e) => e.value) + .toList() + .indexOf(widget.model!.color.value); + + @override + void initState() { + super.initState(); + info = widget.model?.info??''; + color = (widget.model == null ? null : ColorUtils.colorString(widget.model!.color))??''; + } + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), + child: InputButton( + defaultText: '${widget.model?.name??''}', + config: const InputButtonConfig(hint: '收藏集名称', iconData: Icons.check), + onSubmit: _doEdit, + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 8), + child: EditPanel( + defaultText: '${widget.model?.info??''}', + submitClear: false, + hint: '收藏集简介...', + onChange: (v) => info = v, + ), + ), + Container( + alignment: Alignment.center, + padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15), + child: ColorChooser( + defaultIndex: colorIndex, + colors: UnitColor.collectColorSupport, + onChecked: (v) => color = ColorUtils.colorString(v), + ), + ), + ], + ); + } + + void _doEdit(String str){ + name = str; + if (name.isNotEmpty) { + if (widget.type == EditType.add) { + BlocProvider.of(context).add( + EventAddCategory(name: name, info: info, color: color)); + } + if (widget.type == EditType.update) { + BlocProvider.of(context).add( + EventUpdateCategory( + id: widget.model!.id!, + name: name, + info: info, + color: color)); + } + } + Navigator.of(context).pop(); + } + +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/category_page/empty_category.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/empty_category.dart new file mode 100644 index 000000000..6f27ac091 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/empty_category.dart @@ -0,0 +1,69 @@ +import 'dart:io'; + +import 'package:app/app.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +import 'package:toly_ui/toly_ui.dart'; + +import 'package:widget_module/blocs/blocs.dart'; + +import 'package:path/path.dart' as path; +import 'package:sqflite/sqflite.dart'; + +/// create by 张风捷特烈 on 2021/2/25 +/// contact me by email 1981462002@qq.com +/// 说明: + +class EmptyCategory extends StatelessWidget { + + const EmptyCategory({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Center( + child: Panel( + radius: 15, + color: UnitColor.warning_color.withOpacity(0.3), + child: Wrap( + alignment: WrapAlignment.center, + crossAxisAlignment: WrapCrossAlignment.center, + direction: Axis.vertical, + spacing: 5, + children: [ + const Text( + " 您还没有收藏集! ", + style: TextStyle(fontSize: 18,color: UnitColor.head_text_color), + ), + const SizedBox( + width: 10, + ), + FeedbackWidget( + onPressed: ()=>_recallDatabase(context), + child: const Icon( + Icons.refresh, + textDirection: TextDirection.rtl, + color: Colors.blue, + size: 36, + ), + ), + const Text( + "恢复默认", + style: TextStyle(fontSize: 14,color: UnitColor.input_hit_text_color), + ), + ], + ), + )); + } + + _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); + BlocProvider.of(context).add(const EventLoadCategory()); + } +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/category_page/home_right_drawer.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/home_right_drawer.dart new file mode 100644 index 000000000..e0d999b91 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/home_right_drawer.dart @@ -0,0 +1,74 @@ + +import 'package:flutter/material.dart'; + +import 'package:toly_ui/toly_ui.dart'; + +import '../widget_page/unit_drawer_header.dart'; +import 'edit_category_panel.dart'; + +class HomeRightDrawer extends StatefulWidget { + const HomeRightDrawer({Key? key}) : super(key: key); + + @override + _HomeRightDrawerState createState() => _HomeRightDrawerState(); +} + +class _HomeRightDrawerState extends State { + String name=''; + String color=''; + String info=''; + + @override + Widget build(BuildContext context) { + return Drawer( + elevation: 3, + child: _buildChild(context), + ); + } + + Widget _buildChild(BuildContext context) { + // final Color color = BlocProvider.of(context).state.color; + final Color color = Theme.of(context).scaffoldBackgroundColor; + + return Container( + color: color, + child: ListView(padding: EdgeInsets.zero, children: [ + UnitDrawerHeader(color:color), + _buildTitle(context), + const EditCategoryPanel( + + ) + ]), + ); + } + + Widget _buildTitle(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(top: 5.0, bottom: 8), + child: Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Circle( + color: Theme.of(context).primaryColor, + radius: 5, + ), + const Padding( + padding: EdgeInsets.symmetric(horizontal: 8), + child: Text( + '添加收藏集', + style: TextStyle(fontSize: 16, shadows: [ + Shadow( + color: Colors.white, offset: Offset(.5, .5), blurRadius: 1) + ]), + ), + ), + Circle( + color: Theme.of(context).primaryColor, + radius: 5, + ), + ], + ), + ); + } +} 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/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/async_button.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/async_button.dart new file mode 100644 index 000000000..7acd72988 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/async_button.dart @@ -0,0 +1,120 @@ +import 'dart:convert'; + +import 'package:app/app.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.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:widget_ui/widget_ui.dart'; + + +import 'package:toly_ui/toly_ui.dart'; + + +/// create by 张风捷特烈 on 2021/2/24 +/// contact me by email 1981462002@qq.com +/// 说明: 同步数据按钮,点击时请求服务器,获取备份数据。 + +class SyncCategoryButton extends StatefulWidget { + const SyncCategoryButton({Key? key}) : super(key: key); + + @override + _SyncCategoryButtonState createState() => _SyncCategoryButtonState(); +} + +enum AsyncType { loading, error, none, success } + +class _SyncCategoryButtonState extends State { + AsyncType state = AsyncType.none; + + @override + Widget build(BuildContext context) { + Widget result; + switch (state) { + case AsyncType.loading: + result = _buildLoading(); + break; + case AsyncType.error: + result = _buildError(); + break; + case AsyncType.none: + result = _buildDefault(); + break; + case AsyncType.success: + result = _buildSuccess(); + break; + } + return result; + } + + Widget _buildLoading() { + return const CupertinoActivityIndicator(); + } + + Widget _buildError() { + return const Icon( + TolyIcon.error, + size: 25, + color: Colors.red, + ); + } + + Widget _buildDefault() { + return FeedbackWidget( + child: const Icon( + TolyIcon.download, + size: 24, + ), + onPressed: _doSync); + } + + void _doSync() async { + setState(() => state = AsyncType.loading); + 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); + } + setState(() => state = AsyncType.success); + _toDefault(); + } else { + setState(() => state = AsyncType.error); + _toDefault(); + } + } + + Widget _buildSuccess() => const Icon( + TolyIcon.upload_success, + size: 22, + color: Colors.green, + ); + + void _toDefault() async { + await Future.delayed(const Duration(milliseconds: 800)); + setState(() { + state = AsyncType.none; + }); + } +} 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/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/upload_button.dart b/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/upload_button.dart new file mode 100644 index 000000000..06242ece4 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/category_page/sync/upload_button.dart @@ -0,0 +1,108 @@ +import 'dart:convert'; + +import 'package:app/app.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:storage/storage.dart'; + +import 'category_api.dart'; +import 'package:utils/utils.dart'; +import 'package:widget_module/blocs/blocs.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 +/// 说明: + +class UploadCategoryButton extends StatefulWidget { + const UploadCategoryButton({Key? key}) : super(key: key); + + @override + _UploadCategoryButtonState createState() => _UploadCategoryButtonState(); +} + +enum AsyncType { loading, error, none, success } + +class _UploadCategoryButtonState extends State { + AsyncType state = AsyncType.none; + + @override + Widget build(BuildContext context) { + Widget result; + switch (state) { + case AsyncType.loading: + result = _buildLoading(); + break; + case AsyncType.error: + result = _buildError(); + break; + case AsyncType.none: + result = _buildDefault(); + break; + case AsyncType.success: + result = _buildSuccess(); + break; + } + return result; + } + + Widget _buildLoading() { + return const CupertinoActivityIndicator(); + } + + Widget _buildError() { + return const Icon( + TolyIcon.error, + size: 22, + color: Colors.redAccent, + ); + } + + Widget _buildDefault() { + return FeedbackWidget( + child: const Icon( + TolyIcon.upload, + size: 24, + ), + onPressed: _doUploadCategoryData); + } + + void _doUploadCategoryData() async { + setState(() => state = AsyncType.loading); + + 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) { + setState(() => state = AsyncType.success); + _toDefault(); + } else { + setState(() => state = AsyncType.error); + _toDefault(); + } + } + + Widget _buildSuccess() { + return const Icon( + TolyIcon.upload_success, + size: 22, + color: Colors.green, + ); + } + + void _toDefault() async { + await Future.delayed(const Duration(milliseconds: 800)); + setState(() { + state = AsyncType.none; + }); + } +} 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/modules/widget_system/widget_module/lib/views/mobile/search_page/app_search_bar.dart b/modules/widget_system/widget_module/lib/views/mobile/search_page/app_search_bar.dart new file mode 100644 index 000000000..1de3cf280 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/search_page/app_search_bar.dart @@ -0,0 +1,92 @@ +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'; + + +class AppSearchBar extends StatefulWidget { + + const AppSearchBar({Key? key}):super(key: key); + + @override + _AppSearchBarState createState() => _AppSearchBarState(); +} + +class _AppSearchBarState extends State { + final TextEditingController _controller = TextEditingController(); //文本控制器 + + @override + Widget build(BuildContext context) { + bool isDesk = Platform.isMacOS||Platform.isWindows||Platform.isLinux; + return Stack( + alignment: Alignment.centerRight, + children: [ + SizedBox( + height: 35, + child: TextField( + autofocus: true, + controller: _controller, + maxLines: 1, + decoration: InputDecoration( + filled: true, + fillColor: Colors.white, + contentPadding: EdgeInsets.only(top: isDesk?6:1),//调整文字边距 + prefixIcon: Icon(Icons.search), + border: UnderlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.all(Radius.circular(15)), + ), + hintText: "搜点啥...", + hintStyle: TextStyle(fontSize: 14)), + onChanged: _doSearch, + onSubmitted: (str) { + //提交后,收起键盘 + FocusScope.of(context).requestFocus(FocusNode()); + }, + )), + _buildClearIcon() + ], + ); + } + + void _doSearch(String str) { + WidgetsBloc widgetsBloc = BlocProvider.of(context); + final WidgetFilter filter = widgetsBloc.state.filter.copyWith( + name: str, + ); + widgetsBloc.add( + EventSearchWidget(filter: filter), + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + Widget _buildClearIcon() { + return ValueListenableBuilder( + valueListenable: _controller, + builder: (_, TextEditingValue value, __) => value.text.isNotEmpty + ? GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + _controller.clear(); + _doSearch(''); + }, + child: const Padding( + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 8.0), + child: Icon( + CupertinoIcons.clear_circled, + color: Colors.black, + size: 20, + ), + ), + ) + : const SizedBox.shrink()); + } +} 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/views/pages/widget_detail/category_end_drawer.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/category_end_drawer.dart similarity index 77% rename from lib/views/pages/widget_detail/category_end_drawer.dart rename to modules/widget_system/widget_module/lib/views/mobile/widget_detail/category_end_drawer.dart index 3fa0d9c0d..1e8ae41a5 100644 --- a/lib/views/pages/widget_detail/category_end_drawer.dart +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/category_end_drawer.dart @@ -1,13 +1,13 @@ +import 'package:app/app.dart'; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:flutter_unit/app/res/style/unit_text_style.dart'; -import 'package:flutter_unit/blocs/bloc_exp.dart'; -import 'package:flutter_unit/repositories/rep/category_repository.dart'; -import 'package:flutter_unit/views/components/permanent/circle.dart'; -import 'package:flutter_unit/views/components/permanent/panel.dart'; -import 'package:flutter_unit/model/category_model.dart'; -import 'package:flutter_unit/model/widget_model.dart'; -import 'package:flutter_unit/views/components/project/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 @@ -16,13 +16,12 @@ import 'package:flutter_unit/views/components/project/unit_drawer_header.dart'; class CategoryEndDrawer extends StatelessWidget { final WidgetModel widget; - CategoryEndDrawer({this.widget}); + const CategoryEndDrawer({Key? key, required this.widget}) : super(key: key); @override Widget build(BuildContext context) { return Drawer( - child: Container( - child: ListView(padding: EdgeInsets.zero, children: [ + child: ListView(padding: EdgeInsets.zero, children: [ UnitDrawerHeader(color: Theme.of(context).primaryColor), Padding( padding: const EdgeInsets.all(10.0), @@ -41,14 +40,14 @@ class CategoryEndDrawer extends StatelessWidget { Padding( padding: const EdgeInsets.symmetric(horizontal: 15.0), child: Panel( - child: Text(widget.info, style: TStyleUnit.shadowTextStyle), + child: Text(widget.info, style: UnitTextStyle.shadowTextStyle), ), ), - Divider(), + const Divider(), _buildTitle(context), - Divider(), + const Divider(), CategoryInfo(widget.id) - ])), + ]), ); } @@ -86,7 +85,7 @@ class CategoryEndDrawer extends StatelessWidget { class CategoryInfo extends StatefulWidget { final int id; - CategoryInfo(this.id); + const CategoryInfo(this.id, {Key? key}) : super(key: key); @override _CategoryInfoState createState() => _CategoryInfoState(); @@ -105,10 +104,11 @@ class _CategoryInfoState extends State { @override Widget build(BuildContext context) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: 10.0), + padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Wrap( alignment: WrapAlignment.spaceBetween, - spacing: 5, + spacing: 4, + runSpacing: 8, children: categories.map((e) => _buildItem(e)).toList(), ), ); @@ -121,17 +121,20 @@ class _CategoryInfoState extends State { selectedColor: Colors.orange.withAlpha(120), shadowColor: Theme.of(context).primaryColor, elevation: 1, + // labelPadding: const EdgeInsets.only(right: 4,left: 4), avatar: Circle( - radius: 13, + radius: 10, color: category.color, ), selected: inHere, label: Text(category.name), onSelected: (v) async { - await repository.toggleCategory(category.id, widget.id); + await repository.toggleCategory(category.id!, widget.id); _loadCategoryIds(); + String locale = context.read().language.code; + BlocProvider.of(context) - .add(EventLoadCategoryWidget(category.id)); + .add(EventLoadCategoryWidget(category.id!, locale)); }); } diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_detail/collect_widget_list_item.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/collect_widget_list_item.dart new file mode 100644 index 000000000..75e0bff0e --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_detail/collect_widget_list_item.dart @@ -0,0 +1,122 @@ +import 'package:app/app.dart'; +import 'package:flutter/cupertino.dart'; +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'; + + +class CollectWidgetListItem extends StatelessWidget { + final WidgetModel data; + final Function(WidgetModel model)? onDeleteItemClick; + + const CollectWidgetListItem({Key? key, required this.data, this.onDeleteItemClick}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Stack( + children: [ + Material( + color: Theme.of(context).listTileTheme.tileColor, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0,vertical: 6), + child: Row( + children: [ + _buildLeading(), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildTitle(), + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + _buildSummary(), + StarScore( + star: Star( + emptyColor: Colors.white, + size: 12, + fillColor: itemColor), + score: data.lever, + ) + ], + ), + const Spacer(), + FeedbackWidget( + onPressed: () { + onDeleteItemClick?.call(data); + }, + child: const Icon( + CupertinoIcons.delete_solid, + color: Colors.red, + size: 20, + ), + ) + ], + ) + + ], + ), + ), + ], + ), + ), + ), + ], + ); + } + + Widget _buildLeading() => Padding( + padding: const EdgeInsets.only(right: 8), + child: data.image == null + ? Material( + color: Colors.transparent, + child: CircleText( + text: data.name, + size: 40, + fontSize: 18, + color: itemColor, + ), + ) + : CircleImage( + image: data.image!, + size: 40, + ), + ); + + Color get itemColor => Cons.tabColors[data.family.index]; + + Widget _buildTitle() => Row( + children: [ + Expanded( + child: Text(data.name, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + shadows: [ + Shadow( + color: Colors.white, offset: Offset(.3, .3)) + ])), + ), + ], + ); + + Widget _buildSummary() => Padding( + padding: const EdgeInsets.only(bottom: 5, top: 5), + child: Text( + data.nameCN, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: TextStyle( + color: Colors.grey[600], + fontSize: 12, + shadows: const [Shadow(color: Colors.white, offset: Offset(.5, .5))]), + ), + ); +} 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/modules/widget_system/widget_module/lib/views/mobile/widget_page/home_drawer.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_page/home_drawer.dart new file mode 100644 index 000000000..a23c52c54 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_page/home_drawer.dart @@ -0,0 +1,81 @@ +import 'package:app/app.dart'; +import 'package:components/components.dart'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import 'unit_drawer_header.dart'; + +/// create by 张风捷特烈 on 2020-03-26 +/// contact me by email 1981462002@qq.com +/// 说明: + +class HomeDrawer extends StatelessWidget { + + const HomeDrawer({super.key}); + + @override + Widget build(BuildContext context) { + return Drawer( + elevation: 3, + child: _buildChild(context), + ); + } + + Widget _buildChild(BuildContext context) { + + final Color color = Theme.of(context).primaryColor; + + return Container( + color: color.withAlpha(33), + child: ListView( + padding: EdgeInsets.zero, + children: [ + UnitDrawerHeader(color: color), + _buildItem(context, TolyIcon.icon_them, '应用设置', '/settings'), + _buildItem( + 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, '关于应用', '/about_app'), + _buildItem(context, TolyIcon.icon_kafei, '联系本王', '/about_me'), + ], + ), + ); + } + + Widget _buildFlutterUnit(BuildContext context) => NoBorderExpansionTile( + backgroundColor: Colors.white70, + leading: Icon( + Icons.extension, + color: Theme.of(context).primaryColor, + ), + title: const Text('Flutter 集录'), + children: [ + _buildItem(context, TolyIcon.icon_tag, '属性集录', ''), + _buildItem(context, Icons.palette, '绘画集录', ''), + _buildItem(context, Icons.widgets, '布局集录', ''), + _buildItem(context, TolyIcon.icon_bug, '要点集录', ''), + ], + ); + + Widget _buildItem( + BuildContext context, IconData icon, String title, String linkTo, + {VoidCallback? onTap}) => + ListTile( + leading: Icon( + icon, + color: Theme.of(context).primaryColor, + ), + title: Text(title), + 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/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/modules/widget_system/widget_module/lib/views/mobile/widget_page/widget_list_panel.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_page/widget_list_panel.dart new file mode 100644 index 000000000..c19827ce7 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_page/widget_list_panel.dart @@ -0,0 +1,39 @@ +import 'package:components/project_ui/project_ui.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:widget_module/blocs/blocs.dart'; + +import 'phone_widget_content.dart'; + +class WidgetListPanel extends StatelessWidget { + const WidgetListPanel({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + WidgetsState state = context.watch().state; + + + if (state is WidgetsLoaded) { + return PhoneWidgetContent(items: state.widgets); + } + + if (state is WidgetsLoading) { + return const SliverFillRemaining( + child: LoadingShower(), + ); + } + + if (state is WidgetsLoadFailed) { + return SliverFillRemaining( + child: ErrorShower( + error: "数据加载异常:\n${state.error}", + )); + } + + return const SliverFillRemaining( + child: EmptyShower( + message: "没数据,哥也没办法\n(≡ _ ≡)/~┴┴", + ), + ); + } +} diff --git a/modules/widget_system/widget_module/lib/views/mobile/widget_page/widget_page.dart b/modules/widget_system/widget_module/lib/views/mobile/widget_page/widget_page.dart new file mode 100644 index 000000000..4bb471810 --- /dev/null +++ b/modules/widget_system/widget_module/lib/views/mobile/widget_page/widget_page.dart @@ -0,0 +1,74 @@ +import 'package:components/components.dart'; +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: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); + + @override + State createState() => _WidgetPageState(); +} + +class _WidgetPageState extends State { + final RefreshController _refreshController = + RefreshController(initialRefresh: false); + + @override + void dispose() { + _refreshController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return BlocListener( + listener: _listenStateChange, + child: RefreshConfigWrapper( + child: TolyRefresh( + physics: BouncingScrollPhysics(), + controller: _refreshController, + onRefresh: _onRefresh, + enablePullUp: true, + onLoading: _onLoadMore, + child: CustomScrollView( + // key: PageStorageKey(name), + slivers: [ + const WidgetListPanel(), + ], + ), + ), + ), + ); + } + + void _onRefresh() async { + context.read().add(EventRefresh()); + await context.read().refreshFromNet(); + } + + void _onLoadMore() { + context.read().add(EventLoadMore()); + } + + void _listenStateChange(BuildContext context, WidgetsState state) async { + if (state is WidgetsLoaded) { + if (state.operate == LoadOperate.refresh) { + _refreshController.refreshCompleted(); + } + if (state.operate == LoadOperate.more) { + if (state.full) { + _refreshController.loadNoData(); + await Future.delayed(const Duration(milliseconds: 2000)); + _refreshController.resetNoData(); + } else { + _refreshController.loadComplete(); + } + } + } + } +} 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/modules/widget_system/widget_module/test/widget_module_test.dart b/modules/widget_system/widget_module/test/widget_module_test.dart new file mode 100644 index 000000000..3fb811d3a --- /dev/null +++ b/modules/widget_system/widget_module/test/widget_module_test.dart @@ -0,0 +1,12 @@ +// import 'package:flutter_test/flutter_test.dart'; +// +// import 'package:widget_module/widget_module.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/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/modules/widget_system/widget_repository/lib/src/database/po/category_po.dart b/modules/widget_system/widget_repository/lib/src/database/po/category_po.dart new file mode 100644 index 000000000..0a0177115 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/database/po/category_po.dart @@ -0,0 +1,87 @@ + +/// 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 '' +/// );"""; //建表语句 +/// +class CategoryPo { + final int? id; + final String name; + final String color; + final String info; + final DateTime? created; + final DateTime updated; + final String image; + final int count; + final int priority; + + const CategoryPo({ + this.id, + required this.name, + this.color = '#FFF2F2F2', + this.created, + required this.updated, + this.count = 0, + this.priority = 0, + this.info = '这里什么都没有...', + this.image = '', + }); + + factory CategoryPo.fromJson(Map map) { + return CategoryPo( + id: map['id'], + name: map['name'], + color: map["color"], + created: DateTime.parse(map["created"]), + image: map["image"], + priority: map["priority"], + count: map["count"], + updated: DateTime.parse(map["updated"]), + info: map["info"]); + } + + factory CategoryPo.fromNetJson(Map map) { + return CategoryPo( + id: map['id'], + name: map['name'], + color: map["color"], + created: DateTime.fromMillisecondsSinceEpoch(map["created"]), + image: map["image"], + priority: map["priority"], + count: map["count"], + updated: DateTime.fromMillisecondsSinceEpoch(map["updated"]), + info: map["info"]); + } + + Map toJson() => { + "id": id, + "name": name, + "info": info, + "created": created?.millisecondsSinceEpoch, + "updated": updated.millisecondsSinceEpoch, + "image": image, + "count": count, + "color": color, + "priority": priority, + }; + + @override + String toString() { + return 'CategoryPo{id: $id, name: $name, color: $color, info: $info, created: $created, updated: $updated, image: $image, count: $count, priority: $priority}'; + } + + @override + List get props => + [id, name, color, created, image, info, updated, priority,count]; +} diff --git a/lib/repositories/bean/node_po.dart b/modules/widget_system/widget_repository/lib/src/database/po/node_po.dart similarity index 77% rename from lib/repositories/bean/node_po.dart rename to modules/widget_system/widget_repository/lib/src/database/po/node_po.dart index 56db220ab..f5198a1b5 100644 --- a/lib/repositories/bean/node_po.dart +++ b/modules/widget_system/widget_repository/lib/src/database/po/node_po.dart @@ -11,17 +11,18 @@ class NodePo { final String subtitle; final String code; - const NodePo( - {this.id, - this.widgetId, - this.name, - this.priority, - this.subtitle, - this.code}); + const NodePo({ + required this.id, + required this.widgetId, + required this.name, + required this.priority, + required this.subtitle, + required this.code, + }); 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/modules/widget_system/widget_repository/lib/src/model/category_model.dart b/modules/widget_system/widget_repository/lib/src/model/category_model.dart new file mode 100644 index 000000000..790267bb0 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/model/category_model.dart @@ -0,0 +1,82 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:utils/utils.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 +/// contact me by email 1981462002@qq.com +/// 说明: 收藏夹展示数据模型 + +class CategoryModel extends Equatable { + final int? id; + final String name; + final String info; + final String? createDate; + final String? imageCover; + final int? count; + final Color color; + + const CategoryModel( + {required this.name, + required this.id, + required this.info, + this.createDate, + this.imageCover, + this.count, + required this.color}); + + bool get canDelete => id != null && id! > 1; + + static CategoryModel fromPo(CategoryPo po) { + return CategoryModel( + id: po.id, + name: po.name, + info: po.info, + createDate:po.created!=null? DateFormat('yyyy/MM/dd').format(po.created!):null, + imageCover: po.image, + count: po.count, + color: ColorUtils.parse(po.color), + ); + } + + @override + List get props => [ + id, + name, + info, + createDate, + imageCover, + count, + color, + ]; + + @override + String toString() { + return 'CategoryModel{id: $id, name: $name, info: $info, createDate: $createDate, imageCover: $imageCover, count: $count, color: $color}'; + } +} + + +// 收藏集的 To 对象,用与上传/同步 收藏集 +class CategoryTo{ + final CategoryPo model; + final List widgetIds; + final List? likesData; + + CategoryTo({required this.model,required this.widgetIds, this.likesData}); + + Map toJson() => { + "model": model, + "widgetIds": widgetIds, + "likesData":likesData + }; +} \ No newline at end of file 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/modules/widget_system/widget_repository/lib/src/model/widget_filter.dart b/modules/widget_system/widget_repository/lib/src/model/widget_filter.dart new file mode 100644 index 000000000..3482da8e4 --- /dev/null +++ b/modules/widget_system/widget_repository/lib/src/model/widget_filter.dart @@ -0,0 +1,60 @@ +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; + final int pageSize; + + const WidgetFilter({ + this.name = '', + this.locale = 'zh-cn', + this.family, + this.stars = const [-1, -1, -1, -1, -1], + this.page = 1, + this.pageSize = 20, + }); + + int get offset =>pageSize*(page-1); + + WidgetFilter.family( + this.family, { + this.name = '*', + this.page = 1, + this.locale = 'zh-cn', + this.pageSize = 20, + this.stars = const [1, 2, 3, 4, 5], + }); + + WidgetFilter copyWith({ + String? name, + 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, + ); + } + + @override + String toString() { + return 'WidgetFilter{name: $name, family: $family, stars: $stars, page: $page}'; + } +} 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/lib/repositories/rep/category_repository.dart b/modules/widget_system/widget_repository/lib/src/repository/category_repository.dart similarity index 86% rename from lib/repositories/rep/category_repository.dart rename to modules/widget_system/widget_repository/lib/src/repository/category_repository.dart index 1f5b08747..473291905 100644 --- a/lib/repositories/rep/category_repository.dart +++ b/modules/widget_system/widget_repository/lib/src/repository/category_repository.dart @@ -1,6 +1,6 @@ -import 'package:flutter_unit/model/category_model.dart'; -import 'package:flutter_unit/model/widget_model.dart'; -import 'package:flutter_unit/repositories/bean/category_po.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/pubspec.lock b/pubspec.lock index 4a2644331..e16819a50 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,104 +1,238 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + archive: + dependency: "direct main" + description: + name: archive + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.7" args: dependency: transitive description: name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" + version: "2.7.0" async: dependency: transitive description: name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.flutter-io.cn" source: hosted - version: "2.6.1" + version: "2.13.0" bloc: dependency: transitive description: name: bloc + sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e" url: "https://pub.flutter-io.cn" source: hosted - version: "7.0.0" + version: "8.1.4" boolean_selector: dependency: transitive description: name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + version: "2.1.2" characters: dependency: transitive description: name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0" + version: "1.4.0" charcode: dependency: transitive description: name: charcode + sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0" + version: "1.4.0" clock: dependency: transitive description: name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0" + version: "1.1.2" collection: dependency: transitive description: name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.2" + cross_file: + dependency: transitive + description: + name: cross_file + sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.3.4+2" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.6" + csslib: + dependency: transitive + description: + name: csslib + sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" url: "https://pub.flutter-io.cn" source: hosted - version: "1.15.0" + version: "1.0.2" cupertino_icons: dependency: "direct main" description: name: cupertino_icons + sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + url: "https://pub.flutter-io.cn" + source: hosted + 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: + name: dash_painter + sha256: e0b24070aed0549f9139ef1276ca70c155fe78960ec624d6dec3cdb0502f9a2a url: "https://pub.flutter-io.cn" source: hosted version: "1.0.2" + dev_build: + dependency: transitive + description: + name: dev_build + sha256: fda8a54458b2a873a84e0cd1513f4323a1fb0599ed5455245359bc0398bad9ee + url: "https://pub.flutter-io.cn" + source: hosted + 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: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.10" + 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: "2.1.1" equatable: dependency: "direct main" description: name: equatable + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" + version: "2.0.7" fake_async: dependency: transitive description: name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0" + version: "1.3.3" ffi: dependency: transitive description: name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.0" + version: "2.1.4" file: dependency: transitive description: name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.flutter-io.cn" source: hosted - version: "6.1.0" + version: "7.0.1" + file_picker: + dependency: "direct main" + description: + name: file_picker + 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: "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 @@ -108,30 +242,127 @@ packages: dependency: "direct main" description: name: flutter_bloc + sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a url: "https://pub.flutter-io.cn" source: hosted - version: "7.0.0" + version: "8.1.6" + flutter_colorpicker: + dependency: transitive + description: + name: flutter_colorpicker + sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea" + url: "https://pub.flutter-io.cn" + source: hosted + 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: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_markdown: dependency: "direct main" description: name: flutter_markdown + sha256: "08fb8315236099ff8e90cb87bb2b935e0a724a3af1623000a9cec930468e0f27" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.7.7+1" + flutter_plugin_android_lifecycle: + dependency: transitive + description: + name: flutter_plugin_android_lifecycle + 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: "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: "0.6.1" + version: "1.5.2" flutter_spinkit: dependency: "direct main" description: name: flutter_spinkit + sha256: d2696eed13732831414595b98863260e33e8882fc069ee80ec35d4ac9ddb0472 url: "https://pub.flutter-io.cn" source: hosted - version: "5.0.0" + version: "5.2.1" flutter_star: dependency: "direct main" description: name: flutter_star + sha256: "7dc10b8b3667ace2aa575a37ea0c00558a7514019cfe7e76322573d85b72a472" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.0" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1 url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.2" + version: "2.1.0" flutter_test: dependency: "direct dev" description: flutter @@ -142,347 +373,1080 @@ packages: description: flutter source: sdk version: "0.0.0" - http_parser: - dependency: transitive + fx_boot_starter: + dependency: "direct main" description: - name: http_parser + name: fx_boot_starter + sha256: cdc3d6e31a2609214468cb858f071b7f8f8de5731093b1a11305a457557bdf96 url: "https://pub.flutter-io.cn" source: hosted - version: "3.1.4" - install_plugin: + version: "0.1.1" + fx_dao: dependency: "direct main" description: - name: install_plugin + name: fx_dao + sha256: "253381288b921b9ad5e193d5e48531b71ad0c4511be66146603c65d36d2e995b" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.1" - intl: + version: "0.0.3+4" + fx_dio: dependency: "direct main" description: - name: intl + name: fx_dio + sha256: "395549518d055596d58ffcff59b4da0c147d855f6258f2769213c7230c090525" url: "https://pub.flutter-io.cn" source: hosted - version: "0.17.0" - js: + version: "0.0.4+3" + fx_env: dependency: transitive description: - name: js + name: fx_env + sha256: c95836ab108c498d53f43c464e08a5ce64975efdf586fb46f1a4c37bb2c400bf url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.3" - jwt_decoder: + version: "0.0.1" + fx_go_router_ext: dependency: "direct main" description: - name: jwt_decoder + name: fx_go_router_ext + sha256: dc65ac677f2058b8192ca50bdcd508a12ef4b6c150f64ca8d595b536ac6e5d1b url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.1" - markdown: - dependency: transitive + version: "0.0.6+1" + fx_platform_adapter: + dependency: "direct main" description: - name: markdown + name: fx_platform_adapter + sha256: e6d5ca554a1fd019a695a63bbb3e4eee6efe492b5d926542f22d6b64cea99415 url: "https://pub.flutter-io.cn" source: hosted - version: "4.0.0" - matcher: + version: "0.0.3" + fx_string: dependency: transitive description: - name: matcher + name: fx_string + sha256: "3350be2fa11cdb0d4107e4657431d05088a88795ef3145ad37902f6066a5b124" url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.10" - meta: - dependency: transitive + version: "0.0.1" + fx_trace: + dependency: "direct main" description: - name: meta + name: fx_trace + sha256: a1fb64b1a6bfc53609fe55e6d56a9c00e76250818eb5a8cfac280a051e33911c url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.0" - mime: - dependency: transitive + version: "0.0.5+5" + go_router: + dependency: "direct main" description: - name: mime + name: go_router + sha256: "04539267a740931c6d4479a10d466717ca5901c6fdfd3fcda09391bbb8ebd651" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.0" - nested: + version: "14.8.0" + html: dependency: transitive description: - name: nested + name: html + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.0" - package_info: - dependency: "direct main" + version: "0.15.6" + http: + dependency: transitive description: - name: package_info + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.2" - path: + version: "1.4.0" + http_parser: dependency: transitive description: - name: path + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.0" - path_provider: + version: "4.1.2" + image: dependency: "direct main" description: - name: path_provider + name: image + sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.1" - path_provider_linux: - dependency: transitive + version: "4.5.4" + intl: + dependency: "direct overridden" description: - name: path_provider_linux + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" - path_provider_macos: + version: "0.20.2" + json_annotation: dependency: transitive description: - name: path_provider_macos + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" - path_provider_platform_interface: - dependency: transitive + version: "4.9.0" + jwt_decoder: + dependency: "direct main" description: - name: path_provider_platform_interface + name: jwt_decoder + sha256: "54774aebf83f2923b99e6416b4ea915d47af3bde56884eb622de85feabbc559f" url: "https://pub.flutter-io.cn" source: hosted version: "2.0.1" - path_provider_windows: + leak_tracker: dependency: transitive description: - name: path_provider_windows + name: leak_tracker + sha256: "8dcda04c3fc16c14f48a7bb586d4be1f0d1572731b6d81d51772ef47c02081e0" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" - platform: + version: "11.0.1" + leak_tracker_flutter_testing: dependency: transitive description: - name: platform + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.0" - plugin_platform_interface: + version: "3.0.10" + leak_tracker_testing: dependency: transitive description: - name: plugin_platform_interface + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" - process: + version: "3.0.2" + lints: dependency: transitive description: - name: process + name: lints + sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.flutter-io.cn" source: hosted - version: "4.2.1" - provider: + version: "4.0.0" + logging: dependency: transitive description: - name: provider + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 url: "https://pub.flutter-io.cn" source: hosted - version: "5.0.0" - share: - dependency: "direct main" + version: "1.3.0" + markdown: + dependency: transitive description: - name: share + name: markdown + sha256: "935e23e1ff3bc02d390bad4d4be001208ee92cc217cb5b5a6c19bc14aaa318c1" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.1" - shared_preferences: - dependency: "direct main" + version: "7.3.0" + matcher: + dependency: transitive description: - name: shared_preferences + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.5" - shared_preferences_linux: + version: "0.12.17" + material_color_utilities: dependency: transitive description: - name: shared_preferences_linux + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" - shared_preferences_macos: + version: "0.11.1" + meta: dependency: transitive description: - name: shared_preferences_macos + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" - shared_preferences_platform_interface: + version: "1.16.0" + mime: dependency: transitive description: - name: shared_preferences_platform_interface + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" url: "https://pub.flutter-io.cn" source: hosted version: "2.0.0" - shared_preferences_web: + nested: dependency: transitive description: - name: shared_preferences_web + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" - shared_preferences_windows: + version: "1.0.0" + open_file: dependency: transitive description: - name: shared_preferences_windows + name: open_file + sha256: d17e2bddf5b278cb2ae18393d0496aa4f162142ba97d1a9e0c30d476adf99c0e url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_span: + version: "3.5.10" + open_file_android: dependency: transitive description: - name: source_span + name: open_file_android + sha256: "58141fcaece2f453a9684509a7275f231ac0e3d6ceb9a5e6de310a7dff9084aa" url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.1" - sqflite: - dependency: "direct main" + version: "1.0.6" + open_file_ios: + dependency: transitive description: - name: sqflite + name: open_file_ios + sha256: "02996f01e5f6863832068e97f8f3a5ef9b613516db6897f373b43b79849e4d07" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0+3" - sqflite_common: + version: "1.0.3" + open_file_linux: dependency: transitive description: - name: sqflite_common + name: open_file_linux + sha256: d189f799eecbb139c97f8bc7d303f9e720954fa4e0fa1b0b7294767e5f2d7550 url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0+2" - stack_trace: + version: "0.0.5" + open_file_mac: dependency: transitive description: - name: stack_trace + name: open_file_mac + sha256: "1440b1e37ceb0642208cfeb2c659c6cda27b25187a90635c9d1acb7d0584d324" url: "https://pub.flutter-io.cn" source: hosted - version: "1.10.0" - stream_channel: + version: "1.0.3" + open_file_platform_interface: dependency: transitive description: - name: stream_channel + name: open_file_platform_interface + sha256: "101b424ca359632699a7e1213e83d025722ab668b9fd1412338221bf9b0e5757" url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" - string_scanner: + version: "1.0.3" + open_file_web: dependency: transitive description: - name: string_scanner + name: open_file_web + sha256: e3dbc9584856283dcb30aef5720558b90f88036360bd078e494ab80a80130c4f url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0" - synchronized: + version: "0.0.4" + open_file_windows: dependency: transitive description: - name: synchronized + name: open_file_windows + sha256: d26c31ddf935a94a1a3aa43a23f4fff8a5ff4eea395fe7a8cb819cf55431c875 url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.0" - term_glyph: - dependency: transitive + version: "0.0.3" + package_info_plus: + dependency: "direct main" description: - name: term_glyph + name: package_info_plus + sha256: b15fad91c4d3d1f2b48c053dd41cb82da007c27407dc9ab5f9aa59881d0e39d4 url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.0" - test_api: + version: "8.1.4" + package_info_plus_platform_interface: dependency: transitive description: - name: test_api + name: package_info_plus_platform_interface + sha256: "6c935fb612dff8e3cc9632c2b301720c77450a126114126ffaafe28d2e87956c" url: "https://pub.flutter-io.cn" source: hosted - version: "0.3.0" - toggle_rotate: + version: "3.2.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.9.1" + path_parsing: + dependency: transitive + description: + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.17" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.3.0" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.1.0" + platform: + dependency: transitive + description: + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.6" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.8" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.5.1" + posix: + dependency: transitive + description: + name: posix + sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62 + url: "https://pub.flutter-io.cn" + source: hosted + 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: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.1.5" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.2.0" + quill_native_bridge: + dependency: transitive + description: + name: quill_native_bridge + sha256: "00752aca7d67cbd3254709a47558be78427750cb81aa42cfbed354d4a079bcfa" + url: "https://pub.flutter-io.cn" + source: hosted + 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: "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.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: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.1.4" + share_plus_platform_interface: + dependency: transitive + description: + name: share_plus_platform_interface + sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.0.2" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.10" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.5.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + url: "https://pub.flutter-io.cn" + source: hosted + 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: transitive + description: + name: sqflite + sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 + url: "https://pub.flutter-io.cn" + source: hosted + 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: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.5.5" + sqflite_common_ffi: + dependency: transitive + description: + name: sqflite_common_ffi + 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.4.0" + sqlite3: + dependency: transitive + description: + name: sqlite3 + sha256: c0503c69b44d5714e6abbf4c1f51a3c3cc42b75ce785f44404765e4635481d38 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.7.6" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.1" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.4.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: "0669c70faae6270521ee4f05bffd2919892d42d1276e6c495be80174b6bc0ef6" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.3.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.7.6" + toggle_rotate: dependency: "direct main" description: name: toggle_rotate + sha256: "601eba9b41ea8cb2d8765f5b8cb59ef9a8b2f36c592389e846b3aba20d2fc3f2" url: "https://pub.flutter-io.cn" source: hosted - version: "0.0.5" + 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: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.0" + 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: "0.0.1+2" url_launcher: dependency: "direct main" description: name: url_launcher + sha256: "9d06212b1362abc2f0f0d78e6f09f726608c74e3b9462e8368bb03314aa8d603" url: "https://pub.flutter-io.cn" source: hosted - version: "6.0.3" + version: "6.3.1" + url_launcher_android: + dependency: transitive + description: + name: url_launcher_android + sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.3.16" + url_launcher_ios: + dependency: transitive + description: + name: url_launcher_ios + sha256: "7f2022359d4c099eea7df3fdf739f7d3d3b9faf3166fb1dd390775176e0b76cb" + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.3.3" url_launcher_linux: dependency: transitive description: name: url_launcher_linux + sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" + version: "3.2.1" url_launcher_macos: dependency: transitive description: name: url_launcher_macos + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" + version: "3.2.2" url_launcher_platform_interface: dependency: transitive description: name: url_launcher_platform_interface + sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.2" + version: "2.3.2" url_launcher_web: dependency: transitive description: name: url_launcher_web + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" + version: "2.4.1" url_launcher_windows: dependency: transitive description: name: url_launcher_windows + sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.0" + version: "3.1.4" + uuid: + dependency: "direct main" + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.5.1" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: "44cc7104ff32563122a929e4620cf3efd584194eec6d1d913eb5ba593dbcf6de" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.18" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.13" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.17" vector_math: dependency: transitive description: name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.0" + 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: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" + webview_flutter: + dependency: "direct main" + description: + name: webview_flutter + sha256: c3e4fe614b1c814950ad07186007eff2f2e5dd2935eba7b9a9a1af8e5885f1ba + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.13.0" + webview_flutter_android: + dependency: transitive + description: + name: webview_flutter_android + sha256: f6e6afef6e234801da77170f7a1847ded8450778caf2fe13979d140484be3678 + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.7.0" + webview_flutter_platform_interface: + dependency: transitive + description: + name: webview_flutter_platform_interface + sha256: "7cb32b21825bd65569665c32bb00a34ded5779786d6201f5350979d2d529940d" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.13.0" + webview_flutter_wkwebview: + dependency: transitive + description: + name: webview_flutter_wkwebview + sha256: a3d461fe3467014e05f3ac4962e5fdde2a4bf44c561cb53e9ae5c586600fdbc3 + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.22.0" + widget_repository: + dependency: transitive + description: + path: "modules/widget_system/widget_repository" + relative: true + source: path + version: "0.0.1" + widget_ui: + dependency: transitive + description: + path: "modules/widget_system/widget_ui" + relative: true + source: path + version: "0.0.1" win32: dependency: transitive description: name: win32 + sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.4" + version: "5.13.0" + window_manager: + dependency: transitive + description: + name: window_manager + sha256: "732896e1416297c63c9e3fb95aea72d0355f61390263982a47fd519169dc5059" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.4.3" + wrapper: + dependency: "direct main" + description: + name: wrapper + sha256: "35b6d3c1ce4d739756b24571c394f2267fc89f35381098986800f094d468fbf5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.2" xdg_directories: dependency: transitive description: name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" url: "https://pub.flutter-io.cn" source: hosted - version: "0.2.0" + version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.3" sdks: - dart: ">=2.12.0 <3.0.0" - flutter: ">=2.0.0" + dart: ">=3.8.0-0 <4.0.0" + flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml index 6e42aa64a..cec27c193 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,50 +1,110 @@ name: flutter_unit -description: A new Flutter application. - -version: 1.5.3 +description: All Platform Flutter Experience App. +publish_to: none +version: 3.2.3+2006 author: 张风捷特烈 <1981462002@qq.com> homepage: https://juejin.cn/user/149189281194766/posts environment: - sdk: ">=2.7.0 <3.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: sdk: flutter - cupertino_icons: ^1.0.2 + flutter_localizations: + sdk: flutter + cupertino_icons: ^1.0.4 + + # 路由与状态管理 + go_router: ^14.2.0 # 路由管理 + flutter_bloc: ^8.1.6 # 状态管理 - flutter_bloc: ^7.0.0 # 状态管理 - equatable: ^2.0.0 # 相等辅助 - package_info: ^2.0.2 # 应用包信息 - sqflite: ^2.0.0+3 # 数据库 - shared_preferences: ^2.0.5 # xml 固化 + ## 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 解析 - toggle_rotate: ^0.0.5 - install_plugin: ^2.0.1 - flutter_star: ^0.1.2 # 星星组件 - url_launcher: ^6.0.3 # url - share: ^2.0.1 # 文字分享 - intl: ^0.17.0 - path_provider: ^2.0.1 # 路径 -# connectivity: ^3.0.3 #网络状态 - flutter_spinkit: ^5.0.0 # loading - flutter_markdown: ^0.6.1 # markdown - dio: ^3.0.10 # 网络请求 + path_provider: ^2.1.5 # 路径 + + # 平台功能 +# 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 + + # 视图展示 + tolyui: 0.0.4+10 # tolyui + tolyui_refresh: 0.0.1+1 # 下拉刷新 + + dash_painter: ^1.0.2 # 虚线 + flutter_star: ^1.0.2 # 星星组件 + flutter_spinkit: ^5.2.0 # loading + 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: ^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等字体资源 + - family: TolyIcon + fonts: + - asset: assets/iconfont/toly_icon.ttf - family: IndieFlower #字体名 fonts: - asset: assets/fonts/IndieFlower-Regular.ttf @@ -62,7 +122,21 @@ flutter: - asset: assets/fonts/ComicNeue-Regular.ttf - family: CHOPS fonts: - - asset: assets/fonts/CHOPS.TTF - - family: TolyIcon - fonts: - - asset: assets/iconfont/iconfont.ttf \ No newline at end of file + - 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/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/.gitignore b/windows/.gitignore new file mode 100644 index 000000000..d492d0d98 --- /dev/null +++ b/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 000000000..d05a061a0 --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1,104 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(flutter_unit LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "flutter_unit") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +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/CMakeLists.txt b/windows/flutter/CMakeLists.txt new file mode 100644 index 000000000..903f4899d --- /dev/null +++ b/windows/flutter/CMakeLists.txt @@ -0,0 +1,109 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + ${FLUTTER_TARGET_PLATFORM} $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc new file mode 100644 index 000000000..d9665f3a7 --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.cc @@ -0,0 +1,23 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + +#include +#include +#include +#include + +void RegisterPlugins(flutter::PluginRegistry* registry) { + ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenRetrieverWindowsPluginCApi")); + SharePlusWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SharePlusWindowsPluginCApi")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); + WindowManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowManagerPlugin")); +} diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h new file mode 100644 index 000000000..dc139d85a --- /dev/null +++ b/windows/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void RegisterPlugins(flutter::PluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake new file mode 100644 index 000000000..0cefa2b7c --- /dev/null +++ b/windows/flutter/generated_plugins.cmake @@ -0,0 +1,27 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST + screen_retriever_windows + share_plus + url_launcher_windows + window_manager +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/windows/libs/sqlite3.dll b/windows/libs/sqlite3.dll new file mode 100644 index 000000000..401d25603 Binary files /dev/null and b/windows/libs/sqlite3.dll differ diff --git a/windows/runner/CMakeLists.txt b/windows/runner/CMakeLists.txt new file mode 100644 index 000000000..17411a8ab --- /dev/null +++ b/windows/runner/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc new file mode 100644 index 000000000..adc0b5bcb --- /dev/null +++ b/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "com.toly1994" "\0" + VALUE "FileDescription", "flutter_unit" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "flutter_unit" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 com.toly1994. All rights reserved." "\0" + VALUE "OriginalFilename", "flutter_unit.exe" "\0" + VALUE "ProductName", "flutter_unit" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp new file mode 100644 index 000000000..b43b9095e --- /dev/null +++ b/windows/runner/flutter_window.cpp @@ -0,0 +1,61 @@ +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/windows/runner/flutter_window.h b/windows/runner/flutter_window.h new file mode 100644 index 000000000..6da0652f0 --- /dev/null +++ b/windows/runner/flutter_window.h @@ -0,0 +1,33 @@ +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp new file mode 100644 index 000000000..66917269a --- /dev/null +++ b/windows/runner/main.cpp @@ -0,0 +1,43 @@ +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.CreateAndShow(L"flutter_unit", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/windows/runner/resource.h b/windows/runner/resource.h new file mode 100644 index 000000000..66a65d1e4 --- /dev/null +++ b/windows/runner/resource.h @@ -0,0 +1,16 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico new file mode 100644 index 000000000..4ff64ba89 Binary files /dev/null and b/windows/runner/resources/app_icon.ico differ diff --git a/windows/runner/runner.exe.manifest b/windows/runner/runner.exe.manifest new file mode 100644 index 000000000..c977c4a42 --- /dev/null +++ b/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/windows/runner/utils.cpp b/windows/runner/utils.cpp new file mode 100644 index 000000000..f5bf9fa0f --- /dev/null +++ b/windows/runner/utils.cpp @@ -0,0 +1,64 @@ +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/windows/runner/utils.h b/windows/runner/utils.h new file mode 100644 index 000000000..3879d5475 --- /dev/null +++ b/windows/runner/utils.h @@ -0,0 +1,19 @@ +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/windows/runner/win32_window.cpp b/windows/runner/win32_window.cpp new file mode 100644 index 000000000..89b46ec6b --- /dev/null +++ b/windows/runner/win32_window.cpp @@ -0,0 +1,247 @@ +#include "win32_window.h" + +#include + +#include "resource.h" + +namespace { + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + FreeLibrary(user32_module); + } +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + // window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, + window_class, title.c_str(), + WS_OVERLAPPEDWINDOW, // do not add WS_VISIBLE since the window will be shown later + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + return OnCreate(); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} diff --git a/windows/runner/win32_window.h b/windows/runner/win32_window.h new file mode 100644 index 000000000..17ba43112 --- /dev/null +++ b/windows/runner/win32_window.h @@ -0,0 +1,98 @@ +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates and shows a win32 window with |title| and position and size using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size to will treat the width height passed in to this function + // as logical pixels and scale to appropriate for the default monitor. Returns + // true if the window was created successfully. + bool CreateAndShow(const std::wstring& title, + const Point& origin, + const Size& size); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_