diff --git a/.github/ISSUE_TEMPLATE/deer-issue-template.md b/.github/ISSUE_TEMPLATE/deer-issue-template.md index 36c4fec64..e3ea3aa83 100644 --- a/.github/ISSUE_TEMPLATE/deer-issue-template.md +++ b/.github/ISSUE_TEMPLATE/deer-issue-template.md @@ -9,10 +9,10 @@ assignees: '' ### 运行环境 ### -- [x] 电脑系统:如:`Windows 10` -- [x] 设备型号:如:`小米MIX 2s` -- [x] 设备系统版本:如 `Android 10` -- [x] Flutter 版本:如 `2.0.3` +- [x] 电脑系统:如:`Windows 11` +- [x] 设备型号:如:`小米13` +- [x] 设备系统版本:如 `Android 13` +- [x] Flutter 版本:如 `3.13.0` ### 具体问题描述 ### diff --git a/.github/workflows/flutter-drive.yml b/.github/workflows/flutter-drive.yml index b1efff756..e27fce756 100644 --- a/.github/workflows/flutter-drive.yml +++ b/.github/workflows/flutter-drive.yml @@ -17,39 +17,46 @@ jobs: # A set of different configurations of the virtual environment. matrix: device: - - "iPhone 8 (14.4)" - - "iPhone 11 Pro Max (14.4)" + - "iPhone 15 Pro Max" # When set to true, GitHub cancels all in-progress jobs if any matrix job # fails. fail-fast: false # The type of machine to run the job on. - runs-on: macOS-latest + runs-on: macos-latest # Contains a sequence of tasks. steps: # A name for your step to display on GitHub. - - name: "List all simulators" - run: "xcrun instruments -s" +# - name: "List all simulators" +# run: "xcrun instruments -s" +# - name: "Start Simulator" +# run: | +# UDID=$( +# xcrun instruments -s | +# awk \ +# -F ' *[][]' \ +# -v 'device=${{ matrix.device }}' \ +# '$1 == device { print $2 }' +# ) +# xcrun simctl boot "${UDID:?No Simulator with this name found}" - name: "Start Simulator" - run: | - UDID=$( - xcrun instruments -s | - awk \ - -F ' *[][]' \ - -v 'device=${{ matrix.device }}' \ - '$1 == device { print $2 }' - ) - xcrun simctl boot "${UDID:?No Simulator with this name found}" + # https://github.com/futureware-tech/simulator-action + uses: futureware-tech/simulator-action@v2 + with: + model: ${{ matrix.device }} + erase_before_boot: true + shutdown_after_job: true # The branch or tag ref that triggered the workflow will be checked out. # https://github.com/marketplace/actions/checkout - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # Sets up a flutter environment. # https://github.com/marketplace/actions/flutter-action - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: - flutter-version: '2.0.3' + flutter-version: '3.35.1' channel: 'stable' # or: 'dev' or 'beta' + - run: "flutter clean" - name: "Run Flutter Driver tests" - run: "flutter drive --target=test_driver/driver.dart" + run: "flutter drive --target=test_driver/driver.dart --no-enable-impeller" #https://github.com/flutter/flutter/issues/128391 drive_android: # The type of machine to run the job on. @@ -58,13 +65,18 @@ jobs: strategy: # set of different configurations of the virtual environment. matrix: - api-level: [21, 29] - target: [default] + api-level: [29, 34] + target: [google_apis] steps: - - uses: actions/checkout@v2 - - uses: subosito/flutter-action@v1 + - uses: actions/checkout@v3 + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + distribution: "oracle" + java-version: "17" + - uses: subosito/flutter-action@v2 with: - flutter-version: '2.0.3' + flutter-version: '3.35.1' channel: 'stable' # or: 'dev' or 'beta' - name: "Run Flutter Driver tests" # GitHub Action for installing, configuring and running Android Emulators (work only Mac OS) @@ -73,7 +85,7 @@ jobs: with: api-level: ${{ matrix.api-level }} target: ${{ matrix.target }} - arch: x86_64 + arch: arm64-v8a profile: Nexus 6 script: "flutter drive --target=test_driver/driver.dart" @@ -84,13 +96,13 @@ jobs: steps: # The branch or tag ref that triggered the workflow will be checked out. # https://github.com/actions/checkout - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # Setup a flutter environment. # https://github.com/marketplace/actions/flutter-action - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: - flutter-version: '2.0.3' + flutter-version: '3.35.1' channel: 'stable' - run: "flutter pub get" - name: "Run Flutter Accessibility Tests" - run: "flutter test test/accessibility_test.dart" \ No newline at end of file + run: "flutter test test/accessibility_test.dart" diff --git a/.github/workflows/flutter-web-deploy.yml b/.github/workflows/flutter-web-deploy.yml index efb638759..b6482c7d6 100644 --- a/.github/workflows/flutter-web-deploy.yml +++ b/.github/workflows/flutter-web-deploy.yml @@ -12,10 +12,11 @@ jobs: runs-on: macos-latest steps: - uses: actions/checkout@v2.3.1 - - uses: subosito/flutter-action@v1 + - uses: subosito/flutter-action@v2 with: - flutter-version: '2.0.3' + flutter-version: '3.35.1' channel: 'stable' + architecture: x64 - name: "Web Build 🔧" run: | flutter pub get diff --git a/.gitignore b/.gitignore index cd2f87ebf..1a75eff26 100644 --- a/.gitignore +++ b/.gitignore @@ -37,6 +37,7 @@ **/android/gradlew.bat **/android/local.properties **/android/**/GeneratedPluginRegistrant.java +**/android/app/.cxx # iOS/XCode related **/ios/**/*.mode1v3 @@ -75,3 +76,4 @@ lib/generated_plugin_registrant.dart !**/ios/**/default.pbxuser !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages +/ios/build/ diff --git a/README-EN.md b/README-EN.md index 735a778bb..ad320b8df 100644 --- a/README-EN.md +++ b/README-EN.md @@ -2,15 +2,17 @@ -This project is an exercise project for individuals to learn Flutter. +## English | [中文](README.md) -Realize specific design effects by setting, modifying, combining self-contained widgets and customizing to meet the needs of daily development. +This project is an exercise in learning Flutter for personal growth and development. -See the design catalog for the design drawings of this project. +To achieve specific design outcomes and meet the demands of daily development, one may employ the methods of configuring, modifying, combining pre-existing components, and customizing. + +The design plans for this project can be found in the "design" directory. You may utilize these plans to practice with a specific goal in mind. Any implementation is solely based on personal comprehension and learning. Should there be any superior implementation strategies, I welcome the opportunity for discussion. ## Preview -The effect of some pages is as follows: +Some of the page effects are as follows: | ![](./preview/Screenshot_1.png) | ![](./preview/Screenshot_2.png) | ![](./preview/Screenshot_3.png) | ![](./preview/Screenshot_4.png) | | :--------------------------------: | :---------------------------------: | :-------------------------------: | :-------------------------------: | @@ -19,65 +21,72 @@ The effect of some pages is as follows: | ![](./preview/Screenshot_13.png) | ![](./preview/Screenshot_14.png) | ![](./preview/Screenshot_15.png) | ![](./preview/Screenshot_17.png) | | ![](./preview/Screenshot_18.png) | ![](./preview/Screenshot_19.png) | ![](./preview/Screenshot_20.png) | ![](./preview/Screenshot_21.png) | | ![](./preview/Screenshot_22.jpg) | ![](./preview/Screenshot_23.jpg) | ![](./preview/Screenshot_24.jpg) | ![](./preview/Screenshot_25.jpg) | -| ![](./preview/Screenshot_26.jpg) | ![](./preview/Screenshot_27.jpg) | | | - -## Content - -* mvp mode -* Use `provider`(version 5.x) for state management -* Network request encapsulation based on `dio` (version 4.x) -* Driver testing, accessibility testing. -* Support dark mode -* Localization(Thanks @ghedwards) -* Use `Sliver` series of components to achieve complex scrolling effects -* Use amap Positioning to select address -* Pull down to refresh + pull up to load more -* Check update +| ![](./preview/Screenshot_26.jpg) | ![](./preview/Screenshot_27.jpg) | ![](./preview/lottie.gif) | | + +**If you find this project satisfactory, kindly show your support by giving it a Star or Fork. Rest assured, this project is being continuously maintained and any issues can be brought to our attention by submitting an Issue.** + +## Realizing the content. + +* MVP pattern +* State management using `provider` (version 6.x) +* Network request encapsulation based on `dio` (version 5.x) +* Integration testing and accessibility testing +* Support for dark mode +* Localization(Thanks to @ghedwards) +* Implementation of complex scrolling effects using `Sliver` series components +* Location selection using AMap (supports Web) +* Encapsulation of common widgets handling +* Pull-to-refresh and load-more functionality +* Application update check * PopupWindow -* Scan code(qr_code_scanner plugin) -* Menu switching animation (circular diffusion, 3D flip) -* Sliding delete +* QR code scanning functionality (using the qr_code_scanner plugin) +* Menu switching animations (circular expansion, 3D flip) +* Swipe-to-delete * City selection -* Similar to the three-level linkage of Jingdong's choice of cities -* Custom Dialog -* Sticky headers +* Three-level linkage selection similar to JD's city selection +* Various custom dialogs +* Sticky header for lists * Password input keyboard -* Verification code input -* Custom Simple Calendar -* Graphs and [pie charts](https://dartpad.cn/d06f8f737d6eb2d87978eb2d14b87864) -* Modular routing management -* More optimization +* Verification code input box +* Custom simple calendar +* Line chart and [pie charts](https://dartpad.cn/d06f8f737d6eb2d87978eb2d14b87864) +* Modularized route management +* More demos (ripple animation, scratch card, lottie) +* More detailed optimizations +You may download and experience it specifically by accessing the following links: -Android package:[Click to download](https://www.pgyer.com/gYXj),Password: `111111`。 +For the Android version, kindly click on the link provided: [Download here](https://www.pgyer.com/oEm8me), and enter the password: `111111`. -iOS and Web needs to download the code to run. +As for iOS, you will need to download and run the code on your own. -Web:https://simplezhli.github.io/flutter_deer/ +For web experience, please visit: https://simplezhli.github.io/flutter_deer/ -## Project environment +## The project's operational environment. -[![Build Status](https://github.com/simplezhli/flutter_deer/workflows/flutter_deer%20driver/badge.svg?branch=master)](https://github.com/simplezhli/flutter_deer/actions?query=workflow%3A%22flutter_deer+driver%22+branch%3Amaster) +[![flutter_deer driver](https://github.com/simplezhli/flutter_deer/actions/workflows/flutter-drive.yml/badge.svg?branch=master)](https://github.com/simplezhli/flutter_deer/actions/workflows/flutter-drive.yml) - 1. Flutter version 2.0.3 + 1. Flutter version 3.35.1 - 2. Dart version 2.12.2 + 2. Dart version 3.9.0 -## Precautions +## Precautions to be taken. -- iOS can execute commands `flutter build ios` to create `release` versions. Android can execute commands `flutter build apk` to create `release` versions. +- In debug mode, there may be some lagging, which is considered a normal occurrence. A satisfactory experience requires the creation of a release package. To create a release version for iOS, execute the command `flutter build ios`. For Android, execute the command `flutter build apk`. -- If there is a problem with the project, you can try to find a solution in the [iOS problem summary](./docs/iOS问题汇总.md) and [Android problem summary](./docs/Android问题汇总.md). +- If there are any issues with the project's execution, please refer to the [iOS issue summary](./docs/iOS问题汇总.md) and [Android issue summary](./docs/Android问题汇总.md) for possible solutions. -- Due to some plug-ins, this project has imperfect support on the Windows and macOS. Those who are interested can run the experience by themselves. +- Due to certain plugin limitations, this project is only available for preview on Windows and macOS. Those interested may run and experience it themselves. -- You can execute integration test commands to `flutter drive --target=test_driver/driver.dart` view function demonstrations. +- To view the functionality demonstration, execute the integration test command `flutter drive --target=test_driver/driver.dart`. + +- Due to the abundance of pages, it may be difficult to match the design at first. However, I have added the relative path of the design in the code comments, which can be searched or located for the corresponding page. I hope this will be helpful to you. -- I have added a relative path to the design drawings in the page notes. I can search or find the corresponding page. I hope it will help you. +- This project uses the [FlutterJsonBeanFactory](https://github.com/zhangruiyu/FlutterJsonBeanFactory) plugin to generate Beans. -- This project uses the FlutterJsonBeanFactory plugin to generate beans. +- Web performance may be slower due to large resource files such as js and deployment on Github. -## Summary of experience +## Summary of Experience - [Flutter开发中的一些Tips(一)](https://weilu.blog.csdn.net/article/details/90546727) @@ -113,31 +122,32 @@ Web:https://simplezhli.github.io/flutter_deer/ ## Tripartite library used -| 库 | 功能 | +| library | Functionality | | -------------------------- | --------------- | -| [dio](https://github.com/flutterchina/dio) | **Network library** | +| [dio](https://github.com/cfug/dio) | **Networking library** | | [provider](https://github.com/rrousselGit/provider) | **State management** | -| [flutter_2d_amap](https://github.com/simplezhli/flutter_2d_amap) | **2D AMap** | +| [flutter_2d_amap](https://github.com/simplezhli/flutter_2d_amap) | **2D map from Amap** | | [cached_network_image](https://github.com/renefloor/flutter_cached_network_image) | **Image loading** | -| [fluro](https://github.com/theyakka/fluro) | **Route** | -| [flutter_oktoast](https://github.com/OpenFlutter/flutter_oktoast) | **Toast** | -| [common_utils](https://github.com/Sky24n/common_utils) | **Dart common utils library** | -| [flutter_slidable](https://github.com/letsar/flutter_slidable) | **Sliding delete** | -| [flustars](https://github.com/Sky24n/flustars) | **Flutter common utils library** | -| [flutter_swiper](https://github.com/best-flutter/flutter_swiper) | **Flutter banner component** | -| [url_launcher](https://github.com/flutter/plugins/tree/master/packages/url_launcher) | **Launch URL plugin** | -| [image_picker](https://github.com/flutter/plugins/tree/master/packages/image_picker) | **Picture selection plugin** | -| [rxdart](https://github.com/ReactiveX/rxdart) | **Dart responsive extension** | +| [fluro](https://github.com/theyakka/fluro) | **Routing management** | +| [flutter_oktoast](https://github.com/OpenFlutter/flutter_oktoast) | **Toast notifications** | +| [common_utils](https://github.com/Sky24n/common_utils) | **Common Dart utility library** | +| [flutter_slidable](https://github.com/letsar/flutter_slidable) | **Swipe-to-delete** | +| [flustars](https://github.com/Sky24n/flustars) | **Common Flutter utility library** | +| [flutter_swiper](https://github.com/best-flutter/flutter_swiper) | **Flutter carousel component** | +| [url_launcher](https://github.com/flutter/plugins/tree/master/packages/url_launcher) | **Plugin for launching URLs** | +| [image_picker](https://github.com/flutter/plugins/tree/master/packages/image_picker) | **Plugin for selecting images** | +| [rxdart](https://github.com/ReactiveX/rxdart) | **Reactive extensions for Dart** | | [webview_flutter](https://github.com/flutter/plugins/tree/master/packages/webview_flutter) | **WebView plugin** | -| [keyboard_actions](https://github.com/diegoveloper/flutter_keyboard_actions) | **Keyboard actions** | -| [sticky_headers](https://github.com/fluttercommunity/flutter_sticky_headers) | **Sticky headers** | +| [keyboard_actions](https://github.com/diegoveloper/flutter_keyboard_actions) | **Handling keyboard events** | | [azlistview](https://github.com/flutterchina/azlistview) | **City selection list** | -| [date_utils](https://github.com/apptreesoftware/date_utils) | **Commonly used date tools** | +| [date_utils](https://github.com/apptreesoftware/date_utils) | **Common date utility classes** | | [bezier_chart](https://github.com/aeyrium/bezier-chart) | **Bezier chart** | -| [sprintf](https://github.com/Naddiseo/dart-sprintf) | **Format String** | -| [qr_code_scanner](https://github.com/juliuscanute/qr_code_scanner) | **Scan code** | +| [sprintf](https://github.com/Naddiseo/dart-sprintf) | **String formatting** | +| [qr_code_scanner](https://github.com/juliuscanute/qr_code_scanner) | **Scanning QR codes** | | [intl](https://github.com/dart-lang/intl) | **Localization** | +| [device_info_plus](https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus) | **Getting device information** | | [vibration](https://github.com/benjamindean/flutter_vibration) | **Vibration** | +| [lottie](https://github.com/xvrh/lottie-flutter) | **Animation effects** | For details, please refer to the [pubspec.yaml](https://github.com/simplezhli/flutter_deer/blob/master/pubspec.yaml) file. @@ -145,7 +155,9 @@ For details, please refer to the [pubspec.yaml](https://github.com/simplezhli/fl * [x] Web support. -* [ ] Migrate to null-safety. +* [x] Migrate to null-safety. + +* [ ] Migrate to Navigator 2.0. ## Thanks For @@ -159,7 +171,7 @@ For details, please refer to the [pubspec.yaml](https://github.com/simplezhli/fl 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 + https://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, diff --git a/README.md b/README.md index 22d95ce82..fe7db29ef 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ +## [English](README-EN.md) | 中文 + 本项目为个人学习Flutter的练习项目。 通过设置、修改、组合自带部件以及自定义来实现具体的设计效果,满足日常开发的需求。 @@ -19,15 +21,15 @@ | ![](./preview/Screenshot_13.png) | ![](./preview/Screenshot_14.png) | ![](./preview/Screenshot_15.png) | ![](./preview/Screenshot_17.png) | | ![](./preview/Screenshot_18.png) | ![](./preview/Screenshot_19.png) | ![](./preview/Screenshot_20.png) | ![](./preview/Screenshot_21.png) | | ![](./preview/Screenshot_22.jpg) | ![](./preview/Screenshot_23.jpg) | ![](./preview/Screenshot_24.jpg) | ![](./preview/Screenshot_25.jpg) | -| ![](./preview/Screenshot_26.jpg) | ![](./preview/Screenshot_27.jpg) | | | +| ![](./preview/Screenshot_26.jpg) | ![](./preview/Screenshot_27.jpg) | ![](./preview/lottie.gif) | | -**觉得还可以的话,来个Star、Fork支持一波!有问题欢迎提Issue。** +**觉得还可以的话,来个Star、Fork支持一波!本项目持续维护中,有问题欢迎提Issue。** -## 实现内容 +## 实现内容(已迁移到空安全) * mvp模式 -* 使用`provider` (5.x 版本)做状态管理 -* 基于`dio` (4.x 版本)的网络请求封装 +* 使用`provider` (6.x 版本)做状态管理 +* 基于`dio` (5.x 版本)的网络请求封装 * 完整的集成测试、可访问性测试。 * 支持深色模式 * 本地化(感谢 @ghedwards) @@ -49,11 +51,12 @@ * 自定义简易日历 * 曲线图及[饼状图](https://dartpad.cn/d06f8f737d6eb2d87978eb2d14b87864) * 模块化路由管理 +* 更多Demo(水波纹动画、刮刮卡、lottie) * 更多的细节优化 具体可以下载体验: -Android版安装包:[点击下载](https://www.pgyer.com/gYXj),安装密码:`111111`。 +Android版安装包:[点击去下载](https://github.com/simplezhli/flutter_deer/releases)。 iOS需要自行下载代码运行。 @@ -61,11 +64,11 @@ Web体验地址:https://simplezhli.github.io/flutter_deer/ ## 项目运行环境 -[![Build Status](https://github.com/simplezhli/flutter_deer/workflows/flutter_deer%20driver/badge.svg?branch=master)](https://github.com/simplezhli/flutter_deer/actions?query=workflow%3A%22flutter_deer+driver%22+branch%3Amaster) +[![flutter_deer driver](https://github.com/simplezhli/flutter_deer/actions/workflows/flutter-drive.yml/badge.svg?branch=master)](https://github.com/simplezhli/flutter_deer/actions/workflows/flutter-drive.yml) + + 1. Flutter version 3.35.1 - 1. Flutter version 2.0.3 - - 2. Dart version 2.12.2 + 2. Dart version 3.9.0 ## 注意事项 @@ -76,12 +79,12 @@ Web体验地址:https://simplezhli.github.io/flutter_deer/ - 项目运行有问题可以在[iOS问题汇总](./docs/iOS问题汇总.md)、[Android问题汇总](./docs/Android问题汇总.md)中尝试寻找解决办法。 - 由于部分插件的原因,本项目在Windows、macOS仅做预览(主要为原生功能方面,UI问题不大)。有兴趣的可自行运行体验。 - + - 可以执行集成测试命令`flutter drive --target=test_driver/driver.dart` 查看功能演示。 - 因为页面有点多,一开始可能会导致页面无法与设计图对应上。我在代码注释中有添加设计图的相对路径,可以搜索或查找到对应页面,希望对你有帮助。 -- 本项目使用FlutterJsonBeanFactory插件来生成Bean。FlutterJsonBeanFactory插件使用可以查看[这篇文章](https://www.jianshu.com/p/14cbcbaa74b7)。 +- 本项目使用[FlutterJsonBeanFactory](https://github.com/zhangruiyu/FlutterJsonBeanFactory)插件来生成Bean。 - Web受制于js等资源过大和部署在Github上,访问会慢一些。 @@ -118,12 +121,14 @@ Web体验地址:https://simplezhli.github.io/flutter_deer/ - [【译】正确操作Dart中的字符串](https://weilu.blog.csdn.net/article/details/107857569) - [【译】学习Flutter中新的Navigator和Router系统](https://weilu.blog.csdn.net/article/details/108902282) - + +- [【译】Flutter 2.2中的新功能](https://weilu.blog.csdn.net/article/details/117061293) + ## 使用到的三方库 | 库 | 功能 | | -------------------------- | --------------- | -| [dio](https://github.com/flutterchina/dio) | **网络库** | +| [dio](https://github.com/cfug/dio) | **网络库** | | [provider](https://github.com/rrousselGit/provider) | **状态管理** | | [flutter_2d_amap](https://github.com/simplezhli/flutter_2d_amap) | **高德2D地图** | | [cached_network_image](https://github.com/renefloor/flutter_cached_network_image) | **图片加载** | @@ -138,16 +143,17 @@ Web体验地址:https://simplezhli.github.io/flutter_deer/ | [rxdart](https://github.com/ReactiveX/rxdart) | **Dart的响应式扩展** | | [webview_flutter](https://github.com/flutter/plugins/tree/master/packages/webview_flutter) | **WebView插件** | | [keyboard_actions](https://github.com/diegoveloper/flutter_keyboard_actions) | **处理键盘事件** | -| [sticky_headers](https://github.com/fluttercommunity/flutter_sticky_headers) | **列表悬浮头** | | [azlistview](https://github.com/flutterchina/azlistview) | **城市选择列表** | | [date_utils](https://github.com/apptreesoftware/date_utils) | **常用的日期工具类** | | [bezier_chart](https://github.com/aeyrium/bezier-chart) | **曲线图表** | | [sprintf](https://github.com/Naddiseo/dart-sprintf) | **格式化String** | | [qr_code_scanner](https://github.com/juliuscanute/qr_code_scanner) | **扫码功能** | | [intl](https://github.com/dart-lang/intl) | **本地化** | +| [device_info_plus](https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus) | **获取设备信息** | | [vibration](https://github.com/benjamindean/flutter_vibration) | **振动** | +| [lottie](https://github.com/xvrh/lottie-flutter) | **动画效果** | -详细内容可以参看[pubspec.yaml](https://github.com/simplezhli/flutter_deer/blob/master/pubspec.yaml)文件 +详细内容可以参看[pubspec.yaml](https://github.com/simplezhli/flutter_deer/blob/master/pubspec.yaml)文件 ## 后续计划: @@ -159,8 +165,6 @@ Web体验地址:https://simplezhli.github.io/flutter_deer/ * [x] 页面添加设计图路径注释,方便寻找对应的设计图。 -* [x] 项目中有使用这一套框架及组件,会同步修复及优化遇到的问题。 - * [x] 添加集成测试。 * [x] 深色模式支持。 @@ -169,11 +173,17 @@ Web体验地址:https://simplezhli.github.io/flutter_deer/ * [x] Web端支持。 -* [ ] 迁移到空安全。 +* [x] 迁移到空安全。(安装包减少135KB,10.3M -> 10.1M) + +* [ ] 迁移至Navigator 2.0。 ## 已知存在问题: -- 2.0.0 已知存在问题(#68571 #73351 #73388) +- 部分使用的到的三方库没有适配3.0.0,flutter_swiper(flutter_swiper_null_safety_flutter3替代)、flustars(flustars_flutter3替代)、azlistview(升级scrollable_positioned_list)。 + +- 3.10.0 已知存在问题(#105203 #113595) + +- 2.0.0 已知存在问题(#68571 #73351 #74890 #79773 #79931) - ListView在没有设置分割线的情况下,个别Item之间存在大约1像素的间隔([像素对齐问题](https://github.com/flutter/flutter/issues/14288))。 @@ -191,7 +201,7 @@ Web体验地址:https://simplezhli.github.io/flutter_deer/ 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 + https://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, diff --git a/analysis_options.yaml b/analysis_options.yaml old mode 100755 new mode 100644 index 6455f4322..49e698def --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -1,43 +1,32 @@ # Specify analysis options. # -# Until there are meta linter rules, each desired lint must be explicitly enabled. -# See: https://github.com/dart-lang/linter/issues/288 -# -# For a list of lints, see: http://dart-lang.github.io/linter/lints/ -# See the configuration guide for more -# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer +# For a list of lints, see: https://dart.dev/tools/linter-rules +# For guidelines on configuring static analysis, see: +# https://dart.dev/tools/analysis # # There are other similar analysis options files in the flutter repos, # which should be kept in sync with this file: # # - analysis_options.yaml (this file) -# - packages/flutter/lib/analysis_options_user.yaml -# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml -# - https://github.com/flutter/engine/blob/master/analysis_options.yaml +# - https://github.com/flutter/engine/blob/main/analysis_options.yaml +# - https://github.com/flutter/packages/blob/main/analysis_options.yaml # -# This file contains the analysis options used by Flutter tools, such as IntelliJ, -# Android Studio, and the `flutter analyze` command. +# This file contains the analysis options used for code in the flutter/flutter +# repository. analyzer: - strong-mode: - implicit-casts: false - implicit-dynamic: false + language: + strict-casts: true + strict-inference: true + strict-raw-types: true errors: - # treat missing required parameters as a warning (not a hint) - missing_required_param: warning - # treat missing returns as a warning (not a hint) - missing_return: warning - # allow having TODOs in the code - todo: ignore - # allow self-reference to deprecated members (we do this because otherwise we have - # to annotate every member in every test, assert, etc, when we deprecate something) + # allow deprecated members (we do this because otherwise we have to annotate + # every member in every test, assert, etc, when we or the Dart SDK deprecates + # something (https://github.com/flutter/flutter/issues/143312) + deprecated_member_use: ignore deprecated_member_use_from_same_package: ignore - # Ignore analyzer hints for updating pubspecs when using Future or - # Stream and not importing dart:async - # Please see https://github.com/flutter/flutter/pull/24528 for details. - sdk_version_async_exported_from_core: ignore # Turned off until null-safe rollout is complete. - unnecessary_null_comparison: ignore +# unnecessary_null_comparison: ignore exclude: # the following two are relative to the stocks example and the flutter package respectively # see https://github.com/dart-lang/sdk/issues/28463 @@ -45,49 +34,51 @@ analyzer: - "lib/generated/json/**" - "lib/widgets/bezier_chart/**" # - "test/**" -# - "test_driver/**" + - "test_driver/**" + +formatter: + page_width: 100 linter: rules: - # these rules are documented on and in the same order as - # the Dart Lint rules page to make maintenance easier - # https://github.com/dart-lang/linter/blob/master/example/all.yaml + # This list is derived from the list of all available lints located at + # https://github.com/dart-lang/sdk/blob/main/pkg/linter/example/all.yaml - always_declare_return_types - always_put_control_body_on_new_line # - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219 - - always_require_non_null_named_parameters # - always_specify_types # - always_use_package_imports # we do this commonly - annotate_overrides + - annotate_redeclares # - avoid_annotating_with_dynamic # conflicts with always_specify_types - # - avoid_as # required for implicit-casts: true - avoid_bool_literals_in_conditional_expressions - # - avoid_catches_without_on_clauses # we do this commonly - # - avoid_catching_errors # we do this commonly + # - avoid_catches_without_on_clauses # blocked on https://github.com/dart-lang/linter/issues/3023 + # - avoid_catching_errors # blocked on https://github.com/dart-lang/linter/issues/4998 # - avoid_classes_with_only_static_members - # - avoid_double_and_int_checks # only useful when targeting JS runtime + - avoid_double_and_int_checks + - avoid_dynamic_calls - avoid_empty_else - avoid_equals_and_hash_code_on_mutable_classes - # - avoid_escaping_inner_quotes # not yet tested + - avoid_escaping_inner_quotes - avoid_field_initializers_in_const_classes -# - avoid_function_literals_in_foreach_calls - # - avoid_implementing_value_types # not yet tested + # - avoid_final_parameters # incompatible with prefer_final_parameters + - avoid_function_literals_in_foreach_calls +# - avoid_implementing_value_types - avoid_init_to_null - # - avoid_js_rounded_ints # only useful when targeting JS runtime + - avoid_js_rounded_ints + # - avoid_multiple_declarations_per_line # seems to be a stylistic choice we don't subscribe to - avoid_null_checks_in_equality_operators - # - avoid_positional_boolean_parameters # not yet tested - # - avoid_print # not yet tested + # - avoid_positional_boolean_parameters # would have been nice to enable this but by now there's too many places that break it + - avoid_print # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356) - # - avoid_redundant_argument_values # not yet tested + - avoid_redundant_argument_values - avoid_relative_lib_imports - avoid_renaming_method_parameters - avoid_return_types_on_setters - # - avoid_returning_null # there are plenty of valid reasons to return null - # - avoid_returning_null_for_future # not yet tested - avoid_returning_null_for_void - # - avoid_returning_this # there are plenty of valid reasons to return this - # - avoid_setters_without_getters # not yet tested - - avoid_shadowing_type_parameters # not yet tested + # - avoid_returning_this # there are enough valid reasons to return `this` that this lint ends up with too many false positives + - avoid_setters_without_getters + - avoid_shadowing_type_parameters - avoid_single_cascade_in_expression_statements - avoid_slow_async_io - avoid_type_to_string @@ -96,58 +87,71 @@ linter: - avoid_unnecessary_containers - avoid_unused_constructor_parameters - avoid_void_async - # - avoid_web_libraries_in_flutter # not yet tested + # - avoid_web_libraries_in_flutter # we use web libraries in web-specific code, and our tests prevent us from using them elsewhere - await_only_futures - camel_case_extensions - camel_case_types - cancel_subscriptions - # - cascade_invocations # not yet tested + # - cascade_invocations # doesn't match the typical style of this repo - cast_nullable_to_non_nullable # - close_sinks # not reliable enough # - comment_references # blocked on https://github.com/dart-lang/linter/issues/1142 + - conditional_uri_does_not_exist # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204 - control_flow_in_finally - # - curly_braces_in_flow_control_structures # required by flutter style - # - diagnostic_describe_all_properties # not yet tested + - curly_braces_in_flow_control_structures + - depend_on_referenced_packages + - deprecated_consistency + # - diagnostic_describe_all_properties # enabled only at the framework level (packages/flutter/lib) - directives_ordering - # - do_not_use_environment # we do this commonly + # - do_not_use_environment # there are appropriate times to use the environment, especially in our tests and build logic - empty_catches - empty_constructor_bodies - empty_statements + - eol_at_end_of_file - exhaustive_cases - file_names - flutter_style_todos - hash_and_equals - implementation_imports + - implicit_call_tearoffs + - implicit_reopen + - invalid_case_patterns + - invalid_runtime_check_with_js_interop_types # - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811 - - iterable_contains_unrelated_type - # - join_return_with_assignment # required by flutter style + # - join_return_with_assignment # not required by flutter style - leading_newlines_in_multiline_strings - library_names - library_prefixes +# - library_private_types_in_public_api # - lines_longer_than_80_chars # required by flutter style - - list_remove_unrelated_type - # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 + - literal_only_boolean_expressions + - missing_code_block_language_in_doc_comment - missing_whitespace_between_adjacent_strings - no_adjacent_strings_in_list - # - no_default_cases # too many false positives + - no_default_cases - no_duplicate_case_values + - no_leading_underscores_for_library_prefixes + - no_leading_underscores_for_local_identifiers + - no_literal_bool_comparisons - no_logic_in_create_state + - no_self_assignments + - no_wildcard_variable_uses # - no_runtimeType_toString # ok in tests; we enable this only in packages/ - non_constant_identifier_names + - noop_primitive_operations - null_check_on_nullable_type_parameter - null_closures # - omit_local_variable_types # opposite of always_specify_types # - one_member_abstracts # too many false positives - # - only_throw_errors # https://github.com/flutter/flutter/issues/5792 + - only_throw_errors # this does get disabled in a few places where we have legacy code that uses strings et al - overridden_fields - - package_api_docs - package_names - package_prefixed_library_names # - parameter_assignments # we do this commonly - prefer_adjacent_string_concatenation - prefer_asserts_in_initializer_lists - # - prefer_asserts_with_message # required by flutter style + # - prefer_asserts_with_message # not required by flutter style - prefer_collection_literals - prefer_conditional_assignment - prefer_const_constructors @@ -157,11 +161,11 @@ linter: # - prefer_constructors_over_static_methods # far too many false positives - prefer_contains # - prefer_double_quotes # opposite of prefer_single_quotes - - prefer_equal_for_default_values - # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods + # - prefer_expression_function_bodies # conflicts with ./docs/contributing/Style-guide-for-Flutter-repo.md#consider-using--for-short-functions-and-methods - prefer_final_fields - prefer_final_in_for_each - prefer_final_locals + # - prefer_final_parameters # we should enable this one day when it can be auto-fixed (https://github.com/dart-lang/linter/issues/3104), see also parameter_assignments - prefer_for_elements_to_map_fromIterable - prefer_foreach - prefer_function_declarations_over_variables @@ -170,15 +174,15 @@ linter: - prefer_if_null_operators - prefer_initializing_formals - prefer_inlined_adds - # - prefer_int_literals # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#use-double-literals-for-double-constants - # - prefer_interpolation_to_compose_strings # doesn't work with raw strings, see https://github.com/dart-lang/linter/issues/2490 + # - prefer_int_literals # conflicts with ./docs/contributing/Style-guide-for-Flutter-repo.md#use-double-literals-for-double-constants + - prefer_interpolation_to_compose_strings - prefer_is_empty - prefer_is_not_empty - prefer_is_not_operator - prefer_iterable_whereType - # - prefer_mixin # https://github.com/dart-lang/language/issues/32 - - prefer_null_aware_operators # disable until NNBD, see https://github.com/flutter/flutter/pull/32711#issuecomment-492930932 - # - prefer_relative_imports # incompatible with sub-package imports + # - prefer_mixin # Has false positives, see https://github.com/dart-lang/linter/issues/3018 + # - prefer_null_aware_method_calls # "call()" is confusing to people new to the language since it's not documented anywhere +# - prefer_relative_imports - prefer_single_quotes - prefer_spread_collections - prefer_typing_uninitialized_variables @@ -186,9 +190,12 @@ linter: - provide_deprecation_message # - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml - recursive_getters + # - require_trailing_commas # blocked on https://github.com/dart-lang/sdk/issues/47441 + - secure_pubspec_urls - sized_box_for_whitespace + - sized_box_shrink_expand - slash_for_doc_comments - # - sort_child_properties_last # not yet tested + - sort_child_properties_last - sort_constructors_first # - sort_pub_dependencies # prevents separating pinned transitive dependencies - sort_unnamed_constructors_first @@ -197,38 +204,52 @@ linter: - tighten_type_of_initializing_formals # - type_annotate_public_apis # subset of always_specify_types - type_init_formals - # - unawaited_futures # too many false positives + - type_literal_in_constant_pattern + # - unawaited_futures # too many false positives, especially with the way AnimationController works - unnecessary_await_in_return - unnecessary_brace_in_string_interps - unnecessary_const + - unnecessary_constructor_name # - unnecessary_final # conflicts with prefer_final_locals - unnecessary_getters_setters # - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498 + - unnecessary_late + - unnecessary_library_directive + # - unnecessary_library_name # blocked on blocked on https://github.com/dart-lang/dartdoc/issues/3882 - unnecessary_new - unnecessary_null_aware_assignments - # - unnecessary_null_checks # not yet tested + - unnecessary_null_aware_operator_on_extension_on_nullable + - unnecessary_null_checks - unnecessary_null_in_if_null_operators - # - unnecessary_nullable_for_final_variable_declarations + - unnecessary_nullable_for_final_variable_declarations - unnecessary_overrides - unnecessary_parenthesis - # - unnecessary_raw_strings # not yet tested + # - unnecessary_raw_strings # what's "necessary" is a matter of opinion; consistency across strings can help readability more than this lint - unnecessary_statements - unnecessary_string_escapes - unnecessary_string_interpolations - unnecessary_this + - unnecessary_to_list_in_spreads + - unreachable_from_main - unrelated_type_equality_checks - # - unsafe_html # not yet tested + - use_build_context_synchronously + - use_colored_box + # - use_decorated_box # not yet tested + - use_enums - use_full_hex_values_for_flutter_colors - use_function_type_syntax_for_parameters - # - use_if_null_to_convert_nulls_to_bools # not yet tested + - use_if_null_to_convert_nulls_to_bools - use_is_even_rather_than_modulo - use_key_in_widget_constructors - use_late_for_private_fields_and_variables - # - use_named_constants # not yet tested + - use_named_constants - use_raw_strings - use_rethrow_when_possible - # - use_setters_to_change_properties # not yet tested + - use_setters_to_change_properties + - use_super_parameters # - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182 + - use_test_throws_matchers # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review + - use_truncating_division - valid_regexps - void_checks \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 6eaa74fcd..78e82a9f4 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,3 +1,9 @@ +plugins { + id "com.android.application" + id "kotlin-android" + id "dev.flutter.flutter-gradle-plugin" +} + def localProperties = new Properties() def localPropertiesFile = rootProject.file('local.properties') if (localPropertiesFile.exists()) { @@ -6,12 +12,6 @@ if (localPropertiesFile.exists()) { } } -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' @@ -22,28 +22,19 @@ if (flutterVersionName == null) { flutterVersionName = '1.0' } -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - def keystorePropertiesFile = rootProject.file("app/key.properties") def keystoreProperties = new Properties() keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) android { - compileSdkVersion 29 - - lintOptions { - disable 'InvalidPackage' - } + compileSdkVersion 35 defaultConfig { applicationId "com.weilu.deer" - minSdkVersion 21 - targetSdkVersion 29 + minSdkVersion flutter.minSdkVersion + targetSdkVersion 35 versionCode flutterVersionCode.toInteger() versionName flutterVersionName - multiDexEnabled true } signingConfigs { @@ -72,6 +63,15 @@ android { } } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + namespace 'com.weilu.deer' + lint { + disable 'InvalidPackage' + } } flutter { @@ -79,6 +79,5 @@ flutter { } dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'androidx.multidex:multidex:2.0.1' + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20" } diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index a0b1e604d..f880684a6 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 4b3444739..c8c9a64bc 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,8 +1,21 @@ - + + + + + + + + + + + + + + + - - + \ No newline at end of file diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml index a0b1e604d..f880684a6 100644 --- a/android/app/src/profile/AndroidManifest.xml +++ b/android/app/src/profile/AndroidManifest.xml @@ -1,5 +1,4 @@ - + diff --git a/android/build.gradle b/android/build.gradle index 1b6e53702..97cc06256 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,29 +1,11 @@ -buildscript { - ext.kotlin_version = '1.4.30' - repositories { -// maven { url 'https://maven.aliyun.com/repository/public' }//jcenter -// maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }//gradle-plugin -// maven { url 'https://maven.aliyun.com/repository/central' }//central -// maven { url 'https://maven.aliyun.com/repository/google' }//google - google() - jcenter() - maven { url 'https://jitpack.io' } - } - - dependencies { - classpath 'com.android.tools.build:gradle:4.1.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - allprojects { repositories { -// maven { url 'https://maven.aliyun.com/repository/public' } -// maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } -// maven { url 'https://maven.aliyun.com/repository/central' } -// maven { url 'https://maven.aliyun.com/repository/google' } + maven { url 'https://maven.aliyun.com/repository/public' } + maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } + maven { url 'https://maven.aliyun.com/repository/central' } + maven { url 'https://maven.aliyun.com/repository/google' } google() - jcenter() + mavenCentral() maven { url 'https://jitpack.io' } } } @@ -36,6 +18,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } diff --git a/android/gradle.properties b/android/gradle.properties index 2379ec149..9e2da5c9f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -9,11 +9,13 @@ org.gradle.jvmargs=-Xmx3072m -XX:+UseParallelGC -XX:MaxPermSize=1024m -XX:+HeapD # 开启gradle缓存 org.gradle.caching=true -android.enableBuildCache=true #开启 kotlin 的增量和并行编译 kotlin.incremental=true kotlin.incremental.java=true kotlin.incremental.js=true kotlin.caching.enabled=true -kotlin.parallel.tasks.in.project=true \ No newline at end of file +kotlin.parallel.tasks.in.project=true +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false \ No newline at end of file diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 3fad423d3..4b2d7c51f 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Sep 13 21:08:20 CST 2020 +#Sat Nov 23 15:18:48 CST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip diff --git a/android/settings.gradle b/android/settings.gradle index 5a2f14fb1..a01a7e125 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -1,15 +1,25 @@ -include ':app' +pluginManagement { + def flutterSdkPath = { + def properties = new Properties() + file("local.properties").withInputStream { properties.load(it) } + def flutterSdkPath = properties.getProperty("flutter.sdk") + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" + return flutterSdkPath + }() -def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") -def plugins = new Properties() -def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') -if (pluginsFile.exists()) { - pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } + repositories { + google() + mavenCentral() + gradlePluginPortal() + } } -plugins.each { name, path -> - def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() - include ":$name" - project(":$name").projectDir = pluginDirectory +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version '8.12.0' apply false + id "org.jetbrains.kotlin.android" version "1.8.20" apply false } + +include ":app" \ No newline at end of file diff --git a/assets/lottie/bunny_new_mouth.json b/assets/lottie/bunny_new_mouth.json new file mode 100644 index 000000000..e2832274d --- /dev/null +++ b/assets/lottie/bunny_new_mouth.json @@ -0,0 +1,17135 @@ +{ + "assets": [], + "layers": [ + { + "ddd": 0, + "ind": 0, + "ty": 4, + "nm": "left_hand_mask", + "td": 1, + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 500, + 500, + 0 + ] + }, + "a": { + "k": [ + 476.25, + 476.25, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 0, + 262.888 + ], + [ + 262.888, + 0 + ], + [ + 0, + -262.888 + ], + [ + -262.888, + 0 + ] + ], + "o": [ + [ + 0, + -262.888 + ], + [ + -262.888, + 0 + ], + [ + 0, + 262.888 + ], + [ + 262.888, + 0 + ] + ], + "v": [ + [ + 476, + 0 + ], + [ + 0, + -476 + ], + [ + -476, + 0 + ], + [ + 0, + 476 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0.62, + 0.79, + 0.81, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 476.25, + 476.25 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 1, + "ty": 4, + "nm": "hand_left", + "tt": 1, + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + { + "i": { + "x": 0.69, + "y": 1 + }, + "o": { + "x": 1, + "y": 0 + }, + "n": "0p69_1_1_0", + "t": 29, + "s": [ + 208.794, + 1161.368, + 0 + ], + "e": [ + 312.794, + 745.368, + 0 + ], + "to": [ + 17.3333339691162, + -69.3333358764648, + 0 + ], + "ti": [ + -17.3333339691162, + 69.3333358764648, + 0 + ] + }, + { + "i": { + "x": 0.21, + "y": 0.21 + }, + "o": { + "x": 1, + "y": 1 + }, + "n": "0p21_0p21_1_1", + "t": 39, + "s": [ + 312.794, + 745.368, + 0 + ], + "e": [ + 312.794, + 745.368, + 0 + ], + "to": [ + 0, + 0, + 0 + ], + "ti": [ + 0, + 0, + 0 + ] + }, + { + "i": { + "x": 0.21, + "y": 1 + }, + "o": { + "x": 0.13, + "y": 0 + }, + "n": "0p21_1_0p13_0", + "t": 44, + "s": [ + 312.794, + 745.368, + 0 + ], + "e": [ + 208.794, + 1161.368, + 0 + ], + "to": [ + -17.3333339691162, + 69.3333358764648, + 0 + ], + "ti": [ + 0, + 0, + 0 + ] + }, + { + "i": { + "x": 0.667, + "y": 1 + }, + "o": { + "x": 0.13, + "y": 0 + }, + "n": "0p667_1_0p13_0", + "t": 54, + "s": [ + 208.794, + 1161.368, + 0 + ], + "e": [ + 312.794, + 745.368, + 0 + ], + "to": [ + 0, + 0, + 0 + ], + "ti": [ + -17.3333339691162, + 69.3333358764648, + 0 + ] + }, + { + "i": { + "x": 0.69, + "y": 0.69 + }, + "o": { + "x": 0.333, + "y": 0.333 + }, + "n": "0p69_0p69_0p333_0p333", + "t": 59, + "s": [ + 312.794, + 745.368, + 0 + ], + "e": [ + 312.794, + 745.368, + 0 + ], + "to": [ + 0, + 0, + 0 + ], + "ti": [ + 0, + 0, + 0 + ] + }, + { + "i": { + "x": 0.69, + "y": 1 + }, + "o": { + "x": 0.86, + "y": 0 + }, + "n": "0p69_1_0p86_0", + "t": 67.904, + "s": [ + 312.794, + 745.368, + 0 + ], + "e": [ + 208.794, + 1161.368, + 0 + ], + "to": [ + -17.3333339691162, + 69.3333358764648, + 0 + ], + "ti": [ + 17.3333339691162, + -69.3333358764648, + 0 + ] + }, + { + "t": 76 + } + ] + }, + "a": { + "k": [ + 109.858, + 208.134, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + -0.087, + 0.362 + ], + [ + -0.849, + 3.519 + ], + [ + -1.155, + 4.776 + ], + [ + -0.76, + 3.148 + ], + [ + -1.315, + 5.442 + ], + [ + -1.023, + 4.221 + ], + [ + -1.171, + 4.85 + ], + [ + -0.84, + 3.481 + ], + [ + -1.157, + 4.774 + ], + [ + -0.926, + 3.814 + ], + [ + -1.17, + 4.85 + ], + [ + -0.839, + 3.482 + ], + [ + -1.158, + 4.775 + ], + [ + -0.922, + 3.814 + ], + [ + -1.154, + 4.776 + ], + [ + -1.023, + 4.22 + ], + [ + -1.17, + 4.85 + ], + [ + -0.839, + 3.481 + ], + [ + -1.158, + 4.774 + ], + [ + -0.924, + 3.814 + ], + [ + -1.171, + 4.85 + ], + [ + -0.839, + 3.481 + ], + [ + -1.156, + 4.775 + ], + [ + -0.758, + 3.149 + ], + [ + -1.37, + 5.821 + ], + [ + -0.54, + 5.338 + ], + [ + -0.42, + 1.913 + ], + [ + 0.047, + 0.178 + ], + [ + 0.021, + 1.028 + ], + [ + 0.052, + 0.414 + ], + [ + 0.518, + 2.7 + ], + [ + 1.959, + 4.408 + ], + [ + 1.197, + 2.003 + ], + [ + 2.498, + 2.793 + ], + [ + 2.407, + 2.031 + ], + [ + 3.596, + 2.031 + ], + [ + 3.788, + 1.248 + ], + [ + 4.518, + 0.388 + ], + [ + 2.353, + -0.169 + ], + [ + 2.464, + -0.317 + ], + [ + 2.752, + -0.768 + ], + [ + 4.156, + -2.244 + ], + [ + 3.729, + -3.237 + ], + [ + 1.854, + -2.234 + ], + [ + 2.259, + -4.752 + ], + [ + 0.72, + -2.134 + ], + [ + 0.626, + -2.636 + ], + [ + 1.193, + -4.923 + ], + [ + 1.681, + -7 + ], + [ + 1.637, + -6.696 + ], + [ + 1.23, + -5.228 + ], + [ + 1.408, + -5.811 + ], + [ + 1.766, + -7.293 + ], + [ + 1.244, + -5.147 + ], + [ + 1.426, + -5.886 + ], + [ + 1.767, + -7.253 + ], + [ + 0.872, + -3.591 + ], + [ + 1.171, + -4.85 + ], + [ + 0.831, + -3.443 + ], + [ + 1.184, + -4.886 + ], + [ + 0.907, + -3.739 + ], + [ + 1.332, + -5.517 + ], + [ + 0.838, + -3.481 + ], + [ + 1.315, + -5.442 + ], + [ + 0.686, + -2.797 + ], + [ + -40.643, + -16.932 + ] + ], + "o": [ + [ + 0.85, + -3.518 + ], + [ + 1.152, + -4.776 + ], + [ + 0.762, + -3.147 + ], + [ + 1.314, + -5.443 + ], + [ + 1.02, + -4.221 + ], + [ + 1.176, + -4.848 + ], + [ + 0.84, + -3.48 + ], + [ + 1.151, + -4.775 + ], + [ + 0.924, + -3.814 + ], + [ + 1.176, + -4.848 + ], + [ + 0.84, + -3.481 + ], + [ + 1.151, + -4.776 + ], + [ + 0.924, + -3.812 + ], + [ + 1.155, + -4.775 + ], + [ + 1.02, + -4.221 + ], + [ + 1.175, + -4.849 + ], + [ + 0.84, + -3.48 + ], + [ + 1.152, + -4.775 + ], + [ + 0.924, + -3.814 + ], + [ + 1.177, + -4.848 + ], + [ + 0.84, + -3.481 + ], + [ + 1.152, + -4.776 + ], + [ + 0.762, + -3.147 + ], + [ + 1.399, + -5.813 + ], + [ + 1.221, + -5.184 + ], + [ + 0.195, + -1.936 + ], + [ + 0.04, + -0.182 + ], + [ + -0.265, + -1.021 + ], + [ + -0.009, + -0.421 + ], + [ + -0.338, + -2.718 + ], + [ + -0.911, + -4.753 + ], + [ + -0.947, + -2.131 + ], + [ + -1.924, + -3.218 + ], + [ + -2.087, + -2.334 + ], + [ + -3.168, + -2.671 + ], + [ + -3.468, + -1.959 + ], + [ + -4.328, + -1.427 + ], + [ + -2.296, + -0.196 + ], + [ + -2.467, + 0.177 + ], + [ + -2.844, + 0.368 + ], + [ + -4.542, + 1.269 + ], + [ + -4.358, + 2.353 + ], + [ + -2.197, + 1.908 + ], + [ + -3.354, + 4.041 + ], + [ + -0.967, + 2.035 + ], + [ + -0.865, + 2.564 + ], + [ + -1.172, + 4.929 + ], + [ + -1.696, + 6.996 + ], + [ + -1.611, + 6.703 + ], + [ + -1.276, + 5.217 + ], + [ + -1.368, + 5.821 + ], + [ + -1.766, + 7.293 + ], + [ + -1.247, + 5.146 + ], + [ + -1.424, + 5.886 + ], + [ + -1.758, + 7.256 + ], + [ + -0.874, + 3.59 + ], + [ + -1.178, + 4.848 + ], + [ + -0.832, + 3.443 + ], + [ + -1.179, + 4.888 + ], + [ + -0.905, + 3.739 + ], + [ + -1.336, + 5.516 + ], + [ + -0.841, + 3.48 + ], + [ + -1.31, + 5.444 + ], + [ + -0.676, + 2.799 + ], + [ + 34.022, + 26.956 + ], + [ + 0.087, + -0.362 + ] + ], + "v": [ + [ + 17.125, + 196.379 + ], + [ + 19.69, + 185.829 + ], + [ + 23.133, + 171.498 + ], + [ + 25.45, + 162.064 + ], + [ + 29.37, + 145.732 + ], + [ + 32.463, + 133.076 + ], + [ + 35.968, + 118.525 + ], + [ + 38.499, + 108.085 + ], + [ + 41.944, + 93.755 + ], + [ + 44.748, + 82.322 + ], + [ + 48.253, + 67.771 + ], + [ + 50.784, + 57.331 + ], + [ + 54.229, + 43 + ], + [ + 57.032, + 31.568 + ], + [ + 60.466, + 17.234 + ], + [ + 63.557, + 4.579 + ], + [ + 67.062, + -9.973 + ], + [ + 69.591, + -20.413 + ], + [ + 73.04, + -34.742 + ], + [ + 75.841, + -46.176 + ], + [ + 79.347, + -60.727 + ], + [ + 81.876, + -71.167 + ], + [ + 85.324, + -85.497 + ], + [ + 87.637, + -94.933 + ], + [ + 91.803, + -112.38 + ], + [ + 95.063, + -128.023 + ], + [ + 95.547, + -133.832 + ], + [ + 95.546, + -134.402 + ], + [ + 95.415, + -137.487 + ], + [ + 95.409, + -138.749 + ], + [ + 94.28, + -146.888 + ], + [ + 89.919, + -160.601 + ], + [ + 86.662, + -166.785 + ], + [ + 80.106, + -175.871 + ], + [ + 73.416, + -182.468 + ], + [ + 63.265, + -189.504 + ], + [ + 52.348, + -194.232 + ], + [ + 39.068, + -197.004 + ], + [ + 32.109, + -197.297 + ], + [ + 24.709, + -196.721 + ], + [ + 16.32, + -195.009 + ], + [ + 3.285, + -189.717 + ], + [ + -8.791, + -181.263 + ], + [ + -14.825, + -174.988 + ], + [ + -23.31, + -161.822 + ], + [ + -25.88, + -155.575 + ], + [ + -28.075, + -147.761 + ], + [ + -31.685, + -132.998 + ], + [ + -36.743, + -112.002 + ], + [ + -41.57, + -91.892 + ], + [ + -45.367, + -76.234 + ], + [ + -49.575, + -58.795 + ], + [ + -54.878, + -36.917 + ], + [ + -58.609, + -21.478 + ], + [ + -62.883, + -3.82 + ], + [ + -68.164, + 17.945 + ], + [ + -70.814, + 28.708 + ], + [ + -74.32, + 43.259 + ], + [ + -76.828, + 53.586 + ], + [ + -80.358, + 68.25 + ], + [ + -83.1, + 79.462 + ], + [ + -87.092, + 96.012 + ], + [ + -89.624, + 106.451 + ], + [ + -93.542, + 122.784 + ], + [ + -95.593, + 131.176 + ], + [ + 16.864, + 197.466 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0.94, + 0.94, + 0.94, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 108.871, + 212.877 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + -2.786, + 11.518 + ], + [ + -3.055, + 12.631 + ], + [ + -3.136, + 12.966 + ], + [ + -3.461, + 14.304 + ], + [ + -3.307, + 13.672 + ], + [ + -2.958, + 12.223 + ], + [ + -2.635, + 10.886 + ], + [ + -3.051, + 12.632 + ], + [ + -0.322, + 4.6 + ], + [ + -0.29, + 0.427 + ], + [ + -0.07, + 3.445 + ], + [ + 0.031, + 0.271 + ], + [ + -0.008, + 0.346 + ], + [ + 0.138, + 1.291 + ], + [ + 3.501, + 7.461 + ], + [ + 7.346, + 6.858 + ], + [ + 6.083, + 3.294 + ], + [ + 5.005, + 1.469 + ], + [ + 5.277, + 0.165 + ], + [ + 0.422, + 0.008 + ], + [ + 0.358, + 0.007 + ], + [ + 0.311, + 0.236 + ], + [ + 0, + 0 + ], + [ + 0.34, + -0.041 + ], + [ + 0.303, + 0.006 + ], + [ + 0.426, + -0.167 + ], + [ + 1.784, + -0.213 + ], + [ + 4.574, + -1.424 + ], + [ + 7.008, + -5.122 + ], + [ + 5.118, + -7.737 + ], + [ + 2.11, + -8.664 + ], + [ + 2.446, + -10.105 + ], + [ + 3.411, + -14.078 + ], + [ + 2.887, + -11.923 + ], + [ + 2.614, + -10.81 + ], + [ + 3.056, + -12.63 + ], + [ + 2.623, + -10.848 + ], + [ + 3.468, + -14.34 + ], + [ + 1.587, + -6.55 + ], + [ + -4.407, + -3.492 + ], + [ + -0.677, + 2.799 + ], + [ + -1.31, + 5.444 + ], + [ + -0.841, + 3.48 + ], + [ + -1.337, + 5.516 + ], + [ + -0.906, + 3.739 + ], + [ + -1.18, + 4.888 + ], + [ + -0.831, + 3.443 + ], + [ + -1.178, + 4.848 + ], + [ + -0.874, + 3.59 + ], + [ + -1.758, + 7.256 + ], + [ + -1.423, + 5.887 + ], + [ + -1.246, + 5.146 + ], + [ + -1.767, + 7.293 + ], + [ + -1.369, + 5.821 + ], + [ + -1.275, + 5.217 + ], + [ + -1.61, + 6.703 + ], + [ + -1.696, + 6.996 + ], + [ + -1.172, + 4.929 + ], + [ + -0.866, + 2.564 + ], + [ + -0.967, + 2.035 + ], + [ + -3.355, + 4.041 + ], + [ + -2.197, + 1.908 + ], + [ + -4.358, + 2.353 + ], + [ + -4.543, + 1.269 + ], + [ + -2.845, + 0.367 + ], + [ + -2.467, + 0.177 + ], + [ + -2.295, + -0.196 + ], + [ + -4.329, + -1.427 + ], + [ + -3.468, + -1.959 + ], + [ + -3.168, + -2.671 + ], + [ + -2.087, + -2.334 + ], + [ + -1.924, + -3.218 + ], + [ + -0.947, + -2.13 + ], + [ + -0.912, + -4.753 + ], + [ + -0.339, + -2.718 + ], + [ + -0.009, + -0.421 + ], + [ + -0.264, + -1.021 + ], + [ + 0.04, + -0.182 + ], + [ + 0.196, + -1.936 + ], + [ + 1.221, + -5.184 + ], + [ + 1.399, + -5.813 + ], + [ + 0.761, + -3.148 + ], + [ + 1.151, + -4.776 + ], + [ + 0.841, + -3.48 + ], + [ + 1.176, + -4.848 + ], + [ + 0.924, + -3.814 + ], + [ + 1.151, + -4.775 + ], + [ + 0.839, + -3.48 + ], + [ + 1.175, + -4.85 + ], + [ + 1.02, + -4.221 + ], + [ + 1.155, + -4.775 + ], + [ + 0.924, + -3.813 + ], + [ + 1.151, + -4.776 + ], + [ + 0.84, + -3.481 + ], + [ + 1.177, + -4.848 + ], + [ + 0.925, + -3.814 + ], + [ + 1.151, + -4.775 + ], + [ + 0.84, + -3.48 + ], + [ + 1.176, + -4.848 + ], + [ + 1.02, + -4.22 + ], + [ + 1.314, + -5.442 + ], + [ + 0.762, + -3.147 + ], + [ + 1.152, + -4.776 + ], + [ + 0.85, + -3.518 + ], + [ + 0.087, + -0.362 + ], + [ + -4.789, + -1.816 + ], + [ + 0, + 0 + ] + ], + "o": [ + [ + 3.055, + -12.632 + ], + [ + 3.136, + -12.966 + ], + [ + 3.461, + -14.304 + ], + [ + 3.309, + -13.672 + ], + [ + 2.957, + -12.222 + ], + [ + 2.635, + -10.884 + ], + [ + 3.058, + -12.631 + ], + [ + 1.077, + -4.46 + ], + [ + 0.214, + -0.44 + ], + [ + 0.07, + -3.445 + ], + [ + -0.207, + -0.25 + ], + [ + 0.007, + -0.346 + ], + [ + 0.042, + -1.299 + ], + [ + -0.88, + -8.221 + ], + [ + -4.248, + -9.053 + ], + [ + -5.069, + -4.733 + ], + [ + -4.591, + -2.489 + ], + [ + -5.034, + -1.476 + ], + [ + -0.422, + -0.009 + ], + [ + -0.358, + -0.008 + ], + [ + -0.336, + -0.055 + ], + [ + 0, + 0 + ], + [ + -0.323, + 0.224 + ], + [ + -0.303, + -0.007 + ], + [ + -0.433, + 0.031 + ], + [ + -1.791, + 0.153 + ], + [ + -4.754, + 0.569 + ], + [ + -8.293, + 2.582 + ], + [ + -7.495, + 5.478 + ], + [ + -4.922, + 7.439 + ], + [ + -2.461, + 10.1 + ], + [ + -3.408, + 14.078 + ], + [ + -2.888, + 11.924 + ], + [ + -2.617, + 10.811 + ], + [ + -3.054, + 12.63 + ], + [ + -2.625, + 10.847 + ], + [ + -3.467, + 14.34 + ], + [ + -1.584, + 6.55 + ], + [ + 4.278, + 3.642 + ], + [ + 0.686, + -2.797 + ], + [ + 1.315, + -5.442 + ], + [ + 0.837, + -3.481 + ], + [ + 1.333, + -5.517 + ], + [ + 0.906, + -3.739 + ], + [ + 1.183, + -4.886 + ], + [ + 0.831, + -3.443 + ], + [ + 1.172, + -4.85 + ], + [ + 0.872, + -3.591 + ], + [ + 1.766, + -7.254 + ], + [ + 1.426, + -5.886 + ], + [ + 1.245, + -5.146 + ], + [ + 1.766, + -7.293 + ], + [ + 1.407, + -5.811 + ], + [ + 1.229, + -5.228 + ], + [ + 1.637, + -6.696 + ], + [ + 1.682, + -7 + ], + [ + 1.194, + -4.923 + ], + [ + 0.627, + -2.636 + ], + [ + 0.72, + -2.134 + ], + [ + 2.259, + -4.752 + ], + [ + 1.854, + -2.234 + ], + [ + 3.728, + -3.237 + ], + [ + 4.156, + -2.244 + ], + [ + 2.752, + -0.768 + ], + [ + 2.464, + -0.317 + ], + [ + 2.353, + -0.169 + ], + [ + 4.518, + 0.388 + ], + [ + 3.787, + 1.248 + ], + [ + 3.597, + 2.031 + ], + [ + 2.408, + 2.031 + ], + [ + 2.498, + 2.793 + ], + [ + 1.198, + 2.003 + ], + [ + 1.96, + 4.409 + ], + [ + 0.517, + 2.7 + ], + [ + 0.051, + 0.414 + ], + [ + 0.022, + 1.028 + ], + [ + 0.047, + 0.178 + ], + [ + -0.421, + 1.913 + ], + [ + -0.54, + 5.337 + ], + [ + -1.37, + 5.82 + ], + [ + -0.757, + 3.149 + ], + [ + -1.157, + 4.775 + ], + [ + -0.839, + 3.48 + ], + [ + -1.17, + 4.851 + ], + [ + -0.925, + 3.814 + ], + [ + -1.158, + 4.774 + ], + [ + -0.84, + 3.481 + ], + [ + -1.171, + 4.85 + ], + [ + -1.023, + 4.22 + ], + [ + -1.153, + 4.776 + ], + [ + -0.922, + 3.814 + ], + [ + -1.157, + 4.775 + ], + [ + -0.839, + 3.481 + ], + [ + -1.171, + 4.85 + ], + [ + -0.925, + 3.814 + ], + [ + -1.157, + 4.774 + ], + [ + -0.839, + 3.481 + ], + [ + -1.171, + 4.85 + ], + [ + -1.023, + 4.221 + ], + [ + -1.316, + 5.443 + ], + [ + -0.761, + 3.149 + ], + [ + -1.155, + 4.775 + ], + [ + -0.848, + 3.519 + ], + [ + -0.088, + 0.362 + ], + [ + 4.714, + 1.964 + ], + [ + 0, + 0 + ], + [ + 2.789, + -11.518 + ] + ], + "v": [ + [ + 41.848, + 159.493 + ], + [ + 51.007, + 121.597 + ], + [ + 60.422, + 82.7 + ], + [ + 70.799, + 39.788 + ], + [ + 80.73, + -1.226 + ], + [ + 89.593, + -37.897 + ], + [ + 97.511, + -70.549 + ], + [ + 106.671, + -108.445 + ], + [ + 109.181, + -121.959 + ], + [ + 109.398, + -123.344 + ], + [ + 109.608, + -133.679 + ], + [ + 109.506, + -134.493 + ], + [ + 109.527, + -135.532 + ], + [ + 109.389, + -139.419 + ], + [ + 102.719, + -162.942 + ], + [ + 85.432, + -186.885 + ], + [ + 68.712, + -198.905 + ], + [ + 54.295, + -204.815 + ], + [ + 38.875, + -207.592 + ], + [ + 37.61, + -207.617 + ], + [ + 36.536, + -207.639 + ], + [ + 35.524, + -207.778 + ], + [ + 31.62, + -207.857 + ], + [ + 30.597, + -207.759 + ], + [ + 29.688, + -207.778 + ], + [ + 28.388, + -207.717 + ], + [ + 23.015, + -207.247 + ], + [ + 9.016, + -204.266 + ], + [ + -13.915, + -192.662 + ], + [ + -32.81, + -172.808 + ], + [ + -43.335, + -148.634 + ], + [ + -50.671, + -118.321 + ], + [ + -60.899, + -76.087 + ], + [ + -69.566, + -40.317 + ], + [ + -77.412, + -7.885 + ], + [ + -86.575, + 30.007 + ], + [ + -94.456, + 62.547 + ], + [ + -104.849, + 105.568 + ], + [ + -109.608, + 125.217 + ], + [ + -96.579, + 135.919 + ], + [ + -94.528, + 127.527 + ], + [ + -90.61, + 111.194 + ], + [ + -88.079, + 100.755 + ], + [ + -84.086, + 84.205 + ], + [ + -81.344, + 72.993 + ], + [ + -77.814, + 58.329 + ], + [ + -75.307, + 48.002 + ], + [ + -71.8, + 33.451 + ], + [ + -69.15, + 22.688 + ], + [ + -63.87, + 0.923 + ], + [ + -59.596, + -16.736 + ], + [ + -55.865, + -32.174 + ], + [ + -50.561, + -54.052 + ], + [ + -46.353, + -71.491 + ], + [ + -42.557, + -87.149 + ], + [ + -37.73, + -107.259 + ], + [ + -32.672, + -128.255 + ], + [ + -29.062, + -143.018 + ], + [ + -26.866, + -150.832 + ], + [ + -24.296, + -157.079 + ], + [ + -15.811, + -170.245 + ], + [ + -9.777, + -176.52 + ], + [ + 2.298, + -184.974 + ], + [ + 15.334, + -190.266 + ], + [ + 23.723, + -191.978 + ], + [ + 31.122, + -192.554 + ], + [ + 38.081, + -192.261 + ], + [ + 51.362, + -189.489 + ], + [ + 62.278, + -184.761 + ], + [ + 72.429, + -177.725 + ], + [ + 79.119, + -171.128 + ], + [ + 85.675, + -162.042 + ], + [ + 88.932, + -155.859 + ], + [ + 93.294, + -142.145 + ], + [ + 94.423, + -134.006 + ], + [ + 94.428, + -132.744 + ], + [ + 94.559, + -129.659 + ], + [ + 94.561, + -129.089 + ], + [ + 94.076, + -123.28 + ], + [ + 90.817, + -107.637 + ], + [ + 86.65, + -90.19 + ], + [ + 84.338, + -80.754 + ], + [ + 80.89, + -66.424 + ], + [ + 78.36, + -55.985 + ], + [ + 74.855, + -41.433 + ], + [ + 72.053, + -29.999 + ], + [ + 68.605, + -15.67 + ], + [ + 66.076, + -5.23 + ], + [ + 62.57, + 9.322 + ], + [ + 59.479, + 21.977 + ], + [ + 56.045, + 36.311 + ], + [ + 53.242, + 47.743 + ], + [ + 49.797, + 62.074 + ], + [ + 47.267, + 72.514 + ], + [ + 43.761, + 87.065 + ], + [ + 40.957, + 98.498 + ], + [ + 37.512, + 112.828 + ], + [ + 34.982, + 123.268 + ], + [ + 31.476, + 137.819 + ], + [ + 28.384, + 150.474 + ], + [ + 24.464, + 166.806 + ], + [ + 22.146, + 176.241 + ], + [ + 18.703, + 190.572 + ], + [ + 16.139, + 201.122 + ], + [ + 15.878, + 202.209 + ], + [ + 30.131, + 207.884 + ], + [ + 33.483, + 194.044 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 1, + 0.74, + 0.75, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 109.858, + 208.134 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 2", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 2, + "ty": 4, + "nm": "right_hand_mask", + "td": 1, + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 500, + 500, + 0 + ] + }, + "a": { + "k": [ + 476.25, + 476.25, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 0, + 262.888 + ], + [ + 262.888, + 0 + ], + [ + 0, + -262.888 + ], + [ + -262.888, + 0 + ] + ], + "o": [ + [ + 0, + -262.888 + ], + [ + -262.888, + 0 + ], + [ + 0, + 262.888 + ], + [ + 262.888, + 0 + ] + ], + "v": [ + [ + 476, + 0 + ], + [ + 0, + -476 + ], + [ + -476, + 0 + ], + [ + 0, + 476 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0.62, + 0.79, + 0.81, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 476.25, + 476.25 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 3, + "ty": 4, + "nm": "hand_right", + "tt": 1, + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + { + "i": { + "x": 0.69, + "y": 1 + }, + "o": { + "x": 1, + "y": 0 + }, + "n": "0p69_1_1_0", + "t": 29, + "s": [ + 819.292, + 1149.194, + 0 + ], + "e": [ + 689.292, + 745.194, + 0 + ], + "to": [ + -21.6666660308838, + -67.3333358764648, + 0 + ], + "ti": [ + 21.6666660308838, + 67.3333358764648, + 0 + ] + }, + { + "i": { + "x": 0.21, + "y": 0.21 + }, + "o": { + "x": 1, + "y": 1 + }, + "n": "0p21_0p21_1_1", + "t": 39, + "s": [ + 689.292, + 745.194, + 0 + ], + "e": [ + 689.292, + 745.194, + 0 + ], + "to": [ + 0, + 0, + 0 + ], + "ti": [ + 0, + 0, + 0 + ] + }, + { + "i": { + "x": 0.21, + "y": 1 + }, + "o": { + "x": 0.13, + "y": 0 + }, + "n": "0p21_1_0p13_0", + "t": 44, + "s": [ + 689.292, + 745.194, + 0 + ], + "e": [ + 819.292, + 1149.194, + 0 + ], + "to": [ + 21.6666660308838, + 67.3333358764648, + 0 + ], + "ti": [ + 0, + 0, + 0 + ] + }, + { + "i": { + "x": 0.515, + "y": 1 + }, + "o": { + "x": 0.13, + "y": 0 + }, + "n": "0p515_1_0p13_0", + "t": 54, + "s": [ + 819.292, + 1149.194, + 0 + ], + "e": [ + 689.292, + 745.194, + 0 + ], + "to": [ + 0, + 0, + 0 + ], + "ti": [ + 13.3333330154419, + 40.3333320617676, + 0 + ] + }, + { + "i": { + "x": 0.69, + "y": 1 + }, + "o": { + "x": 0.95, + "y": 0 + }, + "n": "0p69_1_0p95_0", + "t": 59, + "s": [ + 689.292, + 745.194, + 0 + ], + "e": [ + 739.292, + 907.194, + 0 + ], + "to": [ + -13.3333330154419, + -40.3333320617676, + 0 + ], + "ti": [ + -21.6666660308838, + -67.3333358764648, + 0 + ] + }, + { + "i": { + "x": 0.69, + "y": 1 + }, + "o": { + "x": 0.86, + "y": 0 + }, + "n": "0p69_1_0p86_0", + "t": 67.904, + "s": [ + 739.292, + 907.194, + 0 + ], + "e": [ + 819.292, + 1149.194, + 0 + ], + "to": [ + 21.6666660308838, + 67.3333358764648, + 0 + ], + "ti": [ + -13.3333330154419, + -40.3333320617676, + 0 + ] + }, + { + "t": 76 + } + ] + }, + "a": { + "k": [ + 109.772, + 207.96, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 0.622, + 2.577 + ], + [ + 1.31, + 5.443 + ], + [ + 0.841, + 3.48 + ], + [ + 1.336, + 5.515 + ], + [ + 0.906, + 3.739 + ], + [ + 1.18, + 4.887 + ], + [ + 0.831, + 3.443 + ], + [ + 1.178, + 4.847 + ], + [ + 0.874, + 3.59 + ], + [ + 1.758, + 7.255 + ], + [ + 1.423, + 5.887 + ], + [ + 1.246, + 5.145 + ], + [ + 1.766, + 7.293 + ], + [ + 1.369, + 5.822 + ], + [ + 1.276, + 5.217 + ], + [ + 1.611, + 6.703 + ], + [ + 1.695, + 6.996 + ], + [ + 1.172, + 4.929 + ], + [ + 0.865, + 2.564 + ], + [ + 0.966, + 2.035 + ], + [ + 3.356, + 4.041 + ], + [ + 2.197, + 1.907 + ], + [ + 4.358, + 2.353 + ], + [ + 4.542, + 1.269 + ], + [ + 2.845, + 0.367 + ], + [ + 2.467, + 0.177 + ], + [ + 2.296, + -0.196 + ], + [ + 4.328, + -1.426 + ], + [ + 3.468, + -1.959 + ], + [ + 3.168, + -2.671 + ], + [ + 2.087, + -2.334 + ], + [ + 1.924, + -3.218 + ], + [ + 0.947, + -2.131 + ], + [ + 0.911, + -4.753 + ], + [ + 0.338, + -2.718 + ], + [ + 0.009, + -0.421 + ], + [ + 0.265, + -1.02 + ], + [ + -0.041, + -0.182 + ], + [ + -0.197, + -1.935 + ], + [ + -1.22, + -5.185 + ], + [ + -1.399, + -5.813 + ], + [ + -0.762, + -3.148 + ], + [ + -1.152, + -4.777 + ], + [ + -0.84, + -3.48 + ], + [ + -1.176, + -4.849 + ], + [ + -0.924, + -3.814 + ], + [ + -1.151, + -4.776 + ], + [ + -0.84, + -3.48 + ], + [ + -1.175, + -4.85 + ], + [ + -1.02, + -4.221 + ], + [ + -1.155, + -4.775 + ], + [ + -0.923, + -3.813 + ], + [ + -1.151, + -4.776 + ], + [ + -0.841, + -3.48 + ], + [ + -1.176, + -4.849 + ], + [ + -0.924, + -3.814 + ], + [ + -1.15, + -4.776 + ], + [ + -0.841, + -3.48 + ], + [ + -1.176, + -4.849 + ], + [ + -1.021, + -4.22 + ], + [ + -1.315, + -5.442 + ], + [ + -0.762, + -3.148 + ], + [ + -1.152, + -4.776 + ], + [ + -0.851, + -3.518 + ], + [ + 0, + 0 + ], + [ + -33.986, + 27.051 + ] + ], + "o": [ + [ + -1.315, + -5.442 + ], + [ + -0.838, + -3.481 + ], + [ + -1.332, + -5.517 + ], + [ + -0.907, + -3.74 + ], + [ + -1.183, + -4.886 + ], + [ + -0.831, + -3.444 + ], + [ + -1.172, + -4.85 + ], + [ + -0.872, + -3.591 + ], + [ + -1.766, + -7.254 + ], + [ + -1.426, + -5.886 + ], + [ + -1.245, + -5.146 + ], + [ + -1.767, + -7.293 + ], + [ + -1.408, + -5.812 + ], + [ + -1.229, + -5.227 + ], + [ + -1.637, + -6.696 + ], + [ + -1.681, + -7 + ], + [ + -1.195, + -4.923 + ], + [ + -0.627, + -2.637 + ], + [ + -0.721, + -2.134 + ], + [ + -2.259, + -4.752 + ], + [ + -1.853, + -2.234 + ], + [ + -3.728, + -3.237 + ], + [ + -4.156, + -2.244 + ], + [ + -2.752, + -0.769 + ], + [ + -2.464, + -0.317 + ], + [ + -2.353, + -0.169 + ], + [ + -4.518, + 0.388 + ], + [ + -3.788, + 1.248 + ], + [ + -3.596, + 2.031 + ], + [ + -2.407, + 2.03 + ], + [ + -2.498, + 2.793 + ], + [ + -1.197, + 2.003 + ], + [ + -1.96, + 4.408 + ], + [ + -0.518, + 2.699 + ], + [ + -0.052, + 0.414 + ], + [ + -0.022, + 1.028 + ], + [ + -0.047, + 0.179 + ], + [ + 0.42, + 1.913 + ], + [ + 0.54, + 5.338 + ], + [ + 1.37, + 5.82 + ], + [ + 0.757, + 3.149 + ], + [ + 1.156, + 4.775 + ], + [ + 0.839, + 3.48 + ], + [ + 1.17, + 4.851 + ], + [ + 0.925, + 3.814 + ], + [ + 1.158, + 4.774 + ], + [ + 0.84, + 3.481 + ], + [ + 1.17, + 4.85 + ], + [ + 1.023, + 4.22 + ], + [ + 1.154, + 4.776 + ], + [ + 0.922, + 3.814 + ], + [ + 1.158, + 4.775 + ], + [ + 0.839, + 3.481 + ], + [ + 1.17, + 4.851 + ], + [ + 0.926, + 3.814 + ], + [ + 1.157, + 4.774 + ], + [ + 0.84, + 3.481 + ], + [ + 1.17, + 4.85 + ], + [ + 1.024, + 4.221 + ], + [ + 1.315, + 5.443 + ], + [ + 0.76, + 3.149 + ], + [ + 1.155, + 4.775 + ], + [ + 0.848, + 3.518 + ], + [ + 0, + 0 + ], + [ + 40.632, + -17.027 + ], + [ + -0.632, + -2.576 + ] + ], + "v": [ + [ + 93.624, + 122.973 + ], + [ + 89.706, + 106.64 + ], + [ + 87.174, + 96.201 + ], + [ + 83.182, + 79.651 + ], + [ + 80.438, + 68.439 + ], + [ + 76.909, + 53.775 + ], + [ + 74.401, + 43.448 + ], + [ + 70.895, + 28.897 + ], + [ + 68.245, + 18.134 + ], + [ + 62.964, + -3.631 + ], + [ + 58.69, + -21.29 + ], + [ + 54.961, + -36.728 + ], + [ + 49.656, + -58.606 + ], + [ + 45.448, + -76.046 + ], + [ + 41.651, + -91.703 + ], + [ + 36.825, + -111.813 + ], + [ + 31.768, + -132.809 + ], + [ + 28.156, + -147.572 + ], + [ + 25.962, + -155.386 + ], + [ + 23.391, + -161.633 + ], + [ + 14.906, + -174.799 + ], + [ + 8.872, + -181.074 + ], + [ + -3.203, + -189.528 + ], + [ + -16.238, + -194.82 + ], + [ + -24.628, + -196.532 + ], + [ + -32.027, + -197.108 + ], + [ + -38.987, + -196.815 + ], + [ + -52.266, + -194.044 + ], + [ + -63.183, + -189.315 + ], + [ + -73.334, + -182.279 + ], + [ + -80.024, + -175.682 + ], + [ + -86.581, + -166.596 + ], + [ + -89.837, + -160.413 + ], + [ + -94.198, + -146.699 + ], + [ + -95.328, + -138.56 + ], + [ + -95.333, + -137.298 + ], + [ + -95.464, + -134.214 + ], + [ + -95.466, + -133.643 + ], + [ + -94.98, + -127.835 + ], + [ + -91.722, + -112.191 + ], + [ + -87.555, + -94.744 + ], + [ + -85.242, + -85.308 + ], + [ + -81.794, + -70.978 + ], + [ + -79.266, + -60.539 + ], + [ + -75.76, + -45.987 + ], + [ + -72.958, + -34.553 + ], + [ + -69.51, + -20.224 + ], + [ + -66.98, + -9.784 + ], + [ + -63.475, + 4.768 + ], + [ + -60.384, + 17.423 + ], + [ + -56.951, + 31.757 + ], + [ + -54.147, + 43.189 + ], + [ + -50.703, + 57.52 + ], + [ + -48.172, + 67.959 + ], + [ + -44.667, + 82.511 + ], + [ + -41.862, + 93.944 + ], + [ + -38.417, + 108.274 + ], + [ + -35.886, + 118.714 + ], + [ + -32.381, + 133.265 + ], + [ + -29.289, + 145.92 + ], + [ + -25.368, + 162.252 + ], + [ + -23.052, + 171.687 + ], + [ + -19.609, + 186.018 + ], + [ + -17.044, + 196.568 + ], + [ + -16.873, + 197.277 + ], + [ + 95.511, + 130.699 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0.94, + 0.94, + 0.94, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 110.763, + 212.689 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 3.468, + 14.34 + ], + [ + 2.625, + 10.848 + ], + [ + 3.055, + 12.63 + ], + [ + 2.618, + 10.81 + ], + [ + 2.889, + 11.924 + ], + [ + 3.407, + 14.078 + ], + [ + 2.461, + 10.1 + ], + [ + 4.922, + 7.438 + ], + [ + 7.495, + 5.479 + ], + [ + 8.293, + 2.582 + ], + [ + 4.755, + 0.569 + ], + [ + 1.791, + 0.154 + ], + [ + 0.433, + 0.03 + ], + [ + 0.304, + -0.006 + ], + [ + 0.322, + 0.223 + ], + [ + 1.301, + -0.027 + ], + [ + 0.336, + -0.054 + ], + [ + 0.358, + -0.008 + ], + [ + 0.422, + -0.008 + ], + [ + 5.034, + -1.477 + ], + [ + 4.592, + -2.488 + ], + [ + 5.07, + -4.733 + ], + [ + 4.249, + -9.054 + ], + [ + 0.88, + -8.221 + ], + [ + -0.042, + -1.299 + ], + [ + -0.008, + -0.346 + ], + [ + 0.207, + -0.25 + ], + [ + -0.07, + -3.445 + ], + [ + -0.215, + -0.439 + ], + [ + -1.077, + -4.46 + ], + [ + -3.057, + -12.631 + ], + [ + -2.635, + -10.885 + ], + [ + -2.957, + -12.223 + ], + [ + -3.307, + -13.672 + ], + [ + -3.46, + -14.303 + ], + [ + -3.136, + -12.966 + ], + [ + -3.054, + -12.632 + ], + [ + -2.789, + -11.518 + ], + [ + -1.089, + -4.498 + ], + [ + -4.712, + 1.975 + ], + [ + 0, + 0 + ], + [ + 0.848, + 3.519 + ], + [ + 1.156, + 4.776 + ], + [ + 0.76, + 3.148 + ], + [ + 1.316, + 5.442 + ], + [ + 1.024, + 4.221 + ], + [ + 1.17, + 4.849 + ], + [ + 0.84, + 3.482 + ], + [ + 1.157, + 4.775 + ], + [ + 0.926, + 3.813 + ], + [ + 1.17, + 4.85 + ], + [ + 0.839, + 3.482 + ], + [ + 1.157, + 4.776 + ], + [ + 0.922, + 3.813 + ], + [ + 1.154, + 4.775 + ], + [ + 1.022, + 4.22 + ], + [ + 1.171, + 4.849 + ], + [ + 0.84, + 3.482 + ], + [ + 1.158, + 4.775 + ], + [ + 0.925, + 3.813 + ], + [ + 1.169, + 4.85 + ], + [ + 0.839, + 3.481 + ], + [ + 1.156, + 4.776 + ], + [ + 0.756, + 3.148 + ], + [ + 1.37, + 5.821 + ], + [ + 0.54, + 5.338 + ], + [ + 0.42, + 1.913 + ], + [ + -0.047, + 0.179 + ], + [ + -0.021, + 1.028 + ], + [ + -0.052, + 0.414 + ], + [ + -0.518, + 2.699 + ], + [ + -1.96, + 4.408 + ], + [ + -1.197, + 2.003 + ], + [ + -2.497, + 2.793 + ], + [ + -2.407, + 2.03 + ], + [ + -3.597, + 2.032 + ], + [ + -3.788, + 1.248 + ], + [ + -4.518, + 0.388 + ], + [ + -2.353, + -0.169 + ], + [ + -2.464, + -0.318 + ], + [ + -2.752, + -0.769 + ], + [ + -4.156, + -2.244 + ], + [ + -3.728, + -3.238 + ], + [ + -1.853, + -2.233 + ], + [ + -2.259, + -4.752 + ], + [ + -0.721, + -2.134 + ], + [ + -0.627, + -2.637 + ], + [ + -1.195, + -4.923 + ], + [ + -1.681, + -7 + ], + [ + -1.638, + -6.697 + ], + [ + -1.229, + -5.228 + ], + [ + -1.408, + -5.811 + ], + [ + -1.766, + -7.293 + ], + [ + -1.245, + -5.147 + ], + [ + -1.426, + -5.886 + ], + [ + -1.765, + -7.254 + ], + [ + -0.872, + -3.591 + ], + [ + -1.172, + -4.85 + ], + [ + -0.831, + -3.443 + ], + [ + -1.184, + -4.885 + ], + [ + -0.906, + -3.739 + ], + [ + -1.332, + -5.517 + ], + [ + -0.838, + -3.481 + ], + [ + -1.315, + -5.442 + ], + [ + -0.632, + -2.575 + ], + [ + -4.275, + 3.657 + ], + [ + 1.527, + 6.314 + ] + ], + "o": [ + [ + -2.623, + -10.848 + ], + [ + -3.056, + -12.63 + ], + [ + -2.614, + -10.81 + ], + [ + -2.886, + -11.924 + ], + [ + -3.41, + -14.078 + ], + [ + -2.446, + -10.105 + ], + [ + -2.11, + -8.664 + ], + [ + -5.118, + -7.738 + ], + [ + -7.008, + -5.122 + ], + [ + -4.575, + -1.424 + ], + [ + -1.784, + -0.213 + ], + [ + -0.427, + -0.167 + ], + [ + -0.302, + 0.006 + ], + [ + -0.34, + -0.041 + ], + [ + -1.302, + 0.026 + ], + [ + -0.312, + 0.235 + ], + [ + -0.359, + 0.007 + ], + [ + -0.422, + 0.009 + ], + [ + -5.276, + 0.165 + ], + [ + -5.005, + 1.468 + ], + [ + -6.083, + 3.295 + ], + [ + -7.344, + 6.859 + ], + [ + -3.501, + 7.461 + ], + [ + -0.137, + 1.291 + ], + [ + 0.007, + 0.347 + ], + [ + -0.031, + 0.271 + ], + [ + 0.07, + 3.446 + ], + [ + 0.291, + 0.427 + ], + [ + 0.322, + 4.601 + ], + [ + 3.051, + 12.633 + ], + [ + 2.635, + 10.886 + ], + [ + 2.959, + 12.222 + ], + [ + 3.308, + 13.671 + ], + [ + 3.461, + 14.304 + ], + [ + 3.137, + 12.966 + ], + [ + 3.056, + 12.632 + ], + [ + 2.786, + 11.518 + ], + [ + 1.088, + 4.497 + ], + [ + 4.787, + -1.826 + ], + [ + 0, + 0 + ], + [ + -0.85, + -3.517 + ], + [ + -1.152, + -4.776 + ], + [ + -0.761, + -3.147 + ], + [ + -1.314, + -5.443 + ], + [ + -1.02, + -4.221 + ], + [ + -1.175, + -4.848 + ], + [ + -0.84, + -3.481 + ], + [ + -1.15, + -4.775 + ], + [ + -0.924, + -3.813 + ], + [ + -1.176, + -4.849 + ], + [ + -0.84, + -3.481 + ], + [ + -1.15, + -4.776 + ], + [ + -0.924, + -3.812 + ], + [ + -1.154, + -4.776 + ], + [ + -1.02, + -4.222 + ], + [ + -1.176, + -4.849 + ], + [ + -0.84, + -3.481 + ], + [ + -1.151, + -4.775 + ], + [ + -0.924, + -3.813 + ], + [ + -1.175, + -4.849 + ], + [ + -0.841, + -3.481 + ], + [ + -1.152, + -4.776 + ], + [ + -0.762, + -3.147 + ], + [ + -1.4, + -5.814 + ], + [ + -1.221, + -5.184 + ], + [ + -0.197, + -1.936 + ], + [ + -0.041, + -0.181 + ], + [ + 0.265, + -1.021 + ], + [ + 0.009, + -0.421 + ], + [ + 0.338, + -2.718 + ], + [ + 0.911, + -4.753 + ], + [ + 0.948, + -2.131 + ], + [ + 1.924, + -3.218 + ], + [ + 2.088, + -2.334 + ], + [ + 3.168, + -2.671 + ], + [ + 3.467, + -1.959 + ], + [ + 4.328, + -1.427 + ], + [ + 2.296, + -0.196 + ], + [ + 2.468, + 0.177 + ], + [ + 2.845, + 0.367 + ], + [ + 4.542, + 1.268 + ], + [ + 4.358, + 2.353 + ], + [ + 2.198, + 1.907 + ], + [ + 3.356, + 4.041 + ], + [ + 0.967, + 2.035 + ], + [ + 0.865, + 2.565 + ], + [ + 1.172, + 4.928 + ], + [ + 1.695, + 6.996 + ], + [ + 1.611, + 6.703 + ], + [ + 1.275, + 5.216 + ], + [ + 1.37, + 5.821 + ], + [ + 1.766, + 7.293 + ], + [ + 1.246, + 5.146 + ], + [ + 1.422, + 5.886 + ], + [ + 1.758, + 7.256 + ], + [ + 0.874, + 3.589 + ], + [ + 1.177, + 4.848 + ], + [ + 0.831, + 3.443 + ], + [ + 1.18, + 4.888 + ], + [ + 0.905, + 3.74 + ], + [ + 1.336, + 5.516 + ], + [ + 0.841, + 3.48 + ], + [ + 1.31, + 5.444 + ], + [ + 0.622, + 2.578 + ], + [ + 4.404, + -3.506 + ], + [ + -1.53, + -6.314 + ], + [ + -3.468, + -14.339 + ] + ], + "v": [ + [ + 94.541, + 62.721 + ], + [ + 86.66, + 30.181 + ], + [ + 77.497, + -7.711 + ], + [ + 69.651, + -40.142 + ], + [ + 60.984, + -75.913 + ], + [ + 50.757, + -118.147 + ], + [ + 43.421, + -148.46 + ], + [ + 32.895, + -172.633 + ], + [ + 14, + -192.488 + ], + [ + -8.93, + -204.091 + ], + [ + -22.93, + -207.073 + ], + [ + -28.302, + -207.543 + ], + [ + -29.603, + -207.603 + ], + [ + -30.512, + -207.585 + ], + [ + -31.534, + -207.682 + ], + [ + -35.438, + -207.603 + ], + [ + -36.45, + -207.465 + ], + [ + -37.524, + -207.443 + ], + [ + -38.79, + -207.418 + ], + [ + -54.209, + -204.64 + ], + [ + -68.627, + -198.731 + ], + [ + -85.347, + -186.711 + ], + [ + -102.634, + -162.767 + ], + [ + -109.304, + -139.245 + ], + [ + -109.441, + -135.358 + ], + [ + -109.42, + -134.319 + ], + [ + -109.522, + -133.505 + ], + [ + -109.313, + -123.17 + ], + [ + -109.095, + -121.785 + ], + [ + -106.585, + -108.271 + ], + [ + -97.425, + -70.375 + ], + [ + -89.508, + -37.722 + ], + [ + -80.645, + -1.051 + ], + [ + -70.714, + 39.962 + ], + [ + -60.337, + 82.874 + ], + [ + -50.922, + 121.771 + ], + [ + -41.763, + 159.667 + ], + [ + -33.397, + 194.218 + ], + [ + -30.13, + 207.71 + ], + [ + -15.883, + 202.005 + ], + [ + -16.053, + 201.296 + ], + [ + -18.618, + 190.746 + ], + [ + -22.061, + 176.415 + ], + [ + -24.378, + 166.981 + ], + [ + -28.298, + 150.649 + ], + [ + -31.391, + 137.993 + ], + [ + -34.896, + 123.443 + ], + [ + -37.427, + 113.002 + ], + [ + -40.872, + 98.672 + ], + [ + -43.676, + 87.24 + ], + [ + -47.181, + 72.688 + ], + [ + -49.712, + 62.248 + ], + [ + -53.156, + 47.917 + ], + [ + -55.96, + 36.486 + ], + [ + -59.394, + 22.152 + ], + [ + -62.484, + 9.496 + ], + [ + -65.99, + -5.055 + ], + [ + -68.52, + -15.496 + ], + [ + -71.968, + -29.825 + ], + [ + -74.77, + -41.258 + ], + [ + -78.274, + -55.81 + ], + [ + -80.804, + -66.25 + ], + [ + -84.252, + -80.58 + ], + [ + -86.564, + -90.015 + ], + [ + -90.731, + -107.463 + ], + [ + -93.99, + -123.106 + ], + [ + -94.475, + -128.915 + ], + [ + -94.474, + -129.485 + ], + [ + -94.343, + -132.57 + ], + [ + -94.337, + -133.832 + ], + [ + -93.208, + -141.97 + ], + [ + -88.847, + -155.684 + ], + [ + -85.59, + -161.868 + ], + [ + -79.034, + -170.954 + ], + [ + -72.344, + -177.55 + ], + [ + -62.192, + -184.587 + ], + [ + -51.275, + -189.315 + ], + [ + -37.996, + -192.087 + ], + [ + -31.037, + -192.38 + ], + [ + -23.638, + -191.803 + ], + [ + -15.248, + -190.091 + ], + [ + -2.213, + -184.8 + ], + [ + 9.863, + -176.345 + ], + [ + 15.896, + -170.071 + ], + [ + 24.382, + -156.905 + ], + [ + 26.952, + -150.658 + ], + [ + 29.147, + -142.843 + ], + [ + 32.758, + -128.081 + ], + [ + 37.815, + -107.085 + ], + [ + 42.643, + -86.974 + ], + [ + 46.438, + -71.317 + ], + [ + 50.647, + -53.878 + ], + [ + 55.951, + -32 + ], + [ + 59.682, + -16.561 + ], + [ + 63.955, + 1.097 + ], + [ + 69.235, + 22.863 + ], + [ + 71.886, + 33.625 + ], + [ + 75.393, + 48.176 + ], + [ + 77.899, + 58.503 + ], + [ + 81.43, + 73.167 + ], + [ + 84.172, + 84.379 + ], + [ + 88.164, + 100.929 + ], + [ + 90.696, + 111.368 + ], + [ + 94.614, + 127.701 + ], + [ + 96.502, + 135.427 + ], + [ + 109.522, + 124.682 + ], + [ + 104.935, + 105.742 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 1, + 0.74, + 0.75, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 109.772, + 207.96 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 2", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 4, + "ty": 4, + "nm": "mouth_smile", + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 500.75, + 815.75, + 0 + ] + }, + "a": { + "k": [ + 0.5, + -0.61, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": [ + { + "i": { + "x": 0, + "y": 1 + }, + "o": { + "x": 1, + "y": 0 + }, + "n": "0_1_1_0", + "t": 32, + "s": [ + { + "i": [ + [ + -0.081, + 0.416 + ], + [ + 0, + 0.614 + ], + [ + 0.017, + 0.114 + ], + [ + 4.432, + 1.114 + ], + [ + 0.455, + 0.099 + ], + [ + 0.63, + 0 + ], + [ + 0.118, + -0.012 + ], + [ + 1.098, + -0.552 + ], + [ + 5.005, + -1.792 + ], + [ + 17.596, + -1.761 + ], + [ + 5.449, + -0.216 + ], + [ + 10.022, + 0.979 + ], + [ + 5.427, + 0.928 + ], + [ + 9.789, + 3.142 + ], + [ + 6.284, + 3.051 + ], + [ + 0.79, + 0.255 + ], + [ + 0.915, + 0.206 + ], + [ + 0.63, + 0 + ], + [ + 0.098, + -0.016 + ], + [ + 1.538, + -3.764 + ], + [ + 0.246, + -0.951 + ], + [ + 0, + -0.58 + ], + [ + -0.022, + -0.149 + ], + [ + -2.832, + -1.687 + ], + [ + -1.661, + -0.753 + ], + [ + -10.509, + -2.813 + ], + [ + -8.427, + -1.306 + ], + [ + -5.769, + -0.425 + ], + [ + -5.447, + -0.289 + ], + [ + -0.241, + -0.027 + ], + [ + -3.43, + 0 + ], + [ + -0.312, + 0.015 + ], + [ + -4.293, + 0.255 + ], + [ + -5.555, + 0.778 + ], + [ + -6.003, + 1.316 + ], + [ + -9.441, + 3.427 + ], + [ + -5.316, + 2.752 + ], + [ + -0.737, + 3.161 + ] + ], + "o": [ + [ + 0, + -0.614 + ], + [ + -0.035, + -0.111 + ], + [ + -0.645, + -4.395 + ], + [ + -0.451, + -0.113 + ], + [ + -0.63, + 0 + ], + [ + -0.116, + 0.032 + ], + [ + -1.23, + 0.122 + ], + [ + -4.743, + 2.385 + ], + [ + -16.622, + 5.95 + ], + [ + -5.424, + 0.543 + ], + [ + -10.064, + 0.399 + ], + [ + -5.48, + -0.536 + ], + [ + -10.152, + -1.736 + ], + [ + -6.661, + -2.138 + ], + [ + -0.75, + -0.364 + ], + [ + -0.888, + -0.287 + ], + [ + -0.63, + 0 + ], + [ + -0.095, + 0.034 + ], + [ + -4.089, + 0.654 + ], + [ + -0.369, + 0.903 + ], + [ + 0, + 0.58 + ], + [ + 0.038, + 0.147 + ], + [ + 0.472, + 3.212 + ], + [ + 1.56, + 0.929 + ], + [ + 9.893, + 4.481 + ], + [ + 8.226, + 2.203 + ], + [ + 5.716, + 0.886 + ], + [ + 5.438, + 0.4 + ], + [ + 0.242, + 0.013 + ], + [ + 3.43, + 0 + ], + [ + 0.311, + -0.029 + ], + [ + 4.296, + -0.209 + ], + [ + 5.604, + -0.333 + ], + [ + 6.089, + -0.852 + ], + [ + 9.827, + -2.154 + ], + [ + 5.635, + -2.045 + ], + [ + 2.928, + -1.516 + ], + [ + 0.096, + -0.413 + ] + ], + "v": [ + [ + -0.25, + -37.45 + ], + [ + -0.25, + -39.292 + ], + [ + -0.352, + -39.627 + ], + [ + -8.441, + -48.41 + ], + [ + -9.805, + -48.71 + ], + [ + -11.695, + -48.71 + ], + [ + -12.043, + -48.617 + ], + [ + -15.549, + -47.635 + ], + [ + -30.201, + -41.435 + ], + [ + -81.575, + -30.008 + ], + [ + -97.904, + -28.937 + ], + [ + -128.04, + -29.897 + ], + [ + -144.399, + -32.098 + ], + [ + -174.316, + -39.388 + ], + [ + -193.782, + -47.058 + ], + [ + -196.066, + -48.073 + ], + [ + -198.805, + -48.71 + ], + [ + -200.695, + -48.71 + ], + [ + -200.982, + -48.612 + ], + [ + -209.494, + -42.035 + ], + [ + -210.25, + -39.19 + ], + [ + -210.25, + -37.45 + ], + [ + -210.139, + -37.008 + ], + [ + -205.218, + -29.585 + ], + [ + -200.329, + -27.133 + ], + [ + -169.658, + -16.368 + ], + [ + -144.675, + -11.119 + ], + [ + -127.458, + -9.034 + ], + [ + -111.119, + -8.149 + ], + [ + -110.395, + -8.07 + ], + [ + -100.105, + -8.07 + ], + [ + -99.172, + -8.154 + ], + [ + -86.283, + -8.786 + ], + [ + -69.554, + -10.561 + ], + [ + -51.415, + -13.81 + ], + [ + -22.49, + -22.107 + ], + [ + -5.996, + -29.157 + ], + [ + -0.494, + -36.201 + ] + ], + "c": true + } + ], + "e": [ + { + "i": [ + [ + -0.044, + 0.566 + ], + [ + 0, + 0.797 + ], + [ + 0.009, + 0.092 + ], + [ + 0.473, + 1.052 + ], + [ + 2.267, + 1.723 + ], + [ + 3.546, + 1.383 + ], + [ + 4.958, + 1.036 + ], + [ + 5.038, + 0.506 + ], + [ + 3.295, + 0.201 + ], + [ + 3.27, + 0.122 + ], + [ + 0.157, + 0.015 + ], + [ + 2.008, + 0 + ], + [ + 0.123, + -0.004 + ], + [ + 2.525, + -0.137 + ], + [ + 3.032, + -0.3 + ], + [ + 3.359, + -0.467 + ], + [ + 3.986, + -0.994 + ], + [ + 4.243, + -1.98 + ], + [ + 2.404, + -2.298 + ], + [ + 0.547, + -2.511 + ], + [ + 0.082, + -0.477 + ], + [ + 0, + -0.797 + ], + [ + -0.009, + -0.122 + ], + [ + -0.09, + -0.954 + ], + [ + -0.77, + -2.696 + ], + [ + -4.189, + -5.385 + ], + [ + -7.486, + -4.304 + ], + [ + -9.579, + -1.491 + ], + [ + -2.987, + -0.129 + ], + [ + -0.602, + -0.035 + ], + [ + -1.278, + 0 + ], + [ + -0.169, + 0.005 + ], + [ + -3.481, + 0.546 + ], + [ + -4.049, + 1.347 + ], + [ + -6.01, + 4.45 + ], + [ + -3.397, + 4.433 + ], + [ + -1.64, + 6.947 + ], + [ + -0.167, + 2.53 + ] + ], + "o": [ + [ + 0, + -0.797 + ], + [ + -0.023, + -0.091 + ], + [ + -0.106, + -1.121 + ], + [ + -1.087, + -2.417 + ], + [ + -2.906, + -2.208 + ], + [ + -4.636, + -1.808 + ], + [ + -4.922, + -1.029 + ], + [ + -3.281, + -0.33 + ], + [ + -3.263, + -0.199 + ], + [ + -0.158, + -0.006 + ], + [ + -2.008, + 0 + ], + [ + -0.123, + 0.014 + ], + [ + -2.53, + 0.082 + ], + [ + -3.048, + 0.165 + ], + [ + -3.381, + 0.335 + ], + [ + -4.103, + 0.571 + ], + [ + -4.648, + 1.159 + ], + [ + -3.171, + 1.48 + ], + [ + -1.997, + 1.909 + ], + [ + -0.103, + 0.473 + ], + [ + 0, + 0.797 + ], + [ + 0.02, + 0.121 + ], + [ + 0.072, + 0.955 + ], + [ + 0.261, + 2.763 + ], + [ + 1.783, + 6.242 + ], + [ + 4.941, + 6.352 + ], + [ + 8.015, + 4.608 + ], + [ + 2.94, + 0.458 + ], + [ + 0.602, + 0.026 + ], + [ + 1.278, + 0 + ], + [ + 0.169, + -0.017 + ], + [ + 3.538, + -0.113 + ], + [ + 4.291, + -0.673 + ], + [ + 7.471, + -2.485 + ], + [ + 4.723, + -3.497 + ], + [ + 4.574, + -5.97 + ], + [ + 0.587, + -2.486 + ], + [ + 0.037, + -0.566 + ] + ], + "v": [ + [ + -36.789, + -43.354 + ], + [ + -36.789, + -45.744 + ], + [ + -36.856, + -46.019 + ], + [ + -37.73, + -49.279 + ], + [ + -42.925, + -55.398 + ], + [ + -52.719, + -60.643 + ], + [ + -67.157, + -64.804 + ], + [ + -82.094, + -67.138 + ], + [ + -91.959, + -67.967 + ], + [ + -101.766, + -68.346 + ], + [ + -102.238, + -68.39 + ], + [ + -108.262, + -68.39 + ], + [ + -108.631, + -68.349 + ], + [ + -116.22, + -68.095 + ], + [ + -125.351, + -67.445 + ], + [ + -135.461, + -66.212 + ], + [ + -147.59, + -63.828 + ], + [ + -161, + -59.264 + ], + [ + -169.508, + -53.749 + ], + [ + -173.463, + -47.173 + ], + [ + -173.711, + -45.744 + ], + [ + -173.711, + -43.354 + ], + [ + -173.652, + -42.991 + ], + [ + -173.44, + -40.125 + ], + [ + -171.868, + -31.94 + ], + [ + -162.909, + -14.495 + ], + [ + -144.266, + 1.498 + ], + [ + -117.864, + 10.635 + ], + [ + -108.971, + 11.506 + ], + [ + -107.167, + 11.61 + ], + [ + -103.333, + 11.61 + ], + [ + -102.827, + 11.56 + ], + [ + -92.3, + 10.584 + ], + [ + -79.789, + 7.549 + ], + [ + -59.57, + -2.862 + ], + [ + -47.388, + -14.757 + ], + [ + -38.06, + -34.133 + ], + [ + -36.92, + -41.656 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0, + "y": 1 + }, + "o": { + "x": 0.333, + "y": 0 + }, + "n": "0_1_0p333_0", + "t": 37, + "s": [ + { + "i": [ + [ + -0.044, + 0.566 + ], + [ + 0, + 0.797 + ], + [ + 0.009, + 0.092 + ], + [ + 0.473, + 1.052 + ], + [ + 2.267, + 1.723 + ], + [ + 3.546, + 1.383 + ], + [ + 4.958, + 1.036 + ], + [ + 5.038, + 0.506 + ], + [ + 3.295, + 0.201 + ], + [ + 3.27, + 0.122 + ], + [ + 0.157, + 0.015 + ], + [ + 2.008, + 0 + ], + [ + 0.123, + -0.004 + ], + [ + 2.525, + -0.137 + ], + [ + 3.032, + -0.3 + ], + [ + 3.359, + -0.467 + ], + [ + 3.986, + -0.994 + ], + [ + 4.243, + -1.98 + ], + [ + 2.404, + -2.298 + ], + [ + 0.547, + -2.511 + ], + [ + 0.082, + -0.477 + ], + [ + 0, + -0.797 + ], + [ + -0.009, + -0.122 + ], + [ + -0.09, + -0.954 + ], + [ + -0.77, + -2.696 + ], + [ + -4.189, + -5.385 + ], + [ + -7.486, + -4.304 + ], + [ + -9.579, + -1.491 + ], + [ + -2.987, + -0.129 + ], + [ + -0.602, + -0.035 + ], + [ + -1.278, + 0 + ], + [ + -0.169, + 0.005 + ], + [ + -3.481, + 0.546 + ], + [ + -4.049, + 1.347 + ], + [ + -6.01, + 4.45 + ], + [ + -3.397, + 4.433 + ], + [ + -1.64, + 6.947 + ], + [ + -0.167, + 2.53 + ] + ], + "o": [ + [ + 0, + -0.797 + ], + [ + -0.023, + -0.091 + ], + [ + -0.106, + -1.121 + ], + [ + -1.087, + -2.417 + ], + [ + -2.906, + -2.208 + ], + [ + -4.636, + -1.808 + ], + [ + -4.922, + -1.029 + ], + [ + -3.281, + -0.33 + ], + [ + -3.263, + -0.199 + ], + [ + -0.158, + -0.006 + ], + [ + -2.008, + 0 + ], + [ + -0.123, + 0.014 + ], + [ + -2.53, + 0.082 + ], + [ + -3.048, + 0.165 + ], + [ + -3.381, + 0.335 + ], + [ + -4.103, + 0.571 + ], + [ + -4.648, + 1.159 + ], + [ + -3.171, + 1.48 + ], + [ + -1.997, + 1.909 + ], + [ + -0.103, + 0.473 + ], + [ + 0, + 0.797 + ], + [ + 0.02, + 0.121 + ], + [ + 0.072, + 0.955 + ], + [ + 0.261, + 2.763 + ], + [ + 1.783, + 6.242 + ], + [ + 4.941, + 6.352 + ], + [ + 8.015, + 4.608 + ], + [ + 2.94, + 0.458 + ], + [ + 0.602, + 0.026 + ], + [ + 1.278, + 0 + ], + [ + 0.169, + -0.017 + ], + [ + 3.538, + -0.113 + ], + [ + 4.291, + -0.673 + ], + [ + 7.471, + -2.485 + ], + [ + 4.723, + -3.497 + ], + [ + 4.574, + -5.97 + ], + [ + 0.587, + -2.486 + ], + [ + 0.037, + -0.566 + ] + ], + "v": [ + [ + -36.789, + -43.354 + ], + [ + -36.789, + -45.744 + ], + [ + -36.856, + -46.019 + ], + [ + -37.73, + -49.279 + ], + [ + -42.925, + -55.398 + ], + [ + -52.719, + -60.643 + ], + [ + -67.157, + -64.804 + ], + [ + -82.094, + -67.138 + ], + [ + -91.959, + -67.967 + ], + [ + -101.766, + -68.346 + ], + [ + -102.238, + -68.39 + ], + [ + -108.262, + -68.39 + ], + [ + -108.631, + -68.349 + ], + [ + -116.22, + -68.095 + ], + [ + -125.351, + -67.445 + ], + [ + -135.461, + -66.212 + ], + [ + -147.59, + -63.828 + ], + [ + -161, + -59.264 + ], + [ + -169.508, + -53.749 + ], + [ + -173.463, + -47.173 + ], + [ + -173.711, + -45.744 + ], + [ + -173.711, + -43.354 + ], + [ + -173.652, + -42.991 + ], + [ + -173.44, + -40.125 + ], + [ + -171.868, + -31.94 + ], + [ + -162.909, + -14.495 + ], + [ + -144.266, + 1.498 + ], + [ + -117.864, + 10.635 + ], + [ + -108.971, + 11.506 + ], + [ + -107.167, + 11.61 + ], + [ + -103.333, + 11.61 + ], + [ + -102.827, + 11.56 + ], + [ + -92.3, + 10.584 + ], + [ + -79.789, + 7.549 + ], + [ + -59.57, + -2.862 + ], + [ + -47.388, + -14.757 + ], + [ + -38.06, + -34.133 + ], + [ + -36.92, + -41.656 + ] + ], + "c": true + } + ], + "e": [ + { + "i": [ + [ + -0.044, + 0.566 + ], + [ + 0, + 0.797 + ], + [ + 0.009, + 0.092 + ], + [ + 0.473, + 1.052 + ], + [ + 2.267, + 1.723 + ], + [ + 3.546, + 1.383 + ], + [ + 4.958, + 1.036 + ], + [ + 5.038, + 0.506 + ], + [ + 3.295, + 0.201 + ], + [ + 3.27, + 0.122 + ], + [ + 0.157, + 0.015 + ], + [ + 2.008, + 0 + ], + [ + 0.123, + -0.004 + ], + [ + 2.525, + -0.137 + ], + [ + 3.032, + -0.3 + ], + [ + 3.359, + -0.467 + ], + [ + 3.986, + -0.994 + ], + [ + 4.243, + -1.98 + ], + [ + 2.404, + -2.298 + ], + [ + 0.547, + -2.511 + ], + [ + 0.082, + -0.477 + ], + [ + 0, + -0.797 + ], + [ + -0.009, + -0.122 + ], + [ + -0.09, + -0.954 + ], + [ + -0.77, + -2.696 + ], + [ + -4.189, + -5.385 + ], + [ + -7.486, + -4.304 + ], + [ + -9.579, + -1.491 + ], + [ + -2.987, + -0.129 + ], + [ + -0.602, + -0.035 + ], + [ + -1.278, + 0 + ], + [ + -0.169, + 0.005 + ], + [ + -3.481, + 0.546 + ], + [ + -4.049, + 1.347 + ], + [ + -6.01, + 4.45 + ], + [ + -3.397, + 4.433 + ], + [ + -1.64, + 6.947 + ], + [ + -0.167, + 2.53 + ] + ], + "o": [ + [ + 0, + -0.797 + ], + [ + -0.023, + -0.091 + ], + [ + -0.106, + -1.121 + ], + [ + -1.087, + -2.417 + ], + [ + -2.906, + -2.208 + ], + [ + -4.636, + -1.808 + ], + [ + -4.922, + -1.029 + ], + [ + -3.281, + -0.33 + ], + [ + -3.263, + -0.199 + ], + [ + -0.158, + -0.006 + ], + [ + -2.008, + 0 + ], + [ + -0.123, + 0.014 + ], + [ + -2.53, + 0.082 + ], + [ + -3.048, + 0.165 + ], + [ + -3.381, + 0.335 + ], + [ + -4.103, + 0.571 + ], + [ + -4.648, + 1.159 + ], + [ + -3.171, + 1.48 + ], + [ + -1.997, + 1.909 + ], + [ + -0.103, + 0.473 + ], + [ + 0, + 0.797 + ], + [ + 0.02, + 0.121 + ], + [ + 0.072, + 0.955 + ], + [ + 0.261, + 2.763 + ], + [ + 1.783, + 6.242 + ], + [ + 4.941, + 6.352 + ], + [ + 8.015, + 4.608 + ], + [ + 2.94, + 0.458 + ], + [ + 0.602, + 0.026 + ], + [ + 1.278, + 0 + ], + [ + 0.169, + -0.017 + ], + [ + 3.538, + -0.113 + ], + [ + 4.291, + -0.673 + ], + [ + 7.471, + -2.485 + ], + [ + 4.723, + -3.497 + ], + [ + 4.574, + -5.97 + ], + [ + 0.587, + -2.486 + ], + [ + 0.037, + -0.566 + ] + ], + "v": [ + [ + -36.789, + -43.354 + ], + [ + -36.789, + -45.744 + ], + [ + -36.856, + -46.019 + ], + [ + -37.73, + -49.279 + ], + [ + -42.925, + -55.398 + ], + [ + -52.719, + -60.643 + ], + [ + -67.157, + -64.804 + ], + [ + -82.094, + -67.138 + ], + [ + -91.959, + -67.967 + ], + [ + -101.766, + -68.346 + ], + [ + -102.238, + -68.39 + ], + [ + -108.262, + -68.39 + ], + [ + -108.631, + -68.349 + ], + [ + -116.22, + -68.095 + ], + [ + -125.351, + -67.445 + ], + [ + -135.461, + -66.212 + ], + [ + -147.59, + -63.828 + ], + [ + -161, + -59.264 + ], + [ + -169.508, + -53.749 + ], + [ + -173.463, + -47.173 + ], + [ + -173.711, + -45.744 + ], + [ + -173.711, + -43.354 + ], + [ + -173.652, + -42.991 + ], + [ + -173.44, + -40.125 + ], + [ + -171.868, + -31.94 + ], + [ + -162.909, + -14.495 + ], + [ + -144.266, + 1.498 + ], + [ + -117.864, + 10.635 + ], + [ + -108.971, + 11.506 + ], + [ + -107.167, + 11.61 + ], + [ + -103.333, + 11.61 + ], + [ + -102.827, + 11.56 + ], + [ + -92.3, + 10.584 + ], + [ + -79.789, + 7.549 + ], + [ + -59.57, + -2.862 + ], + [ + -47.388, + -14.757 + ], + [ + -38.06, + -34.133 + ], + [ + -36.92, + -41.656 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.667, + "y": 1 + }, + "o": { + "x": 0.333, + "y": 0 + }, + "n": "0p667_1_0p333_0", + "t": 44, + "s": [ + { + "i": [ + [ + -0.044, + 0.566 + ], + [ + 0, + 0.797 + ], + [ + 0.009, + 0.092 + ], + [ + 0.473, + 1.052 + ], + [ + 2.267, + 1.723 + ], + [ + 3.546, + 1.383 + ], + [ + 4.958, + 1.036 + ], + [ + 5.038, + 0.506 + ], + [ + 3.295, + 0.201 + ], + [ + 3.27, + 0.122 + ], + [ + 0.157, + 0.015 + ], + [ + 2.008, + 0 + ], + [ + 0.123, + -0.004 + ], + [ + 2.525, + -0.137 + ], + [ + 3.032, + -0.3 + ], + [ + 3.359, + -0.467 + ], + [ + 3.986, + -0.994 + ], + [ + 4.243, + -1.98 + ], + [ + 2.404, + -2.298 + ], + [ + 0.547, + -2.511 + ], + [ + 0.082, + -0.477 + ], + [ + 0, + -0.797 + ], + [ + -0.009, + -0.122 + ], + [ + -0.09, + -0.954 + ], + [ + -0.77, + -2.696 + ], + [ + -4.189, + -5.385 + ], + [ + -7.486, + -4.304 + ], + [ + -9.579, + -1.491 + ], + [ + -2.987, + -0.129 + ], + [ + -0.602, + -0.035 + ], + [ + -1.278, + 0 + ], + [ + -0.169, + 0.005 + ], + [ + -3.481, + 0.546 + ], + [ + -4.049, + 1.347 + ], + [ + -6.01, + 4.45 + ], + [ + -3.397, + 4.433 + ], + [ + -1.64, + 6.947 + ], + [ + -0.167, + 2.53 + ] + ], + "o": [ + [ + 0, + -0.797 + ], + [ + -0.023, + -0.091 + ], + [ + -0.106, + -1.121 + ], + [ + -1.087, + -2.417 + ], + [ + -2.906, + -2.208 + ], + [ + -4.636, + -1.808 + ], + [ + -4.922, + -1.029 + ], + [ + -3.281, + -0.33 + ], + [ + -3.263, + -0.199 + ], + [ + -0.158, + -0.006 + ], + [ + -2.008, + 0 + ], + [ + -0.123, + 0.014 + ], + [ + -2.53, + 0.082 + ], + [ + -3.048, + 0.165 + ], + [ + -3.381, + 0.335 + ], + [ + -4.103, + 0.571 + ], + [ + -4.648, + 1.159 + ], + [ + -3.171, + 1.48 + ], + [ + -1.997, + 1.909 + ], + [ + -0.103, + 0.473 + ], + [ + 0, + 0.797 + ], + [ + 0.02, + 0.121 + ], + [ + 0.072, + 0.955 + ], + [ + 0.261, + 2.763 + ], + [ + 1.783, + 6.242 + ], + [ + 4.941, + 6.352 + ], + [ + 8.015, + 4.608 + ], + [ + 2.94, + 0.458 + ], + [ + 0.602, + 0.026 + ], + [ + 1.278, + 0 + ], + [ + 0.169, + -0.017 + ], + [ + 3.538, + -0.113 + ], + [ + 4.291, + -0.673 + ], + [ + 7.471, + -2.485 + ], + [ + 4.723, + -3.497 + ], + [ + 4.574, + -5.97 + ], + [ + 0.587, + -2.486 + ], + [ + 0.037, + -0.566 + ] + ], + "v": [ + [ + -36.789, + -43.354 + ], + [ + -36.789, + -45.744 + ], + [ + -36.856, + -46.019 + ], + [ + -37.73, + -49.279 + ], + [ + -42.925, + -55.398 + ], + [ + -52.719, + -60.643 + ], + [ + -67.157, + -64.804 + ], + [ + -82.094, + -67.138 + ], + [ + -91.959, + -67.967 + ], + [ + -101.766, + -68.346 + ], + [ + -102.238, + -68.39 + ], + [ + -108.262, + -68.39 + ], + [ + -108.631, + -68.349 + ], + [ + -116.22, + -68.095 + ], + [ + -125.351, + -67.445 + ], + [ + -135.461, + -66.212 + ], + [ + -147.59, + -63.828 + ], + [ + -161, + -59.264 + ], + [ + -169.508, + -53.749 + ], + [ + -173.463, + -47.173 + ], + [ + -173.711, + -45.744 + ], + [ + -173.711, + -43.354 + ], + [ + -173.652, + -42.991 + ], + [ + -173.44, + -40.125 + ], + [ + -171.868, + -31.94 + ], + [ + -162.909, + -14.495 + ], + [ + -144.266, + 1.498 + ], + [ + -117.864, + 10.635 + ], + [ + -108.971, + 11.506 + ], + [ + -107.167, + 11.61 + ], + [ + -103.333, + 11.61 + ], + [ + -102.827, + 11.56 + ], + [ + -92.3, + 10.584 + ], + [ + -79.789, + 7.549 + ], + [ + -59.57, + -2.862 + ], + [ + -47.388, + -14.757 + ], + [ + -38.06, + -34.133 + ], + [ + -36.92, + -41.656 + ] + ], + "c": true + } + ], + "e": [ + { + "i": [ + [ + -0.081, + 0.416 + ], + [ + 0, + 0.614 + ], + [ + 0.017, + 0.114 + ], + [ + 4.432, + 1.114 + ], + [ + 0.455, + 0.099 + ], + [ + 0.63, + 0 + ], + [ + 0.118, + -0.012 + ], + [ + 1.098, + -0.552 + ], + [ + 5.005, + -1.792 + ], + [ + 17.596, + -1.761 + ], + [ + 5.449, + -0.216 + ], + [ + 10.022, + 0.979 + ], + [ + 5.427, + 0.928 + ], + [ + 9.789, + 3.142 + ], + [ + 6.284, + 3.051 + ], + [ + 0.79, + 0.255 + ], + [ + 0.915, + 0.206 + ], + [ + 0.63, + 0 + ], + [ + 0.098, + -0.016 + ], + [ + 1.538, + -3.764 + ], + [ + 0.246, + -0.951 + ], + [ + 0, + -0.58 + ], + [ + -0.022, + -0.149 + ], + [ + -2.832, + -1.687 + ], + [ + -1.661, + -0.753 + ], + [ + -10.509, + -2.813 + ], + [ + -8.427, + -1.306 + ], + [ + -5.769, + -0.425 + ], + [ + -5.447, + -0.289 + ], + [ + -0.241, + -0.027 + ], + [ + -3.43, + 0 + ], + [ + -0.312, + 0.015 + ], + [ + -4.293, + 0.255 + ], + [ + -5.555, + 0.778 + ], + [ + -6.003, + 1.316 + ], + [ + -9.441, + 3.427 + ], + [ + -5.316, + 2.752 + ], + [ + -0.737, + 3.161 + ] + ], + "o": [ + [ + 0, + -0.614 + ], + [ + -0.035, + -0.111 + ], + [ + -0.645, + -4.395 + ], + [ + -0.451, + -0.113 + ], + [ + -0.63, + 0 + ], + [ + -0.116, + 0.032 + ], + [ + -1.23, + 0.122 + ], + [ + -4.743, + 2.385 + ], + [ + -16.622, + 5.95 + ], + [ + -5.424, + 0.543 + ], + [ + -10.064, + 0.399 + ], + [ + -5.48, + -0.536 + ], + [ + -10.152, + -1.736 + ], + [ + -6.661, + -2.138 + ], + [ + -0.75, + -0.364 + ], + [ + -0.888, + -0.287 + ], + [ + -0.63, + 0 + ], + [ + -0.095, + 0.034 + ], + [ + -4.089, + 0.654 + ], + [ + -0.369, + 0.903 + ], + [ + 0, + 0.58 + ], + [ + 0.038, + 0.147 + ], + [ + 0.472, + 3.212 + ], + [ + 1.56, + 0.929 + ], + [ + 9.893, + 4.481 + ], + [ + 8.226, + 2.203 + ], + [ + 5.716, + 0.886 + ], + [ + 5.438, + 0.4 + ], + [ + 0.242, + 0.013 + ], + [ + 3.43, + 0 + ], + [ + 0.311, + -0.029 + ], + [ + 4.296, + -0.209 + ], + [ + 5.604, + -0.333 + ], + [ + 6.089, + -0.852 + ], + [ + 9.827, + -2.154 + ], + [ + 5.635, + -2.045 + ], + [ + 2.928, + -1.516 + ], + [ + 0.096, + -0.413 + ] + ], + "v": [ + [ + -0.25, + -37.45 + ], + [ + -0.25, + -39.292 + ], + [ + -0.352, + -39.627 + ], + [ + -8.441, + -48.41 + ], + [ + -9.805, + -48.71 + ], + [ + -11.695, + -48.71 + ], + [ + -12.043, + -48.617 + ], + [ + -15.549, + -47.635 + ], + [ + -30.201, + -41.435 + ], + [ + -81.575, + -30.008 + ], + [ + -97.904, + -28.937 + ], + [ + -128.04, + -29.897 + ], + [ + -144.399, + -32.098 + ], + [ + -174.316, + -39.388 + ], + [ + -193.782, + -47.058 + ], + [ + -196.066, + -48.073 + ], + [ + -198.805, + -48.71 + ], + [ + -200.695, + -48.71 + ], + [ + -200.982, + -48.612 + ], + [ + -209.494, + -42.035 + ], + [ + -210.25, + -39.19 + ], + [ + -210.25, + -37.45 + ], + [ + -210.139, + -37.008 + ], + [ + -205.218, + -29.585 + ], + [ + -200.329, + -27.133 + ], + [ + -169.658, + -16.368 + ], + [ + -144.675, + -11.119 + ], + [ + -127.458, + -9.034 + ], + [ + -111.119, + -8.149 + ], + [ + -110.395, + -8.07 + ], + [ + -100.105, + -8.07 + ], + [ + -99.172, + -8.154 + ], + [ + -86.283, + -8.786 + ], + [ + -69.554, + -10.561 + ], + [ + -51.415, + -13.81 + ], + [ + -22.49, + -22.107 + ], + [ + -5.996, + -29.157 + ], + [ + -0.494, + -36.201 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.667, + "y": 1 + }, + "o": { + "x": 1, + "y": 0 + }, + "n": "0p667_1_1_0", + "t": 48, + "s": [ + { + "i": [ + [ + -0.081, + 0.416 + ], + [ + 0, + 0.614 + ], + [ + 0.017, + 0.114 + ], + [ + 4.432, + 1.114 + ], + [ + 0.455, + 0.099 + ], + [ + 0.63, + 0 + ], + [ + 0.118, + -0.012 + ], + [ + 1.098, + -0.552 + ], + [ + 5.005, + -1.792 + ], + [ + 17.596, + -1.761 + ], + [ + 5.449, + -0.216 + ], + [ + 10.022, + 0.979 + ], + [ + 5.427, + 0.928 + ], + [ + 9.789, + 3.142 + ], + [ + 6.284, + 3.051 + ], + [ + 0.79, + 0.255 + ], + [ + 0.915, + 0.206 + ], + [ + 0.63, + 0 + ], + [ + 0.098, + -0.016 + ], + [ + 1.538, + -3.764 + ], + [ + 0.246, + -0.951 + ], + [ + 0, + -0.58 + ], + [ + -0.022, + -0.149 + ], + [ + -2.832, + -1.687 + ], + [ + -1.661, + -0.753 + ], + [ + -10.509, + -2.813 + ], + [ + -8.427, + -1.306 + ], + [ + -5.769, + -0.425 + ], + [ + -5.447, + -0.289 + ], + [ + -0.241, + -0.027 + ], + [ + -3.43, + 0 + ], + [ + -0.312, + 0.015 + ], + [ + -4.293, + 0.255 + ], + [ + -5.555, + 0.778 + ], + [ + -6.003, + 1.316 + ], + [ + -9.441, + 3.427 + ], + [ + -5.316, + 2.752 + ], + [ + -0.737, + 3.161 + ] + ], + "o": [ + [ + 0, + -0.614 + ], + [ + -0.035, + -0.111 + ], + [ + -0.645, + -4.395 + ], + [ + -0.451, + -0.113 + ], + [ + -0.63, + 0 + ], + [ + -0.116, + 0.032 + ], + [ + -1.23, + 0.122 + ], + [ + -4.743, + 2.385 + ], + [ + -16.622, + 5.95 + ], + [ + -5.424, + 0.543 + ], + [ + -10.064, + 0.399 + ], + [ + -5.48, + -0.536 + ], + [ + -10.152, + -1.736 + ], + [ + -6.661, + -2.138 + ], + [ + -0.75, + -0.364 + ], + [ + -0.888, + -0.287 + ], + [ + -0.63, + 0 + ], + [ + -0.095, + 0.034 + ], + [ + -4.089, + 0.654 + ], + [ + -0.369, + 0.903 + ], + [ + 0, + 0.58 + ], + [ + 0.038, + 0.147 + ], + [ + 0.472, + 3.212 + ], + [ + 1.56, + 0.929 + ], + [ + 9.893, + 4.481 + ], + [ + 8.226, + 2.203 + ], + [ + 5.716, + 0.886 + ], + [ + 5.438, + 0.4 + ], + [ + 0.242, + 0.013 + ], + [ + 3.43, + 0 + ], + [ + 0.311, + -0.029 + ], + [ + 4.296, + -0.209 + ], + [ + 5.604, + -0.333 + ], + [ + 6.089, + -0.852 + ], + [ + 9.827, + -2.154 + ], + [ + 5.635, + -2.045 + ], + [ + 2.928, + -1.516 + ], + [ + 0.096, + -0.413 + ] + ], + "v": [ + [ + -0.25, + -37.45 + ], + [ + -0.25, + -39.292 + ], + [ + -0.352, + -39.627 + ], + [ + -8.441, + -48.41 + ], + [ + -9.805, + -48.71 + ], + [ + -11.695, + -48.71 + ], + [ + -12.043, + -48.617 + ], + [ + -15.549, + -47.635 + ], + [ + -30.201, + -41.435 + ], + [ + -81.575, + -30.008 + ], + [ + -97.904, + -28.937 + ], + [ + -128.04, + -29.897 + ], + [ + -144.399, + -32.098 + ], + [ + -174.316, + -39.388 + ], + [ + -193.782, + -47.058 + ], + [ + -196.066, + -48.073 + ], + [ + -198.805, + -48.71 + ], + [ + -200.695, + -48.71 + ], + [ + -200.982, + -48.612 + ], + [ + -209.494, + -42.035 + ], + [ + -210.25, + -39.19 + ], + [ + -210.25, + -37.45 + ], + [ + -210.139, + -37.008 + ], + [ + -205.218, + -29.585 + ], + [ + -200.329, + -27.133 + ], + [ + -169.658, + -16.368 + ], + [ + -144.675, + -11.119 + ], + [ + -127.458, + -9.034 + ], + [ + -111.119, + -8.149 + ], + [ + -110.395, + -8.07 + ], + [ + -100.105, + -8.07 + ], + [ + -99.172, + -8.154 + ], + [ + -86.283, + -8.786 + ], + [ + -69.554, + -10.561 + ], + [ + -51.415, + -13.81 + ], + [ + -22.49, + -22.107 + ], + [ + -5.996, + -29.157 + ], + [ + -0.494, + -36.201 + ] + ], + "c": true + } + ], + "e": [ + { + "i": [ + [ + -0.081, + 0.416 + ], + [ + 0, + 0.614 + ], + [ + 0.017, + 0.114 + ], + [ + 4.432, + 1.114 + ], + [ + 0.455, + 0.099 + ], + [ + 0.63, + 0 + ], + [ + 0.118, + -0.012 + ], + [ + 1.098, + -0.552 + ], + [ + 5.005, + -1.792 + ], + [ + 17.596, + -1.761 + ], + [ + 5.449, + -0.216 + ], + [ + 10.022, + 0.979 + ], + [ + 5.427, + 0.928 + ], + [ + 9.789, + 3.142 + ], + [ + 6.284, + 3.051 + ], + [ + 0.79, + 0.255 + ], + [ + 0.915, + 0.206 + ], + [ + 0.63, + 0 + ], + [ + 0.098, + -0.016 + ], + [ + 1.538, + -3.764 + ], + [ + 0.246, + -0.951 + ], + [ + 0, + -0.58 + ], + [ + -0.022, + -0.149 + ], + [ + -2.832, + -1.687 + ], + [ + -1.661, + -0.753 + ], + [ + -10.509, + -2.813 + ], + [ + -8.427, + -1.306 + ], + [ + -5.769, + -0.425 + ], + [ + -5.447, + -0.289 + ], + [ + -0.241, + -0.027 + ], + [ + -3.43, + 0 + ], + [ + -0.312, + 0.015 + ], + [ + -4.293, + 0.255 + ], + [ + -5.555, + 0.778 + ], + [ + -6.003, + 1.316 + ], + [ + -9.441, + 3.427 + ], + [ + -5.316, + 2.752 + ], + [ + -0.737, + 3.161 + ] + ], + "o": [ + [ + 0, + -0.614 + ], + [ + -0.035, + -0.111 + ], + [ + -0.645, + -4.395 + ], + [ + -0.451, + -0.113 + ], + [ + -0.63, + 0 + ], + [ + -0.116, + 0.032 + ], + [ + -1.23, + 0.122 + ], + [ + -4.743, + 2.385 + ], + [ + -16.622, + 5.95 + ], + [ + -5.424, + 0.543 + ], + [ + -10.064, + 0.399 + ], + [ + -5.48, + -0.536 + ], + [ + -10.152, + -1.736 + ], + [ + -6.661, + -2.138 + ], + [ + -0.75, + -0.364 + ], + [ + -0.888, + -0.287 + ], + [ + -0.63, + 0 + ], + [ + -0.095, + 0.034 + ], + [ + -4.089, + 0.654 + ], + [ + -0.369, + 0.903 + ], + [ + 0, + 0.58 + ], + [ + 0.038, + 0.147 + ], + [ + 0.472, + 3.212 + ], + [ + 1.56, + 0.929 + ], + [ + 9.893, + 4.481 + ], + [ + 8.226, + 2.203 + ], + [ + 5.716, + 0.886 + ], + [ + 5.438, + 0.4 + ], + [ + 0.242, + 0.013 + ], + [ + 3.43, + 0 + ], + [ + 0.311, + -0.029 + ], + [ + 4.296, + -0.209 + ], + [ + 5.604, + -0.333 + ], + [ + 6.089, + -0.852 + ], + [ + 9.827, + -2.154 + ], + [ + 5.635, + -2.045 + ], + [ + 2.928, + -1.516 + ], + [ + 0.096, + -0.413 + ] + ], + "v": [ + [ + -0.25, + -37.45 + ], + [ + -0.25, + -39.292 + ], + [ + -0.352, + -39.627 + ], + [ + -8.441, + -48.41 + ], + [ + -9.805, + -48.71 + ], + [ + -11.695, + -48.71 + ], + [ + -12.043, + -48.617 + ], + [ + -15.549, + -47.635 + ], + [ + -30.201, + -41.435 + ], + [ + -81.575, + -30.008 + ], + [ + -97.904, + -28.937 + ], + [ + -128.04, + -29.897 + ], + [ + -144.399, + -32.098 + ], + [ + -174.316, + -39.388 + ], + [ + -193.782, + -47.058 + ], + [ + -196.066, + -48.073 + ], + [ + -198.805, + -48.71 + ], + [ + -200.695, + -48.71 + ], + [ + -200.982, + -48.612 + ], + [ + -209.494, + -42.035 + ], + [ + -210.25, + -39.19 + ], + [ + -210.25, + -37.45 + ], + [ + -210.139, + -37.008 + ], + [ + -205.218, + -29.585 + ], + [ + -200.329, + -27.133 + ], + [ + -169.658, + -16.368 + ], + [ + -144.675, + -11.119 + ], + [ + -127.458, + -9.034 + ], + [ + -111.119, + -8.149 + ], + [ + -110.395, + -8.07 + ], + [ + -100.105, + -8.07 + ], + [ + -99.172, + -8.154 + ], + [ + -86.283, + -8.786 + ], + [ + -69.554, + -10.561 + ], + [ + -51.415, + -13.81 + ], + [ + -22.49, + -22.107 + ], + [ + -5.996, + -29.157 + ], + [ + -0.494, + -36.201 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0, + "y": 1 + }, + "o": { + "x": 1, + "y": 0 + }, + "n": "0_1_1_0", + "t": 55, + "s": [ + { + "i": [ + [ + -0.081, + 0.416 + ], + [ + 0, + 0.614 + ], + [ + 0.017, + 0.114 + ], + [ + 4.432, + 1.114 + ], + [ + 0.455, + 0.099 + ], + [ + 0.63, + 0 + ], + [ + 0.118, + -0.012 + ], + [ + 1.098, + -0.552 + ], + [ + 5.005, + -1.792 + ], + [ + 17.596, + -1.761 + ], + [ + 5.449, + -0.216 + ], + [ + 10.022, + 0.979 + ], + [ + 5.427, + 0.928 + ], + [ + 9.789, + 3.142 + ], + [ + 6.284, + 3.051 + ], + [ + 0.79, + 0.255 + ], + [ + 0.915, + 0.206 + ], + [ + 0.63, + 0 + ], + [ + 0.098, + -0.016 + ], + [ + 1.538, + -3.764 + ], + [ + 0.246, + -0.951 + ], + [ + 0, + -0.58 + ], + [ + -0.022, + -0.149 + ], + [ + -2.832, + -1.687 + ], + [ + -1.661, + -0.753 + ], + [ + -10.509, + -2.813 + ], + [ + -8.427, + -1.306 + ], + [ + -5.769, + -0.425 + ], + [ + -5.447, + -0.289 + ], + [ + -0.241, + -0.027 + ], + [ + -3.43, + 0 + ], + [ + -0.312, + 0.015 + ], + [ + -4.293, + 0.255 + ], + [ + -5.555, + 0.778 + ], + [ + -6.003, + 1.316 + ], + [ + -9.441, + 3.427 + ], + [ + -5.316, + 2.752 + ], + [ + -0.737, + 3.161 + ] + ], + "o": [ + [ + 0, + -0.614 + ], + [ + -0.035, + -0.111 + ], + [ + -0.645, + -4.395 + ], + [ + -0.451, + -0.113 + ], + [ + -0.63, + 0 + ], + [ + -0.116, + 0.032 + ], + [ + -1.23, + 0.122 + ], + [ + -4.743, + 2.385 + ], + [ + -16.622, + 5.95 + ], + [ + -5.424, + 0.543 + ], + [ + -10.064, + 0.399 + ], + [ + -5.48, + -0.536 + ], + [ + -10.152, + -1.736 + ], + [ + -6.661, + -2.138 + ], + [ + -0.75, + -0.364 + ], + [ + -0.888, + -0.287 + ], + [ + -0.63, + 0 + ], + [ + -0.095, + 0.034 + ], + [ + -4.089, + 0.654 + ], + [ + -0.369, + 0.903 + ], + [ + 0, + 0.58 + ], + [ + 0.038, + 0.147 + ], + [ + 0.472, + 3.212 + ], + [ + 1.56, + 0.929 + ], + [ + 9.893, + 4.481 + ], + [ + 8.226, + 2.203 + ], + [ + 5.716, + 0.886 + ], + [ + 5.438, + 0.4 + ], + [ + 0.242, + 0.013 + ], + [ + 3.43, + 0 + ], + [ + 0.311, + -0.029 + ], + [ + 4.296, + -0.209 + ], + [ + 5.604, + -0.333 + ], + [ + 6.089, + -0.852 + ], + [ + 9.827, + -2.154 + ], + [ + 5.635, + -2.045 + ], + [ + 2.928, + -1.516 + ], + [ + 0.096, + -0.413 + ] + ], + "v": [ + [ + -0.25, + -37.45 + ], + [ + -0.25, + -39.292 + ], + [ + -0.352, + -39.627 + ], + [ + -8.441, + -48.41 + ], + [ + -9.805, + -48.71 + ], + [ + -11.695, + -48.71 + ], + [ + -12.043, + -48.617 + ], + [ + -15.549, + -47.635 + ], + [ + -30.201, + -41.435 + ], + [ + -81.575, + -30.008 + ], + [ + -97.904, + -28.937 + ], + [ + -128.04, + -29.897 + ], + [ + -144.399, + -32.098 + ], + [ + -174.316, + -39.388 + ], + [ + -193.782, + -47.058 + ], + [ + -196.066, + -48.073 + ], + [ + -198.805, + -48.71 + ], + [ + -200.695, + -48.71 + ], + [ + -200.982, + -48.612 + ], + [ + -209.494, + -42.035 + ], + [ + -210.25, + -39.19 + ], + [ + -210.25, + -37.45 + ], + [ + -210.139, + -37.008 + ], + [ + -205.218, + -29.585 + ], + [ + -200.329, + -27.133 + ], + [ + -169.658, + -16.368 + ], + [ + -144.675, + -11.119 + ], + [ + -127.458, + -9.034 + ], + [ + -111.119, + -8.149 + ], + [ + -110.395, + -8.07 + ], + [ + -100.105, + -8.07 + ], + [ + -99.172, + -8.154 + ], + [ + -86.283, + -8.786 + ], + [ + -69.554, + -10.561 + ], + [ + -51.415, + -13.81 + ], + [ + -22.49, + -22.107 + ], + [ + -5.996, + -29.157 + ], + [ + -0.494, + -36.201 + ] + ], + "c": true + } + ], + "e": [ + { + "i": [ + [ + -0.044, + 0.566 + ], + [ + 0, + 0.797 + ], + [ + 0.009, + 0.092 + ], + [ + 0.473, + 1.052 + ], + [ + 2.267, + 1.723 + ], + [ + 3.546, + 1.383 + ], + [ + 4.958, + 1.036 + ], + [ + 5.038, + 0.506 + ], + [ + 3.295, + 0.201 + ], + [ + 3.27, + 0.122 + ], + [ + 0.157, + 0.015 + ], + [ + 2.008, + 0 + ], + [ + 0.123, + -0.004 + ], + [ + 2.525, + -0.137 + ], + [ + 3.032, + -0.3 + ], + [ + 3.359, + -0.467 + ], + [ + 3.986, + -0.994 + ], + [ + 4.243, + -1.98 + ], + [ + 2.404, + -2.298 + ], + [ + 0.547, + -2.511 + ], + [ + 0.082, + -0.477 + ], + [ + 0, + -0.797 + ], + [ + -0.009, + -0.122 + ], + [ + -0.09, + -0.954 + ], + [ + -0.77, + -2.696 + ], + [ + -4.189, + -5.385 + ], + [ + -7.486, + -4.304 + ], + [ + -9.579, + -1.491 + ], + [ + -2.987, + -0.129 + ], + [ + -0.602, + -0.035 + ], + [ + -1.278, + 0 + ], + [ + -0.169, + 0.005 + ], + [ + -3.481, + 0.546 + ], + [ + -4.049, + 1.347 + ], + [ + -6.01, + 4.45 + ], + [ + -3.397, + 4.433 + ], + [ + -1.64, + 6.947 + ], + [ + -0.167, + 2.53 + ] + ], + "o": [ + [ + 0, + -0.797 + ], + [ + -0.023, + -0.091 + ], + [ + -0.106, + -1.121 + ], + [ + -1.087, + -2.417 + ], + [ + -2.906, + -2.208 + ], + [ + -4.636, + -1.808 + ], + [ + -4.922, + -1.029 + ], + [ + -3.281, + -0.33 + ], + [ + -3.263, + -0.199 + ], + [ + -0.158, + -0.006 + ], + [ + -2.008, + 0 + ], + [ + -0.123, + 0.014 + ], + [ + -2.53, + 0.082 + ], + [ + -3.048, + 0.165 + ], + [ + -3.381, + 0.335 + ], + [ + -4.103, + 0.571 + ], + [ + -4.648, + 1.159 + ], + [ + -3.171, + 1.48 + ], + [ + -1.997, + 1.909 + ], + [ + -0.103, + 0.473 + ], + [ + 0, + 0.797 + ], + [ + 0.02, + 0.121 + ], + [ + 0.072, + 0.955 + ], + [ + 0.261, + 2.763 + ], + [ + 1.783, + 6.242 + ], + [ + 4.941, + 6.352 + ], + [ + 8.015, + 4.608 + ], + [ + 2.94, + 0.458 + ], + [ + 0.602, + 0.026 + ], + [ + 1.278, + 0 + ], + [ + 0.169, + -0.017 + ], + [ + 3.538, + -0.113 + ], + [ + 4.291, + -0.673 + ], + [ + 7.471, + -2.485 + ], + [ + 4.723, + -3.497 + ], + [ + 4.574, + -5.97 + ], + [ + 0.587, + -2.486 + ], + [ + 0.037, + -0.566 + ] + ], + "v": [ + [ + -36.789, + -43.354 + ], + [ + -36.789, + -45.744 + ], + [ + -36.856, + -46.019 + ], + [ + -37.73, + -49.279 + ], + [ + -42.925, + -55.398 + ], + [ + -52.719, + -60.643 + ], + [ + -67.157, + -64.804 + ], + [ + -82.094, + -67.138 + ], + [ + -91.959, + -67.967 + ], + [ + -101.766, + -68.346 + ], + [ + -102.238, + -68.39 + ], + [ + -108.262, + -68.39 + ], + [ + -108.631, + -68.349 + ], + [ + -116.22, + -68.095 + ], + [ + -125.351, + -67.445 + ], + [ + -135.461, + -66.212 + ], + [ + -147.59, + -63.828 + ], + [ + -161, + -59.264 + ], + [ + -169.508, + -53.749 + ], + [ + -173.463, + -47.173 + ], + [ + -173.711, + -45.744 + ], + [ + -173.711, + -43.354 + ], + [ + -173.652, + -42.991 + ], + [ + -173.44, + -40.125 + ], + [ + -171.868, + -31.94 + ], + [ + -162.909, + -14.495 + ], + [ + -144.266, + 1.498 + ], + [ + -117.864, + 10.635 + ], + [ + -108.971, + 11.506 + ], + [ + -107.167, + 11.61 + ], + [ + -103.333, + 11.61 + ], + [ + -102.827, + 11.56 + ], + [ + -92.3, + 10.584 + ], + [ + -79.789, + 7.549 + ], + [ + -59.57, + -2.862 + ], + [ + -47.388, + -14.757 + ], + [ + -38.06, + -34.133 + ], + [ + -36.92, + -41.656 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 1, + "y": 1 + }, + "o": { + "x": 0.333, + "y": 0 + }, + "n": "1_1_0p333_0", + "t": 58, + "s": [ + { + "i": [ + [ + -0.044, + 0.566 + ], + [ + 0, + 0.797 + ], + [ + 0.009, + 0.092 + ], + [ + 0.473, + 1.052 + ], + [ + 2.267, + 1.723 + ], + [ + 3.546, + 1.383 + ], + [ + 4.958, + 1.036 + ], + [ + 5.038, + 0.506 + ], + [ + 3.295, + 0.201 + ], + [ + 3.27, + 0.122 + ], + [ + 0.157, + 0.015 + ], + [ + 2.008, + 0 + ], + [ + 0.123, + -0.004 + ], + [ + 2.525, + -0.137 + ], + [ + 3.032, + -0.3 + ], + [ + 3.359, + -0.467 + ], + [ + 3.986, + -0.994 + ], + [ + 4.243, + -1.98 + ], + [ + 2.404, + -2.298 + ], + [ + 0.547, + -2.511 + ], + [ + 0.082, + -0.477 + ], + [ + 0, + -0.797 + ], + [ + -0.009, + -0.122 + ], + [ + -0.09, + -0.954 + ], + [ + -0.77, + -2.696 + ], + [ + -4.189, + -5.385 + ], + [ + -7.486, + -4.304 + ], + [ + -9.579, + -1.491 + ], + [ + -2.987, + -0.129 + ], + [ + -0.602, + -0.035 + ], + [ + -1.278, + 0 + ], + [ + -0.169, + 0.005 + ], + [ + -3.481, + 0.546 + ], + [ + -4.049, + 1.347 + ], + [ + -6.01, + 4.45 + ], + [ + -3.397, + 4.433 + ], + [ + -1.64, + 6.947 + ], + [ + -0.167, + 2.53 + ] + ], + "o": [ + [ + 0, + -0.797 + ], + [ + -0.023, + -0.091 + ], + [ + -0.106, + -1.121 + ], + [ + -1.087, + -2.417 + ], + [ + -2.906, + -2.208 + ], + [ + -4.636, + -1.808 + ], + [ + -4.922, + -1.029 + ], + [ + -3.281, + -0.33 + ], + [ + -3.263, + -0.199 + ], + [ + -0.158, + -0.006 + ], + [ + -2.008, + 0 + ], + [ + -0.123, + 0.014 + ], + [ + -2.53, + 0.082 + ], + [ + -3.048, + 0.165 + ], + [ + -3.381, + 0.335 + ], + [ + -4.103, + 0.571 + ], + [ + -4.648, + 1.159 + ], + [ + -3.171, + 1.48 + ], + [ + -1.997, + 1.909 + ], + [ + -0.103, + 0.473 + ], + [ + 0, + 0.797 + ], + [ + 0.02, + 0.121 + ], + [ + 0.072, + 0.955 + ], + [ + 0.261, + 2.763 + ], + [ + 1.783, + 6.242 + ], + [ + 4.941, + 6.352 + ], + [ + 8.015, + 4.608 + ], + [ + 2.94, + 0.458 + ], + [ + 0.602, + 0.026 + ], + [ + 1.278, + 0 + ], + [ + 0.169, + -0.017 + ], + [ + 3.538, + -0.113 + ], + [ + 4.291, + -0.673 + ], + [ + 7.471, + -2.485 + ], + [ + 4.723, + -3.497 + ], + [ + 4.574, + -5.97 + ], + [ + 0.587, + -2.486 + ], + [ + 0.037, + -0.566 + ] + ], + "v": [ + [ + -36.789, + -43.354 + ], + [ + -36.789, + -45.744 + ], + [ + -36.856, + -46.019 + ], + [ + -37.73, + -49.279 + ], + [ + -42.925, + -55.398 + ], + [ + -52.719, + -60.643 + ], + [ + -67.157, + -64.804 + ], + [ + -82.094, + -67.138 + ], + [ + -91.959, + -67.967 + ], + [ + -101.766, + -68.346 + ], + [ + -102.238, + -68.39 + ], + [ + -108.262, + -68.39 + ], + [ + -108.631, + -68.349 + ], + [ + -116.22, + -68.095 + ], + [ + -125.351, + -67.445 + ], + [ + -135.461, + -66.212 + ], + [ + -147.59, + -63.828 + ], + [ + -161, + -59.264 + ], + [ + -169.508, + -53.749 + ], + [ + -173.463, + -47.173 + ], + [ + -173.711, + -45.744 + ], + [ + -173.711, + -43.354 + ], + [ + -173.652, + -42.991 + ], + [ + -173.44, + -40.125 + ], + [ + -171.868, + -31.94 + ], + [ + -162.909, + -14.495 + ], + [ + -144.266, + 1.498 + ], + [ + -117.864, + 10.635 + ], + [ + -108.971, + 11.506 + ], + [ + -107.167, + 11.61 + ], + [ + -103.333, + 11.61 + ], + [ + -102.827, + 11.56 + ], + [ + -92.3, + 10.584 + ], + [ + -79.789, + 7.549 + ], + [ + -59.57, + -2.862 + ], + [ + -47.388, + -14.757 + ], + [ + -38.06, + -34.133 + ], + [ + -36.92, + -41.656 + ] + ], + "c": true + } + ], + "e": [ + { + "i": [ + [ + -0.029, + 0.566 + ], + [ + 0, + 0.797 + ], + [ + 0.006, + 0.092 + ], + [ + 0.309, + 1.052 + ], + [ + 1.481, + 1.723 + ], + [ + 2.317, + 1.383 + ], + [ + 3.239, + 1.036 + ], + [ + 3.292, + 0.506 + ], + [ + 2.153, + 0.201 + ], + [ + 2.136, + 0.122 + ], + [ + 0.103, + 0.015 + ], + [ + 1.312, + 0 + ], + [ + 0.081, + -0.004 + ], + [ + 1.65, + -0.137 + ], + [ + 1.981, + -0.3 + ], + [ + 2.195, + -0.467 + ], + [ + 2.604, + -0.994 + ], + [ + 2.772, + -1.98 + ], + [ + 1.571, + -2.298 + ], + [ + 0.357, + -2.511 + ], + [ + 0.054, + -0.477 + ], + [ + 0, + -0.797 + ], + [ + -0.006, + -0.122 + ], + [ + -0.059, + -0.954 + ], + [ + -0.503, + -2.696 + ], + [ + -2.737, + -5.385 + ], + [ + -4.891, + -4.304 + ], + [ + -6.259, + -1.491 + ], + [ + -1.952, + -0.129 + ], + [ + -0.393, + -0.035 + ], + [ + -0.835, + 0 + ], + [ + -0.111, + 0.005 + ], + [ + -2.274, + 0.546 + ], + [ + -2.646, + 1.347 + ], + [ + -3.927, + 4.45 + ], + [ + -2.219, + 4.434 + ], + [ + -1.071, + 6.947 + ], + [ + -0.109, + 2.53 + ] + ], + "o": [ + [ + 0, + -0.797 + ], + [ + -0.015, + -0.091 + ], + [ + -0.069, + -1.121 + ], + [ + -0.71, + -2.417 + ], + [ + -1.898, + -2.208 + ], + [ + -3.029, + -1.807 + ], + [ + -3.216, + -1.029 + ], + [ + -2.144, + -0.33 + ], + [ + -2.132, + -0.199 + ], + [ + -0.103, + -0.006 + ], + [ + -1.312, + 0 + ], + [ + -0.08, + 0.014 + ], + [ + -1.653, + 0.082 + ], + [ + -1.992, + 0.166 + ], + [ + -2.209, + 0.335 + ], + [ + -2.681, + 0.571 + ], + [ + -3.037, + 1.159 + ], + [ + -2.072, + 1.48 + ], + [ + -1.305, + 1.909 + ], + [ + -0.067, + 0.473 + ], + [ + 0, + 0.797 + ], + [ + 0.013, + 0.121 + ], + [ + 0.047, + 0.955 + ], + [ + 0.171, + 2.763 + ], + [ + 1.165, + 6.242 + ], + [ + 3.229, + 6.352 + ], + [ + 5.237, + 4.608 + ], + [ + 1.921, + 0.458 + ], + [ + 0.393, + 0.026 + ], + [ + 0.835, + 0 + ], + [ + 0.11, + -0.017 + ], + [ + 2.311, + -0.113 + ], + [ + 2.803, + -0.672 + ], + [ + 4.881, + -2.485 + ], + [ + 3.086, + -3.497 + ], + [ + 2.989, + -5.97 + ], + [ + 0.383, + -2.486 + ], + [ + 0.024, + -0.566 + ] + ], + "v": [ + [ + -60.519, + -43.354 + ], + [ + -60.519, + -45.744 + ], + [ + -60.563, + -46.019 + ], + [ + -61.134, + -49.279 + ], + [ + -64.529, + -55.398 + ], + [ + -70.927, + -60.643 + ], + [ + -80.361, + -64.804 + ], + [ + -90.121, + -67.138 + ], + [ + -96.566, + -67.967 + ], + [ + -102.974, + -68.346 + ], + [ + -103.282, + -68.39 + ], + [ + -107.218, + -68.39 + ], + [ + -107.459, + -68.349 + ], + [ + -112.417, + -68.095 + ], + [ + -118.384, + -67.445 + ], + [ + -124.989, + -66.212 + ], + [ + -132.914, + -63.828 + ], + [ + -141.676, + -59.264 + ], + [ + -147.235, + -53.749 + ], + [ + -149.818, + -47.173 + ], + [ + -149.981, + -45.744 + ], + [ + -149.981, + -43.354 + ], + [ + -149.942, + -42.992 + ], + [ + -149.804, + -40.125 + ], + [ + -148.776, + -31.94 + ], + [ + -142.922, + -14.495 + ], + [ + -130.742, + 1.498 + ], + [ + -113.492, + 10.635 + ], + [ + -107.682, + 11.506 + ], + [ + -106.502, + 11.61 + ], + [ + -103.997, + 11.61 + ], + [ + -103.667, + 11.56 + ], + [ + -96.789, + 10.584 + ], + [ + -88.614, + 7.549 + ], + [ + -75.404, + -2.862 + ], + [ + -67.444, + -14.757 + ], + [ + -61.35, + -34.133 + ], + [ + -60.605, + -41.656 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 1, + "y": 1 + }, + "o": { + "x": 0, + "y": 0 + }, + "n": "1_1_0_0", + "t": 65, + "s": [ + { + "i": [ + [ + -0.029, + 0.566 + ], + [ + 0, + 0.797 + ], + [ + 0.006, + 0.092 + ], + [ + 0.309, + 1.052 + ], + [ + 1.481, + 1.723 + ], + [ + 2.317, + 1.383 + ], + [ + 3.239, + 1.036 + ], + [ + 3.292, + 0.506 + ], + [ + 2.153, + 0.201 + ], + [ + 2.136, + 0.122 + ], + [ + 0.103, + 0.015 + ], + [ + 1.312, + 0 + ], + [ + 0.081, + -0.004 + ], + [ + 1.65, + -0.137 + ], + [ + 1.981, + -0.3 + ], + [ + 2.195, + -0.467 + ], + [ + 2.604, + -0.994 + ], + [ + 2.772, + -1.98 + ], + [ + 1.571, + -2.298 + ], + [ + 0.357, + -2.511 + ], + [ + 0.054, + -0.477 + ], + [ + 0, + -0.797 + ], + [ + -0.006, + -0.122 + ], + [ + -0.059, + -0.954 + ], + [ + -0.503, + -2.696 + ], + [ + -2.737, + -5.385 + ], + [ + -4.891, + -4.304 + ], + [ + -6.259, + -1.491 + ], + [ + -1.952, + -0.129 + ], + [ + -0.393, + -0.035 + ], + [ + -0.835, + 0 + ], + [ + -0.111, + 0.005 + ], + [ + -2.274, + 0.546 + ], + [ + -2.646, + 1.347 + ], + [ + -3.927, + 4.45 + ], + [ + -2.219, + 4.434 + ], + [ + -1.071, + 6.947 + ], + [ + -0.109, + 2.53 + ] + ], + "o": [ + [ + 0, + -0.797 + ], + [ + -0.015, + -0.091 + ], + [ + -0.069, + -1.121 + ], + [ + -0.71, + -2.417 + ], + [ + -1.898, + -2.208 + ], + [ + -3.029, + -1.807 + ], + [ + -3.216, + -1.029 + ], + [ + -2.144, + -0.33 + ], + [ + -2.132, + -0.199 + ], + [ + -0.103, + -0.006 + ], + [ + -1.312, + 0 + ], + [ + -0.08, + 0.014 + ], + [ + -1.653, + 0.082 + ], + [ + -1.992, + 0.166 + ], + [ + -2.209, + 0.335 + ], + [ + -2.681, + 0.571 + ], + [ + -3.037, + 1.159 + ], + [ + -2.072, + 1.48 + ], + [ + -1.305, + 1.909 + ], + [ + -0.067, + 0.473 + ], + [ + 0, + 0.797 + ], + [ + 0.013, + 0.121 + ], + [ + 0.047, + 0.955 + ], + [ + 0.171, + 2.763 + ], + [ + 1.165, + 6.242 + ], + [ + 3.229, + 6.352 + ], + [ + 5.237, + 4.608 + ], + [ + 1.921, + 0.458 + ], + [ + 0.393, + 0.026 + ], + [ + 0.835, + 0 + ], + [ + 0.11, + -0.017 + ], + [ + 2.311, + -0.113 + ], + [ + 2.803, + -0.672 + ], + [ + 4.881, + -2.485 + ], + [ + 3.086, + -3.497 + ], + [ + 2.989, + -5.97 + ], + [ + 0.383, + -2.486 + ], + [ + 0.024, + -0.566 + ] + ], + "v": [ + [ + -60.519, + -43.354 + ], + [ + -60.519, + -45.744 + ], + [ + -60.563, + -46.019 + ], + [ + -61.134, + -49.279 + ], + [ + -64.529, + -55.398 + ], + [ + -70.927, + -60.643 + ], + [ + -80.361, + -64.804 + ], + [ + -90.121, + -67.138 + ], + [ + -96.566, + -67.967 + ], + [ + -102.974, + -68.346 + ], + [ + -103.282, + -68.39 + ], + [ + -107.218, + -68.39 + ], + [ + -107.459, + -68.349 + ], + [ + -112.417, + -68.095 + ], + [ + -118.384, + -67.445 + ], + [ + -124.989, + -66.212 + ], + [ + -132.914, + -63.828 + ], + [ + -141.676, + -59.264 + ], + [ + -147.235, + -53.749 + ], + [ + -149.818, + -47.173 + ], + [ + -149.981, + -45.744 + ], + [ + -149.981, + -43.354 + ], + [ + -149.942, + -42.992 + ], + [ + -149.804, + -40.125 + ], + [ + -148.776, + -31.94 + ], + [ + -142.922, + -14.495 + ], + [ + -130.742, + 1.498 + ], + [ + -113.492, + 10.635 + ], + [ + -107.682, + 11.506 + ], + [ + -106.502, + 11.61 + ], + [ + -103.997, + 11.61 + ], + [ + -103.667, + 11.56 + ], + [ + -96.789, + 10.584 + ], + [ + -88.614, + 7.549 + ], + [ + -75.404, + -2.862 + ], + [ + -67.444, + -14.757 + ], + [ + -61.35, + -34.133 + ], + [ + -60.605, + -41.656 + ] + ], + "c": true + } + ], + "e": [ + { + "i": [ + [ + -0.029, + 0.566 + ], + [ + 0, + 0.797 + ], + [ + 0.006, + 0.092 + ], + [ + 0.309, + 1.052 + ], + [ + 1.481, + 1.723 + ], + [ + 2.317, + 1.383 + ], + [ + 3.239, + 1.036 + ], + [ + 3.292, + 0.506 + ], + [ + 2.153, + 0.201 + ], + [ + 2.136, + 0.122 + ], + [ + 0.103, + 0.015 + ], + [ + 1.312, + 0 + ], + [ + 0.081, + -0.004 + ], + [ + 1.65, + -0.137 + ], + [ + 1.981, + -0.3 + ], + [ + 2.195, + -0.467 + ], + [ + 2.604, + -0.994 + ], + [ + 2.772, + -1.98 + ], + [ + 1.571, + -2.298 + ], + [ + 0.357, + -2.511 + ], + [ + 0.054, + -0.477 + ], + [ + 0, + -0.797 + ], + [ + -0.006, + -0.122 + ], + [ + -0.059, + -0.954 + ], + [ + -0.503, + -2.696 + ], + [ + -2.737, + -5.385 + ], + [ + -4.891, + -4.304 + ], + [ + -6.259, + -1.491 + ], + [ + -1.952, + -0.129 + ], + [ + -0.393, + -0.035 + ], + [ + -0.835, + 0 + ], + [ + -0.111, + 0.005 + ], + [ + -2.274, + 0.546 + ], + [ + -2.646, + 1.347 + ], + [ + -3.927, + 4.45 + ], + [ + -2.219, + 4.434 + ], + [ + -1.071, + 6.947 + ], + [ + -0.109, + 2.53 + ] + ], + "o": [ + [ + 0, + -0.797 + ], + [ + -0.015, + -0.091 + ], + [ + -0.069, + -1.121 + ], + [ + -0.71, + -2.417 + ], + [ + -1.898, + -2.208 + ], + [ + -3.029, + -1.807 + ], + [ + -3.216, + -1.029 + ], + [ + -2.144, + -0.33 + ], + [ + -2.132, + -0.199 + ], + [ + -0.103, + -0.006 + ], + [ + -1.312, + 0 + ], + [ + -0.08, + 0.014 + ], + [ + -1.653, + 0.082 + ], + [ + -1.992, + 0.166 + ], + [ + -2.209, + 0.335 + ], + [ + -2.681, + 0.571 + ], + [ + -3.037, + 1.159 + ], + [ + -2.072, + 1.48 + ], + [ + -1.305, + 1.909 + ], + [ + -0.067, + 0.473 + ], + [ + 0, + 0.797 + ], + [ + 0.013, + 0.121 + ], + [ + 0.047, + 0.955 + ], + [ + 0.171, + 2.763 + ], + [ + 1.165, + 6.242 + ], + [ + 3.229, + 6.352 + ], + [ + 5.237, + 4.608 + ], + [ + 1.921, + 0.458 + ], + [ + 0.393, + 0.026 + ], + [ + 0.835, + 0 + ], + [ + 0.11, + -0.017 + ], + [ + 2.311, + -0.113 + ], + [ + 2.803, + -0.672 + ], + [ + 4.881, + -2.485 + ], + [ + 3.086, + -3.497 + ], + [ + 2.989, + -5.97 + ], + [ + 0.383, + -2.486 + ], + [ + 0.024, + -0.566 + ] + ], + "v": [ + [ + -60.519, + -43.354 + ], + [ + -60.519, + -45.744 + ], + [ + -60.563, + -46.019 + ], + [ + -61.134, + -49.279 + ], + [ + -64.529, + -55.398 + ], + [ + -70.927, + -60.643 + ], + [ + -80.361, + -64.804 + ], + [ + -90.121, + -67.138 + ], + [ + -96.566, + -67.967 + ], + [ + -102.974, + -68.346 + ], + [ + -103.282, + -68.39 + ], + [ + -107.218, + -68.39 + ], + [ + -107.459, + -68.349 + ], + [ + -112.417, + -68.095 + ], + [ + -118.384, + -67.445 + ], + [ + -124.989, + -66.212 + ], + [ + -132.914, + -63.828 + ], + [ + -141.676, + -59.264 + ], + [ + -147.235, + -53.749 + ], + [ + -149.818, + -47.173 + ], + [ + -149.981, + -45.744 + ], + [ + -149.981, + -43.354 + ], + [ + -149.942, + -42.992 + ], + [ + -149.804, + -40.125 + ], + [ + -148.776, + -31.94 + ], + [ + -142.922, + -14.495 + ], + [ + -130.742, + 1.498 + ], + [ + -113.492, + 10.635 + ], + [ + -107.682, + 11.506 + ], + [ + -106.502, + 11.61 + ], + [ + -103.997, + 11.61 + ], + [ + -103.667, + 11.56 + ], + [ + -96.789, + 10.584 + ], + [ + -88.614, + 7.549 + ], + [ + -75.404, + -2.862 + ], + [ + -67.444, + -14.757 + ], + [ + -61.35, + -34.133 + ], + [ + -60.605, + -41.656 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0, + "y": 1 + }, + "o": { + "x": 0, + "y": 0 + }, + "n": "0_1_0_0", + "t": 68.25, + "s": [ + { + "i": [ + [ + -0.029, + 0.566 + ], + [ + 0, + 0.797 + ], + [ + 0.006, + 0.092 + ], + [ + 0.309, + 1.052 + ], + [ + 1.481, + 1.723 + ], + [ + 2.317, + 1.383 + ], + [ + 3.239, + 1.036 + ], + [ + 3.292, + 0.506 + ], + [ + 2.153, + 0.201 + ], + [ + 2.136, + 0.122 + ], + [ + 0.103, + 0.015 + ], + [ + 1.312, + 0 + ], + [ + 0.081, + -0.004 + ], + [ + 1.65, + -0.137 + ], + [ + 1.981, + -0.3 + ], + [ + 2.195, + -0.467 + ], + [ + 2.604, + -0.994 + ], + [ + 2.772, + -1.98 + ], + [ + 1.571, + -2.298 + ], + [ + 0.357, + -2.511 + ], + [ + 0.054, + -0.477 + ], + [ + 0, + -0.797 + ], + [ + -0.006, + -0.122 + ], + [ + -0.059, + -0.954 + ], + [ + -0.503, + -2.696 + ], + [ + -2.737, + -5.385 + ], + [ + -4.891, + -4.304 + ], + [ + -6.259, + -1.491 + ], + [ + -1.952, + -0.129 + ], + [ + -0.393, + -0.035 + ], + [ + -0.835, + 0 + ], + [ + -0.111, + 0.005 + ], + [ + -2.274, + 0.546 + ], + [ + -2.646, + 1.347 + ], + [ + -3.927, + 4.45 + ], + [ + -2.219, + 4.434 + ], + [ + -1.071, + 6.947 + ], + [ + -0.109, + 2.53 + ] + ], + "o": [ + [ + 0, + -0.797 + ], + [ + -0.015, + -0.091 + ], + [ + -0.069, + -1.121 + ], + [ + -0.71, + -2.417 + ], + [ + -1.898, + -2.208 + ], + [ + -3.029, + -1.807 + ], + [ + -3.216, + -1.029 + ], + [ + -2.144, + -0.33 + ], + [ + -2.132, + -0.199 + ], + [ + -0.103, + -0.006 + ], + [ + -1.312, + 0 + ], + [ + -0.08, + 0.014 + ], + [ + -1.653, + 0.082 + ], + [ + -1.992, + 0.166 + ], + [ + -2.209, + 0.335 + ], + [ + -2.681, + 0.571 + ], + [ + -3.037, + 1.159 + ], + [ + -2.072, + 1.48 + ], + [ + -1.305, + 1.909 + ], + [ + -0.067, + 0.473 + ], + [ + 0, + 0.797 + ], + [ + 0.013, + 0.121 + ], + [ + 0.047, + 0.955 + ], + [ + 0.171, + 2.763 + ], + [ + 1.165, + 6.242 + ], + [ + 3.229, + 6.352 + ], + [ + 5.237, + 4.608 + ], + [ + 1.921, + 0.458 + ], + [ + 0.393, + 0.026 + ], + [ + 0.835, + 0 + ], + [ + 0.11, + -0.017 + ], + [ + 2.311, + -0.113 + ], + [ + 2.803, + -0.672 + ], + [ + 4.881, + -2.485 + ], + [ + 3.086, + -3.497 + ], + [ + 2.989, + -5.97 + ], + [ + 0.383, + -2.486 + ], + [ + 0.024, + -0.566 + ] + ], + "v": [ + [ + -60.519, + -43.354 + ], + [ + -60.519, + -45.744 + ], + [ + -60.563, + -46.019 + ], + [ + -61.134, + -49.279 + ], + [ + -64.529, + -55.398 + ], + [ + -70.927, + -60.643 + ], + [ + -80.361, + -64.804 + ], + [ + -90.121, + -67.138 + ], + [ + -96.566, + -67.967 + ], + [ + -102.974, + -68.346 + ], + [ + -103.282, + -68.39 + ], + [ + -107.218, + -68.39 + ], + [ + -107.459, + -68.349 + ], + [ + -112.417, + -68.095 + ], + [ + -118.384, + -67.445 + ], + [ + -124.989, + -66.212 + ], + [ + -132.914, + -63.828 + ], + [ + -141.676, + -59.264 + ], + [ + -147.235, + -53.749 + ], + [ + -149.818, + -47.173 + ], + [ + -149.981, + -45.744 + ], + [ + -149.981, + -43.354 + ], + [ + -149.942, + -42.992 + ], + [ + -149.804, + -40.125 + ], + [ + -148.776, + -31.94 + ], + [ + -142.922, + -14.495 + ], + [ + -130.742, + 1.498 + ], + [ + -113.492, + 10.635 + ], + [ + -107.682, + 11.506 + ], + [ + -106.502, + 11.61 + ], + [ + -103.997, + 11.61 + ], + [ + -103.667, + 11.56 + ], + [ + -96.789, + 10.584 + ], + [ + -88.614, + 7.549 + ], + [ + -75.404, + -2.862 + ], + [ + -67.444, + -14.757 + ], + [ + -61.35, + -34.133 + ], + [ + -60.605, + -41.656 + ] + ], + "c": true + } + ], + "e": [ + { + "i": [ + [ + -0.044, + 0.566 + ], + [ + 0, + 0.797 + ], + [ + 0.009, + 0.092 + ], + [ + 0.473, + 1.052 + ], + [ + 2.267, + 1.723 + ], + [ + 3.546, + 1.383 + ], + [ + 4.958, + 1.036 + ], + [ + 5.038, + 0.506 + ], + [ + 3.295, + 0.201 + ], + [ + 3.27, + 0.122 + ], + [ + 0.157, + 0.015 + ], + [ + 2.008, + 0 + ], + [ + 0.123, + -0.004 + ], + [ + 2.525, + -0.137 + ], + [ + 3.032, + -0.3 + ], + [ + 3.359, + -0.467 + ], + [ + 3.986, + -0.994 + ], + [ + 4.243, + -1.98 + ], + [ + 2.404, + -2.298 + ], + [ + 0.547, + -2.511 + ], + [ + 0.082, + -0.477 + ], + [ + 0, + -0.797 + ], + [ + -0.009, + -0.122 + ], + [ + -0.09, + -0.954 + ], + [ + -0.77, + -2.696 + ], + [ + -4.189, + -5.385 + ], + [ + -7.486, + -4.304 + ], + [ + -9.579, + -1.491 + ], + [ + -2.987, + -0.129 + ], + [ + -0.602, + -0.035 + ], + [ + -1.278, + 0 + ], + [ + -0.169, + 0.005 + ], + [ + -3.481, + 0.546 + ], + [ + -4.049, + 1.347 + ], + [ + -6.01, + 4.45 + ], + [ + -3.397, + 4.433 + ], + [ + -1.64, + 6.947 + ], + [ + -0.167, + 2.53 + ] + ], + "o": [ + [ + 0, + -0.797 + ], + [ + -0.023, + -0.091 + ], + [ + -0.106, + -1.121 + ], + [ + -1.087, + -2.417 + ], + [ + -2.906, + -2.208 + ], + [ + -4.636, + -1.808 + ], + [ + -4.922, + -1.029 + ], + [ + -3.281, + -0.33 + ], + [ + -3.263, + -0.199 + ], + [ + -0.158, + -0.006 + ], + [ + -2.008, + 0 + ], + [ + -0.123, + 0.014 + ], + [ + -2.53, + 0.082 + ], + [ + -3.048, + 0.165 + ], + [ + -3.381, + 0.335 + ], + [ + -4.103, + 0.571 + ], + [ + -4.648, + 1.159 + ], + [ + -3.171, + 1.48 + ], + [ + -1.997, + 1.909 + ], + [ + -0.103, + 0.473 + ], + [ + 0, + 0.797 + ], + [ + 0.02, + 0.121 + ], + [ + 0.072, + 0.955 + ], + [ + 0.261, + 2.763 + ], + [ + 1.783, + 6.242 + ], + [ + 4.941, + 6.352 + ], + [ + 8.015, + 4.608 + ], + [ + 2.94, + 0.458 + ], + [ + 0.602, + 0.026 + ], + [ + 1.278, + 0 + ], + [ + 0.169, + -0.017 + ], + [ + 3.538, + -0.113 + ], + [ + 4.291, + -0.673 + ], + [ + 7.471, + -2.485 + ], + [ + 4.723, + -3.497 + ], + [ + 4.574, + -5.97 + ], + [ + 0.587, + -2.486 + ], + [ + 0.037, + -0.566 + ] + ], + "v": [ + [ + -36.789, + -43.354 + ], + [ + -36.789, + -45.744 + ], + [ + -36.856, + -46.019 + ], + [ + -37.73, + -49.279 + ], + [ + -42.925, + -55.398 + ], + [ + -52.719, + -60.643 + ], + [ + -67.157, + -64.804 + ], + [ + -82.094, + -67.138 + ], + [ + -91.959, + -67.967 + ], + [ + -101.766, + -68.346 + ], + [ + -102.238, + -68.39 + ], + [ + -108.262, + -68.39 + ], + [ + -108.631, + -68.349 + ], + [ + -116.22, + -68.095 + ], + [ + -125.351, + -67.445 + ], + [ + -135.461, + -66.212 + ], + [ + -147.59, + -63.828 + ], + [ + -161, + -59.264 + ], + [ + -169.508, + -53.749 + ], + [ + -173.463, + -47.173 + ], + [ + -173.711, + -45.744 + ], + [ + -173.711, + -43.354 + ], + [ + -173.652, + -42.991 + ], + [ + -173.44, + -40.125 + ], + [ + -171.868, + -31.94 + ], + [ + -162.909, + -14.495 + ], + [ + -144.266, + 1.498 + ], + [ + -117.864, + 10.635 + ], + [ + -108.971, + 11.506 + ], + [ + -107.167, + 11.61 + ], + [ + -103.333, + 11.61 + ], + [ + -102.827, + 11.56 + ], + [ + -92.3, + 10.584 + ], + [ + -79.789, + 7.549 + ], + [ + -59.57, + -2.862 + ], + [ + -47.388, + -14.757 + ], + [ + -38.06, + -34.133 + ], + [ + -36.92, + -41.656 + ] + ], + "c": true + } + ] + }, + { + "i": { + "x": 0.308, + "y": 1 + }, + "o": { + "x": 0.703, + "y": 0 + }, + "n": "0p308_1_0p703_0", + "t": 71.144, + "s": [ + { + "i": [ + [ + -0.044, + 0.566 + ], + [ + 0, + 0.797 + ], + [ + 0.009, + 0.092 + ], + [ + 0.473, + 1.052 + ], + [ + 2.267, + 1.723 + ], + [ + 3.546, + 1.383 + ], + [ + 4.958, + 1.036 + ], + [ + 5.038, + 0.506 + ], + [ + 3.295, + 0.201 + ], + [ + 3.27, + 0.122 + ], + [ + 0.157, + 0.015 + ], + [ + 2.008, + 0 + ], + [ + 0.123, + -0.004 + ], + [ + 2.525, + -0.137 + ], + [ + 3.032, + -0.3 + ], + [ + 3.359, + -0.467 + ], + [ + 3.986, + -0.994 + ], + [ + 4.243, + -1.98 + ], + [ + 2.404, + -2.298 + ], + [ + 0.547, + -2.511 + ], + [ + 0.082, + -0.477 + ], + [ + 0, + -0.797 + ], + [ + -0.009, + -0.122 + ], + [ + -0.09, + -0.954 + ], + [ + -0.77, + -2.696 + ], + [ + -4.189, + -5.385 + ], + [ + -7.486, + -4.304 + ], + [ + -9.579, + -1.491 + ], + [ + -2.987, + -0.129 + ], + [ + -0.602, + -0.035 + ], + [ + -1.278, + 0 + ], + [ + -0.169, + 0.005 + ], + [ + -3.481, + 0.546 + ], + [ + -4.049, + 1.347 + ], + [ + -6.01, + 4.45 + ], + [ + -3.397, + 4.433 + ], + [ + -1.64, + 6.947 + ], + [ + -0.167, + 2.53 + ] + ], + "o": [ + [ + 0, + -0.797 + ], + [ + -0.023, + -0.091 + ], + [ + -0.106, + -1.121 + ], + [ + -1.087, + -2.417 + ], + [ + -2.906, + -2.208 + ], + [ + -4.636, + -1.808 + ], + [ + -4.922, + -1.029 + ], + [ + -3.281, + -0.33 + ], + [ + -3.263, + -0.199 + ], + [ + -0.158, + -0.006 + ], + [ + -2.008, + 0 + ], + [ + -0.123, + 0.014 + ], + [ + -2.53, + 0.082 + ], + [ + -3.048, + 0.165 + ], + [ + -3.381, + 0.335 + ], + [ + -4.103, + 0.571 + ], + [ + -4.648, + 1.159 + ], + [ + -3.171, + 1.48 + ], + [ + -1.997, + 1.909 + ], + [ + -0.103, + 0.473 + ], + [ + 0, + 0.797 + ], + [ + 0.02, + 0.121 + ], + [ + 0.072, + 0.955 + ], + [ + 0.261, + 2.763 + ], + [ + 1.783, + 6.242 + ], + [ + 4.941, + 6.352 + ], + [ + 8.015, + 4.608 + ], + [ + 2.94, + 0.458 + ], + [ + 0.602, + 0.026 + ], + [ + 1.278, + 0 + ], + [ + 0.169, + -0.017 + ], + [ + 3.538, + -0.113 + ], + [ + 4.291, + -0.673 + ], + [ + 7.471, + -2.485 + ], + [ + 4.723, + -3.497 + ], + [ + 4.574, + -5.97 + ], + [ + 0.587, + -2.486 + ], + [ + 0.037, + -0.566 + ] + ], + "v": [ + [ + -36.789, + -43.354 + ], + [ + -36.789, + -45.744 + ], + [ + -36.856, + -46.019 + ], + [ + -37.73, + -49.279 + ], + [ + -42.925, + -55.398 + ], + [ + -52.719, + -60.643 + ], + [ + -67.157, + -64.804 + ], + [ + -82.094, + -67.138 + ], + [ + -91.959, + -67.967 + ], + [ + -101.766, + -68.346 + ], + [ + -102.238, + -68.39 + ], + [ + -108.262, + -68.39 + ], + [ + -108.631, + -68.349 + ], + [ + -116.22, + -68.095 + ], + [ + -125.351, + -67.445 + ], + [ + -135.461, + -66.212 + ], + [ + -147.59, + -63.828 + ], + [ + -161, + -59.264 + ], + [ + -169.508, + -53.749 + ], + [ + -173.463, + -47.173 + ], + [ + -173.711, + -45.744 + ], + [ + -173.711, + -43.354 + ], + [ + -173.652, + -42.991 + ], + [ + -173.44, + -40.125 + ], + [ + -171.868, + -31.94 + ], + [ + -162.909, + -14.495 + ], + [ + -144.266, + 1.498 + ], + [ + -117.864, + 10.635 + ], + [ + -108.971, + 11.506 + ], + [ + -107.167, + 11.61 + ], + [ + -103.333, + 11.61 + ], + [ + -102.827, + 11.56 + ], + [ + -92.3, + 10.584 + ], + [ + -79.789, + 7.549 + ], + [ + -59.57, + -2.862 + ], + [ + -47.388, + -14.757 + ], + [ + -38.06, + -34.133 + ], + [ + -36.92, + -41.656 + ] + ], + "c": true + } + ], + "e": [ + { + "i": [ + [ + -0.081, + 0.416 + ], + [ + 0, + 0.614 + ], + [ + 0.017, + 0.114 + ], + [ + 4.432, + 1.114 + ], + [ + 0.455, + 0.099 + ], + [ + 0.63, + 0 + ], + [ + 0.118, + -0.012 + ], + [ + 1.098, + -0.552 + ], + [ + 5.005, + -1.792 + ], + [ + 17.596, + -1.761 + ], + [ + 5.449, + -0.216 + ], + [ + 10.022, + 0.979 + ], + [ + 5.427, + 0.928 + ], + [ + 9.789, + 3.142 + ], + [ + 6.284, + 3.051 + ], + [ + 0.79, + 0.255 + ], + [ + 0.915, + 0.206 + ], + [ + 0.63, + 0 + ], + [ + 0.098, + -0.016 + ], + [ + 1.538, + -3.764 + ], + [ + 0.246, + -0.951 + ], + [ + 0, + -0.58 + ], + [ + -0.022, + -0.149 + ], + [ + -2.832, + -1.687 + ], + [ + -1.661, + -0.753 + ], + [ + -10.509, + -2.813 + ], + [ + -8.427, + -1.306 + ], + [ + -5.769, + -0.425 + ], + [ + -5.447, + -0.289 + ], + [ + -0.241, + -0.027 + ], + [ + -3.43, + 0 + ], + [ + -0.312, + 0.015 + ], + [ + -4.293, + 0.255 + ], + [ + -5.555, + 0.778 + ], + [ + -6.003, + 1.316 + ], + [ + -9.441, + 3.427 + ], + [ + -5.316, + 2.752 + ], + [ + -0.737, + 3.161 + ] + ], + "o": [ + [ + 0, + -0.614 + ], + [ + -0.035, + -0.111 + ], + [ + -0.645, + -4.395 + ], + [ + -0.451, + -0.113 + ], + [ + -0.63, + 0 + ], + [ + -0.116, + 0.032 + ], + [ + -1.23, + 0.122 + ], + [ + -4.743, + 2.385 + ], + [ + -16.622, + 5.95 + ], + [ + -5.424, + 0.543 + ], + [ + -10.064, + 0.399 + ], + [ + -5.48, + -0.536 + ], + [ + -10.152, + -1.736 + ], + [ + -6.661, + -2.138 + ], + [ + -0.75, + -0.364 + ], + [ + -0.888, + -0.287 + ], + [ + -0.63, + 0 + ], + [ + -0.095, + 0.034 + ], + [ + -4.089, + 0.654 + ], + [ + -0.369, + 0.903 + ], + [ + 0, + 0.58 + ], + [ + 0.038, + 0.147 + ], + [ + 0.472, + 3.212 + ], + [ + 1.56, + 0.929 + ], + [ + 9.893, + 4.481 + ], + [ + 8.226, + 2.203 + ], + [ + 5.716, + 0.886 + ], + [ + 5.438, + 0.4 + ], + [ + 0.242, + 0.013 + ], + [ + 3.43, + 0 + ], + [ + 0.311, + -0.029 + ], + [ + 4.296, + -0.209 + ], + [ + 5.604, + -0.333 + ], + [ + 6.089, + -0.852 + ], + [ + 9.827, + -2.154 + ], + [ + 5.635, + -2.045 + ], + [ + 2.928, + -1.516 + ], + [ + 0.096, + -0.413 + ] + ], + "v": [ + [ + -0.25, + -37.45 + ], + [ + -0.25, + -39.292 + ], + [ + -0.352, + -39.627 + ], + [ + -8.441, + -48.41 + ], + [ + -9.805, + -48.71 + ], + [ + -11.695, + -48.71 + ], + [ + -12.043, + -48.617 + ], + [ + -15.549, + -47.635 + ], + [ + -30.201, + -41.435 + ], + [ + -81.575, + -30.008 + ], + [ + -97.904, + -28.937 + ], + [ + -128.04, + -29.897 + ], + [ + -144.399, + -32.098 + ], + [ + -174.316, + -39.388 + ], + [ + -193.782, + -47.058 + ], + [ + -196.066, + -48.073 + ], + [ + -198.805, + -48.71 + ], + [ + -200.695, + -48.71 + ], + [ + -200.982, + -48.612 + ], + [ + -209.494, + -42.035 + ], + [ + -210.25, + -39.19 + ], + [ + -210.25, + -37.45 + ], + [ + -210.139, + -37.008 + ], + [ + -205.218, + -29.585 + ], + [ + -200.329, + -27.133 + ], + [ + -169.658, + -16.368 + ], + [ + -144.675, + -11.119 + ], + [ + -127.458, + -9.034 + ], + [ + -111.119, + -8.149 + ], + [ + -110.395, + -8.07 + ], + [ + -100.105, + -8.07 + ], + [ + -99.172, + -8.154 + ], + [ + -86.283, + -8.786 + ], + [ + -69.554, + -10.561 + ], + [ + -51.415, + -13.81 + ], + [ + -22.49, + -22.107 + ], + [ + -5.996, + -29.157 + ], + [ + -0.494, + -36.201 + ] + ], + "c": true + } + ] + }, + { + "t": 75 + } + ] + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0, + 0, + 0, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 105.25, + 28.39 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 5, + "ty": 4, + "nm": "nose", + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 503.498, + 680.02, + 0 + ] + }, + "a": { + "k": [ + 42.233, + 29.174, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + -3.557, + 23.651 + ], + [ + -14.41, + -16.492 + ], + [ + 16.97, + -2.701 + ] + ], + "o": [ + [ + 2.439, + -16.225 + ], + [ + 14.41, + 16.492 + ], + [ + -16.971, + 2.7 + ] + ], + "v": [ + [ + -38.426, + -12.003 + ], + [ + 27.573, + -12.432 + ], + [ + -1.301, + 26.225 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0, + 0, + 0, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 42.233, + 29.175 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 6, + "ty": 3, + "nm": "eye_pupil_controller", + "ks": { + "o": { + "k": 0 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + { + "i": { + "x": 0.833, + "y": 0.833 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "n": "0p833_0p833_0p167_0", + "t": 0, + "s": [ + 481, + 519.5, + 0 + ], + "e": [ + 459.5, + 527, + 0 + ], + "to": [ + 0, + 0, + 0 + ], + "ti": [ + -4.03295516967773, + -4.07727336883545, + 0 + ] + }, + { + "i": { + "x": 0.833, + "y": 0.833 + }, + "o": { + "x": 0.167, + "y": 0.167 + }, + "n": "0p833_0p833_0p167_0p167", + "t": 4, + "s": [ + 459.5, + 527, + 0 + ], + "e": [ + 481.875, + 531.25, + 0 + ], + "to": [ + 3.79166674613953, + 3.83333325386047, + 0 + ], + "ti": [ + -11.1676235198975, + 0.20281778275967, + 0 + ] + }, + { + "i": { + "x": 0.833, + "y": 0.833 + }, + "o": { + "x": 0.167, + "y": 0.167 + }, + "n": "0p833_0p833_0p167_0p167", + "t": 13, + "s": [ + 481.875, + 531.25, + 0 + ], + "e": [ + 506.5, + 524, + 0 + ], + "to": [ + 11.9174766540527, + -0.21643604338169, + 0 + ], + "ti": [ + -6.04166650772095, + 4.625, + 0 + ] + }, + { + "i": { + "x": 0.667, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0.167 + }, + "n": "0p667_1_0p167_0p167", + "t": 22, + "s": [ + 506.5, + 524, + 0 + ], + "e": [ + 481, + 519.5, + 0 + ], + "to": [ + 5.39564085006714, + -4.13045644760132, + 0 + ], + "ti": [ + 0, + 0, + 0 + ] + }, + { + "i": { + "x": 0.667, + "y": 0.667 + }, + "o": { + "x": 0.167, + "y": 0.167 + }, + "n": "0p667_0p667_0p167_0p167", + "t": 29, + "s": [ + 481, + 519.5, + 0 + ], + "e": [ + 481, + 519.5, + 0 + ], + "to": [ + 0, + 0, + 0 + ], + "ti": [ + 0, + 0, + 0 + ] + }, + { + "i": { + "x": 0.667, + "y": 0.667 + }, + "o": { + "x": 0.167, + "y": 0.167 + }, + "n": "0p667_0p667_0p167_0p167", + "t": 59, + "s": [ + 481, + 519.5, + 0 + ], + "e": [ + 481, + 519.5, + 0 + ], + "to": [ + 0, + 0, + 0 + ], + "ti": [ + 0, + 0, + 0 + ] + }, + { + "i": { + "x": 0.667, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "n": "0p667_1_0p167_0", + "t": 60.667, + "s": [ + 481, + 519.5, + 0 + ], + "e": [ + 483.5, + 535, + 0 + ], + "to": [ + 0, + 0, + 0 + ], + "ti": [ + 0, + 0, + 0 + ] + }, + { + "i": { + "x": 0.667, + "y": 1 + }, + "o": { + "x": 0.167, + "y": 0 + }, + "n": "0p667_1_0p167_0", + "t": 69, + "s": [ + 483.5, + 535, + 0 + ], + "e": [ + 481, + 519.5, + 0 + ], + "to": [ + 0, + 0, + 0 + ], + "ti": [ + 0, + 0, + 0 + ] + }, + { + "i": { + "x": 0.667, + "y": 0.667 + }, + "o": { + "x": 0.167, + "y": 0.167 + }, + "n": "0p667_0p667_0p167_0p167", + "t": 71.682, + "s": [ + 481, + 519.5, + 0 + ], + "e": [ + 481, + 519.5, + 0 + ], + "to": [ + 0, + 0, + 0 + ], + "ti": [ + 0, + 0, + 0 + ] + }, + { + "t": 74 + } + ] + }, + "a": { + "k": [ + 0, + 0, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 7, + "ty": 4, + "nm": "eye_pupil_left", + "parent": 6, + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + -136.393, + 117.431, + 0 + ] + }, + "a": { + "k": [ + 15.684, + 15.18, + 0 + ] + }, + "s": { + "k": [ + 170, + 170, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + -0.518, + 0.056 + ], + [ + -3.048, + -3.18 + ], + [ + -0.381, + -3.103 + ], + [ + 2.85, + -3.249 + ], + [ + 3.341, + -0.639 + ], + [ + 3.717, + 2.785 + ], + [ + 0.788, + 3.507 + ], + [ + -0.239, + 1.647 + ], + [ + -3.323, + 2.447 + ], + [ + -2.342, + 0.372 + ] + ], + "o": [ + [ + 4.772, + 0.078 + ], + [ + 2.154, + 2.248 + ], + [ + 0.529, + 4.306 + ], + [ + -2.252, + 2.566 + ], + [ + -4.542, + 0.869 + ], + [ + -2.871, + -2.151 + ], + [ + -0.363, + -1.619 + ], + [ + 0.597, + -4.113 + ], + [ + 1.905, + -1.403 + ], + [ + 0.89, + -0.142 + ] + ], + "v": [ + [ + -0.538, + -14.93 + ], + [ + 11.057, + -10.14 + ], + [ + 14.905, + -2.099 + ], + [ + 11.371, + 9.246 + ], + [ + 2.932, + 14.061 + ], + [ + -9.513, + 11.235 + ], + [ + -15.01, + 2.721 + ], + [ + -15.194, + -2.185 + ], + [ + -9.237, + -11.996 + ], + [ + -2.849, + -14.672 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 1, + 1, + 1, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 15.683, + 15.18 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 8, + "ty": 4, + "nm": "eye_pupil_right", + "parent": 6, + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 172.459, + 117.431, + 0 + ] + }, + "a": { + "k": [ + 15.684, + 15.18, + 0 + ] + }, + "s": { + "k": [ + 170, + 170, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + -0.519, + 0.056 + ], + [ + -3.048, + -3.18 + ], + [ + -0.38, + -3.103 + ], + [ + 2.851, + -3.249 + ], + [ + 3.341, + -0.639 + ], + [ + 3.717, + 2.785 + ], + [ + 0.787, + 3.507 + ], + [ + -0.24, + 1.647 + ], + [ + -3.323, + 2.447 + ], + [ + -2.341, + 0.372 + ] + ], + "o": [ + [ + 4.771, + 0.078 + ], + [ + 2.154, + 2.248 + ], + [ + 0.53, + 4.306 + ], + [ + -2.252, + 2.566 + ], + [ + -4.542, + 0.869 + ], + [ + -2.87, + -2.151 + ], + [ + -0.364, + -1.619 + ], + [ + 0.597, + -4.113 + ], + [ + 1.904, + -1.403 + ], + [ + 0.89, + -0.142 + ] + ], + "v": [ + [ + -0.538, + -14.93 + ], + [ + 11.058, + -10.14 + ], + [ + 14.904, + -2.099 + ], + [ + 11.371, + 9.246 + ], + [ + 2.933, + 14.061 + ], + [ + -9.514, + 11.235 + ], + [ + -15.01, + 2.721 + ], + [ + -15.194, + -2.185 + ], + [ + -9.236, + -11.996 + ], + [ + -2.85, + -14.672 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 1, + 1, + 1, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 15.684, + 15.18 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 9, + "ty": 4, + "nm": "eyeball_left", + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 345.573, + 632.847, + 0 + ] + }, + "a": { + "k": [ + 65.824, + 65.826, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 36.218, + 0 + ], + [ + 0, + -36.212 + ], + [ + -36.219, + 0 + ], + [ + 0, + 36.219 + ] + ], + "o": [ + [ + -36.219, + 0 + ], + [ + 0, + 36.219 + ], + [ + 36.218, + 0 + ], + [ + 0, + -36.212 + ] + ], + "v": [ + [ + 0.002, + -65.576 + ], + [ + -65.574, + -0.003 + ], + [ + 0.002, + 65.576 + ], + [ + 65.574, + -0.003 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ind": 1, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + -27.336, + 0 + ], + [ + 0, + -27.335 + ], + [ + 27.334, + 0 + ], + [ + 0, + 27.338 + ] + ], + "o": [ + [ + 27.334, + 0 + ], + [ + 0, + 27.338 + ], + [ + -27.336, + 0 + ], + [ + 0, + -27.335 + ] + ], + "v": [ + [ + 0.002, + -49.576 + ], + [ + 49.574, + -0.003 + ], + [ + 0.002, + 49.576 + ], + [ + -49.574, + -0.003 + ] + ], + "c": true + } + }, + "nm": "Path 2", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "mm", + "mm": 1, + "nm": "Merge Paths 1", + "mn": "ADBE Vector Filter - Merge" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 1, + 1, + 1, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 65.824, + 65.826 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 4, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 31.746, + 0 + ], + [ + 0, + 31.749 + ], + [ + -31.748, + 0 + ], + [ + 0, + -31.745 + ] + ], + "o": [ + [ + -31.748, + 0 + ], + [ + 0, + -31.745 + ], + [ + 31.746, + 0 + ], + [ + 0, + 31.749 + ] + ], + "v": [ + [ + 0.002, + 57.576 + ], + [ + -57.575, + -0.003 + ], + [ + 0.002, + -57.576 + ], + [ + 57.575, + -0.003 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0, + 0, + 0, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 65.824, + 65.826 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 2", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 10, + "ty": 4, + "nm": "eyeball_right", + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 654.426, + 632.847, + 0 + ] + }, + "a": { + "k": [ + 65.824, + 65.826, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 36.218, + 0 + ], + [ + 0, + -36.212 + ], + [ + -36.219, + 0 + ], + [ + 0, + 36.219 + ] + ], + "o": [ + [ + -36.219, + 0 + ], + [ + 0, + 36.219 + ], + [ + 36.218, + 0 + ], + [ + 0, + -36.212 + ] + ], + "v": [ + [ + 0.002, + -65.576 + ], + [ + -65.574, + -0.003 + ], + [ + 0.002, + 65.576 + ], + [ + 65.574, + -0.003 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ind": 1, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + -27.337, + 0 + ], + [ + 0, + -27.335 + ], + [ + 27.334, + 0 + ], + [ + 0, + 27.338 + ] + ], + "o": [ + [ + 27.334, + 0 + ], + [ + 0, + 27.338 + ], + [ + -27.337, + 0 + ], + [ + 0, + -27.335 + ] + ], + "v": [ + [ + 0.002, + -49.576 + ], + [ + 49.574, + -0.003 + ], + [ + 0.002, + 49.576 + ], + [ + -49.574, + -0.003 + ] + ], + "c": true + } + }, + "nm": "Path 2", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "mm", + "mm": 1, + "nm": "Merge Paths 1", + "mn": "ADBE Vector Filter - Merge" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 1, + 1, + 1, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 65.824, + 65.826 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 4, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 31.745, + 0 + ], + [ + 0, + 31.749 + ], + [ + -31.748, + 0 + ], + [ + 0, + -31.745 + ] + ], + "o": [ + [ + -31.748, + 0 + ], + [ + 0, + -31.745 + ], + [ + 31.745, + 0 + ], + [ + 0, + 31.749 + ] + ], + "v": [ + [ + 0.002, + 57.576 + ], + [ + -57.574, + -0.003 + ], + [ + 0.002, + -57.576 + ], + [ + 57.574, + -0.003 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0, + 0, + 0, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 65.824, + 65.826 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 2", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 11, + "ty": 4, + "nm": "cheek_left", + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 268.359, + 718.298, + 0 + ] + }, + "a": { + "k": [ + 37.624, + 37.625, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 0, + -20.645 + ], + [ + 20.64, + 0 + ], + [ + 0, + 20.637 + ], + [ + -20.645, + 0 + ] + ], + "o": [ + [ + 0, + 20.637 + ], + [ + -20.645, + 0 + ], + [ + 0, + -20.645 + ], + [ + 20.64, + 0 + ] + ], + "v": [ + [ + 37.374, + 0 + ], + [ + 0.002, + 37.375 + ], + [ + -37.374, + 0 + ], + [ + 0.002, + -37.375 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 1, + 0.74, + 0.75, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 37.624, + 37.626 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 12, + "ty": 4, + "nm": "cheek_right", + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 731.64, + 718.298, + 0 + ] + }, + "a": { + "k": [ + 37.622, + 37.625, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 0, + -20.645 + ], + [ + 20.645, + 0 + ], + [ + 0, + 20.637 + ], + [ + -20.637, + 0 + ] + ], + "o": [ + [ + 0, + 20.637 + ], + [ + -20.637, + 0 + ], + [ + 0, + -20.645 + ], + [ + 20.645, + 0 + ] + ], + "v": [ + [ + 37.372, + 0 + ], + [ + -0.004, + 37.375 + ], + [ + -37.372, + 0 + ], + [ + -0.004, + -37.375 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 1, + 0.74, + 0.75, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 37.622, + 37.626 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 13, + "ty": 4, + "nm": "face", + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 499.999, + 618.729, + 0 + ] + }, + "a": { + "k": [ + 321.693, + 269.521, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + -16.966, + -150.969 + ], + [ + 183.631, + 0 + ], + [ + -19.111, + 170.049 + ], + [ + -151.907, + 0 + ] + ], + "o": [ + [ + 19.111, + 170.049 + ], + [ + -183.64, + 0 + ], + [ + 16.966, + -150.969 + ], + [ + 151.905, + 0 + ] + ], + "v": [ + [ + 302.333, + 0.828 + ], + [ + 0.003, + 269.271 + ], + [ + -302.332, + 0.828 + ], + [ + 0.003, + -269.271 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0.94, + 0.94, + 0.94, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 321.693, + 269.521 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 14, + "ty": 4, + "nm": "ear_right", + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 763.547, + 347.608, + 0 + ] + }, + "a": { + "k": [ + 121.241, + 208.634, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 0, + 0 + ], + [ + -36.779, + 4.6 + ], + [ + 71.08, + -141.307 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 36.786, + -4.592 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -58.273, + 77.218 + ], + [ + 7.089, + -105.401 + ], + [ + -12.807, + 109.993 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 1, + 0.74, + 0.75, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 88.503, + 211.259 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 0, + 0 + ], + [ + -81.399, + 7.085 + ], + [ + 168.251, + -305.441 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + 81.407, + -7.093 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + -120.991, + 60.422 + ], + [ + -19.402, + -201.291 + ], + [ + -47.26, + 208.384 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0.94, + 0.94, + 0.94, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 121.241, + 208.634 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 2", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 15, + "ty": 4, + "nm": "ear_left", + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 236.452, + 347.608, + 0 + ] + }, + "a": { + "k": [ + 121.236, + 208.634, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 0, + 0 + ], + [ + 36.779, + 4.6 + ], + [ + -71.08, + -141.307 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + -36.779, + -4.592 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 58.275, + 77.218 + ], + [ + -7.091, + -105.401 + ], + [ + 12.805, + 109.993 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 1, + 0.74, + 0.75, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 153.974, + 211.259 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + }, + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 0, + 0 + ], + [ + 81.41, + 7.085 + ], + [ + -168.248, + -305.441 + ] + ], + "o": [ + [ + 0, + 0 + ], + [ + -81.399, + -7.093 + ], + [ + 0, + 0 + ] + ], + "v": [ + [ + 120.986, + 60.422 + ], + [ + 19.392, + -201.291 + ], + [ + 47.262, + 208.384 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0.94, + 0.94, + 0.94, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 121.236, + 208.634 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 2", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 16, + "ty": 4, + "nm": "body", + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 499.697, + 811.329, + 0 + ] + }, + "a": { + "k": [ + 233.907, + 164.921, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 55.823, + 51.688 + ], + [ + 0, + 0 + ], + [ + 24.473, + -75.204 + ], + [ + -85.039, + 0 + ], + [ + -68.954, + 38.859 + ] + ], + "o": [ + [ + -126.866, + -117.468 + ], + [ + 0, + 0 + ], + [ + 69.094, + 39.071 + ], + [ + 84.791, + 0 + ], + [ + -14.957, + -44.737 + ] + ], + "v": [ + [ + 133.898, + -47.203 + ], + [ + -164.471, + -30.757 + ], + [ + -233.657, + 103.292 + ], + [ + 0.303, + 164.671 + ], + [ + 233.657, + 103.636 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0.94, + 0.94, + 0.94, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 233.907, + 164.921 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + }, + { + "ddd": 0, + "ind": 17, + "ty": 4, + "nm": "bg", + "ks": { + "o": { + "k": 100 + }, + "r": { + "k": 0 + }, + "p": { + "k": [ + 500, + 500, + 0 + ] + }, + "a": { + "k": [ + 476.25, + 476.25, + 0 + ] + }, + "s": { + "k": [ + 100, + 100, + 100 + ] + } + }, + "ao": 0, + "shapes": [ + { + "ty": "gr", + "it": [ + { + "ind": 0, + "ty": "sh", + "ks": { + "k": { + "i": [ + [ + 0, + 262.888 + ], + [ + 262.888, + 0 + ], + [ + 0, + -262.888 + ], + [ + -262.888, + 0 + ] + ], + "o": [ + [ + 0, + -262.888 + ], + [ + -262.888, + 0 + ], + [ + 0, + 262.888 + ], + [ + 262.888, + 0 + ] + ], + "v": [ + [ + 476, + 0 + ], + [ + 0, + -476 + ], + [ + -476, + 0 + ], + [ + 0, + 476 + ] + ], + "c": true + } + }, + "nm": "Path 1", + "mn": "ADBE Vector Shape - Group" + }, + { + "ty": "fl", + "fillEnabled": true, + "c": { + "k": [ + 0.62, + 0.79, + 0.81, + 1 + ] + }, + "o": { + "k": 100 + }, + "nm": "Fill 1", + "mn": "ADBE Vector Graphic - Fill" + }, + { + "ty": "tr", + "p": { + "k": [ + 476.25, + 476.25 + ], + "ix": 2 + }, + "a": { + "k": [ + 0, + 0 + ], + "ix": 1 + }, + "s": { + "k": [ + 100, + 100 + ], + "ix": 3 + }, + "r": { + "k": 0, + "ix": 6 + }, + "o": { + "k": 100, + "ix": 7 + }, + "sk": { + "k": 0, + "ix": 4 + }, + "sa": { + "k": 0, + "ix": 5 + }, + "nm": "Transform" + } + ], + "nm": "Group 1", + "np": 2, + "mn": "ADBE Vector Group" + } + ], + "ip": 0, + "op": 78, + "st": 0, + "bm": 0, + "sr": 1 + } + ], + "v": "4.5.3", + "ddd": 0, + "ip": 0, + "op": 78, + "fr": 25, + "w": 1000, + "h": 1000 +} \ 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/docs/Android\351\227\256\351\242\230\346\261\207\346\200\273.md" "b/docs/Android\351\227\256\351\242\230\346\261\207\346\200\273.md" index 8b00f3101..725f2cc1c 100644 --- "a/docs/Android\351\227\256\351\242\230\346\261\207\346\200\273.md" +++ "b/docs/Android\351\227\256\351\242\230\346\261\207\346\200\273.md" @@ -26,9 +26,25 @@ maven { 参考:https://github.com/flutter/flutter/issues/39729 +## 关于打包 + +默认使用`flutter build apk`命令,包含32、64位。 + +添加`--target-platform`可指定平台,比如`android-arm`或`android-arm64`,来减小包体积。 + +还可以使用`--split-debug-info`标志省略调试信息,来减小包体积。(注意使用此方式无法获取可读的堆栈信息) + +完整举例子: + +``` +flutter build apk --target-platform android-arm64 --obfuscate --split-debug-info=/flutter_deer/ +``` + ## 历史问题 -- 1.22.0已知问题(~~#67262~~ ~~#67213~~) +- 3.10.0已知问题(~~#124546~~ ~~#126560~~ ~~#131319~~ ~~#73388~~)。 + +- 1.22.0已知问题(~~#67262~~ ~~#67213~~)。 - 1.17.0已知问题(~~#25767~~ ~~#47191~~)。 diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 22de15160..66fdb4dea 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,9 +1,47 @@ # Change Log: +## 1.3.3 + +* 适配Android 14。 +* Flutter SDK升至3.24.0。 + +## 1.3.2 + +* Android导航栏颜色优化。 + +## 1.3.1 + +* Flutter SDK升至3.16.5。 + +## 1.3.0 + +* 适配Android 13。 +* Flutter SDK升至3.10.0。 + +## 1.2.3 + +* Flutter SDK升至3.7.0。 + +## 1.2.2 + +* 适配Android 12。 +* Flutter SDK升至3.0.0。 + +## 1.2.1 + +* 适配Android 11。 + +## 1.2.0 + +* 迁移到空安全。 +* 修复曲线图长按滑动不展示提示框问题。 + ## 1.1.7 * 深色模式适配导航栏,优化启动效果。 * 适配Android 11键盘弹出动画。 +* 商品列表进入编辑页时添加Hero动画。 +* 添加lottie使用demo。 ## 1.1.6 @@ -64,4 +102,8 @@ * 启动页适配深色模式。 * 更好的无障碍支持。 +## 1.0.7 + +* 优化商品列表 + diff --git "a/docs/Web\351\227\256\351\242\230\346\261\207\346\200\273.md" "b/docs/Web\351\227\256\351\242\230\346\261\207\346\200\273.md" index 6bc5b3cfb..e54755964 100644 --- "a/docs/Web\351\227\256\351\242\230\346\261\207\346\200\273.md" +++ "b/docs/Web\351\227\256\351\242\230\346\261\207\346\200\273.md" @@ -1,14 +1,10 @@ -# Web 问题汇总(flutter 2.0.3) +# Web 问题汇总(flutter 2.10.3) -## CanvasKit渲染(默认PC浏览器) - -### 使用`DecorationImage`的`colorFilter`属性。 - -`ColorFilter.mode`中的color为null时,Web报错NoSuchMethodError: invalid member on null: 'red' ,因此这里注意指定色值。 +## service worker -其他相关问题: +flutter 2.2中新的`service worker`加载机制目前发现兼容不够,部分浏览器无法正常工作。(web/index1.html) -- [[web] wrong image filter on web app built with HTML renderer](https://github.com/flutter/flutter/issues/76966) +## CanvasKit渲染(默认PC浏览器) ### 中文文字、表情等加载延迟导致乱码现象 @@ -20,19 +16,18 @@ - [[Web] [CanvasKit][Feature Request]: Load fonts as soon as detecting browser locale](https://github.com/flutter/flutter/issues/77023) -## HTML渲染(默认手机浏览器) - -### 使用`TextOverflow.ellipsis`属性。 - -现象如下: +## 指定渲染引擎 -- 文字没有超出,后面出现红色省略号。 +``` +flutter run -d chrome --release --web-renderer html +// 或 +flutter run -d chrome --release --web-renderer canvaskit +``` -- 文字超出,未出现省略号。 +> 总结:HTML渲染相较于CanvasKit渲染,UI还原度差一些,但综合性能相对较好。 -其他相关问题: -- [[canvaskit] font renders missing glyph when text overflow is ellipsis](https://github.com/flutter/flutter/issues/76473) +## 已解决问题 ### 使用`Transform`。 @@ -40,15 +35,38 @@ 目前处理方法是添加`RepaintBoundary`。 -## 指定渲染引擎 +### 按钮的大小在移动端与Web端不同 -``` -flutter run -d chrome --release --web-renderer html -// 或 -flutter run -d chrome --release --web-renderer canvaskit -``` +可以`ThemeData`中全局指定`visualDensity`属性为`VisualDensity.standard`。 -> 总结:HTML渲染相较于CanvasKit渲染,UI还原度差一些,但综合性能相对较好。 +详情见[Buttons not respecting default dimensions](https://github.com/flutter/flutter/issues/77142) + +## 已修复问题 + +### ~~使用`DecorationImage`的`colorFilter`属性。~~ + +`ColorFilter.mode`中的color为null时,Web报错NoSuchMethodError: invalid member on null: 'red' 。 +2.2.0上`ColorFilter.mode`中的color以不能设为null。 + +其他相关问题: + +[[web] wrong image filter on web app built with HTML renderer](https://github.com/flutter/flutter/issues/76966) + +### ~~使用`Locale`报空安全错误~~ + +2.2.0已修复,详情见[[Web]: App throws null safety errors on Locale using latest stable, but works on Master](https://github.com/flutter/flutter/issues/79351) + +### ~~HTML渲染使用`TextOverflow.ellipsis`属性~~ + +现象如下: + +- 文字没有超出,后面出现红色省略号。 + +- 文字超出,未出现省略号。 + +其他相关问题: + +- [[canvaskit] font renders missing glyph when text overflow is ellipsis](https://github.com/flutter/flutter/issues/76473) diff --git a/integration_test/goods_test.dart b/integration_test/goods_test.dart new file mode 100644 index 000000000..e9ea04337 --- /dev/null +++ b/integration_test/goods_test.dart @@ -0,0 +1,130 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_deer/goods/page/goods_edit_page.dart'; +import 'package:flutter_deer/goods/page/goods_page.dart'; +import 'package:flutter_deer/goods/page/goods_size_page.dart'; +import 'package:flutter_deer/main.dart'; +import 'package:flutter_deer/res/constant.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + + +/// flutter drive --driver integration_test/integration_test.dart --target integration_test/goods_test.dart +void main() { + + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + group('商品部分:', () { + + Constant.isDriverTest = true; + + tearDown(() { + debugPrint('< Success'); + }); + + testWidgets('商品页测试',(WidgetTester tester) async { + runApp(MyApp(home: const GoodsPage())); + await tester.pumpAndSettle(); + + await tester.tap(find.text('待售')); + await tester.pumpAndSettle(); + + final Finder pageView = find.byKey(const Key('pageView')); + await tester.drag(pageView, const Offset(400, 0)); + await tester.pumpAndSettle(); + + await tester.tap(find.text('全部商品')); + await tester.pumpAndSettle(); + await tester.tap(find.text('休闲食品')); + await tester.pumpAndSettle(); + //进入搜索页 + await tester.tap(find.byKey(const Key('search'))); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key('search_back'))); + await tester.pumpAndSettle(); + //添加商品 + await tester.tap(find.byKey(const Key('add'))); + await tester.pumpAndSettle(); + await tester.tap(find.text('添加商品')); + await tester.pumpAndSettle(); + await tester.tap(find.byTooltip('Back')); + await tester.pumpAndSettle(); + // 商品菜单 + await tester.tap(find.byKey(const Key('goods_menu_item_2'))); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key('goods_operation_item_2'))); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key('goods_delete_item_2'))); + await tester.pumpAndSettle(); + await tester.tap(find.text('确认删除')); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key('goods_menu_item_1'))); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key('goods_edit_item_1'))); + await tester.pumpAndSettle(); + }); + + testWidgets('商品编辑页测试',(WidgetTester tester) async { + runApp(MyApp(home: const GoodsEditPage())); + await tester.pumpAndSettle(); + + await tester.drag(find.byKey(const Key('goods_edit_page')), const Offset(0, -500)); + await tester.pumpAndSettle(); + await tester.tap(find.text('商品类型')); + await tester.pumpAndSettle(); + await tester.tap(find.text('生鲜果蔬')); + await tester.pumpAndSettle(); + await tester.tap(find.text('厨房用具')); + await tester.pumpAndSettle(); + await tester.tap(find.text('碗碟')); + await tester.pumpAndSettle(); + }, timeout: const Timeout(Duration(seconds: 30))); + + testWidgets('商品规格页测试',(WidgetTester tester) async { + runApp(MyApp(home: const GoodsSizePage())); + + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key('hint'))); + await tester.pumpAndSettle(); + + final richText = find.byKey(const Key('name_edit')).first; + fireOnTap(richText, '编辑'); + await tester.pumpAndSettle(); + await tester.tap(find.text('取消')); + await tester.pumpAndSettle(); + + await tester.tap(find.byKey(const Key('2'))); + await tester.pumpAndSettle(); + await tester.tap(find.byTooltip('Back')); + await tester.pumpAndSettle(); + // 侧滑删除 + await tester.drag(find.byKey(const Key('2')), const Offset(-100, 0)); + await tester.pumpAndSettle(); + await tester.tap(find.byKey(const Key('delete_2'))); + await tester.pumpAndSettle(); + await tester.tap(find.byTooltip('Back')); + await tester.pumpAndSettle(); + await tester.tap(find.byTooltip('Back')); + await tester.pumpAndSettle(); + }, timeout: const Timeout(Duration(seconds: 30))); + }); +} + +/// https://github.com/flutter/flutter/issues/56023 +/// Runs the onTap handler for the [TextSpan] which matches the search-string. +void fireOnTap(Finder finder, String text) { + final Element element = finder.evaluate().single; + final RenderParagraph paragraph = element.renderObject! as RenderParagraph; + // The children are the individual TextSpans which have GestureRecognizers + paragraph.text.visitChildren((InlineSpan span) { + if (span is TextSpan) { + if (span.text != text) { + return true; // continue iterating. + } + + (span.recognizer! as TapGestureRecognizer).onTap!(); + } + return false; // stop iterating, we found the one. + }); +} diff --git a/integration_test/integration_test.dart b/integration_test/integration_test.dart index 8f56e577a..53758622a 100644 --- a/integration_test/integration_test.dart +++ b/integration_test/integration_test.dart @@ -1,4 +1,4 @@ import 'package:integration_test/integration_test_driver.dart'; -Future main() => integrationDriver(); \ No newline at end of file +Future main() => integrationDriver(); diff --git a/integration_test/login_test.dart b/integration_test/login_test.dart index 85c7e1307..547aa22b0 100644 --- a/integration_test/login_test.dart +++ b/integration_test/login_test.dart @@ -14,7 +14,7 @@ void main() { group('登录部分:', () { tearDown(() { - print('< Success'); + debugPrint('< Success'); }); testWidgets('登录页按钮点击',(WidgetTester tester) async { @@ -80,4 +80,4 @@ void main() { await tester.tap(find.byKey(const Key('login'))); // 点击登录 }, timeout: const Timeout(Duration(seconds: 30))); }); -} \ No newline at end of file +} diff --git a/ios/Flutter/Flutter.podspec b/ios/Flutter/Flutter.podspec index 2c4421cfe..3aed58d35 100644 --- a/ios/Flutter/Flutter.podspec +++ b/ios/Flutter/Flutter.podspec @@ -1,17 +1,17 @@ # -# NOTE: This podspec is NOT to be published. It is only used as a local source! -# This is a generated file; do not edit or check into version control. +# This podspec is NOT to be published. It is only used as a local source! +# This is a generated file; do not edit or check into version control. # Pod::Spec.new do |s| s.name = 'Flutter' s.version = '1.0.0' - s.summary = 'High-performance, high-fidelity mobile apps.' - s.homepage = 'https://flutter.io' - s.license = { :type => 'MIT' } + s.summary = 'A UI toolkit for beautiful and fast apps.' + s.homepage = 'https://flutter.dev' + s.license = { :type => 'BSD' } s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } - s.ios.deployment_target = '8.0' + s.ios.deployment_target = '13.0' # Framework linking is handled by Flutter tooling, not CocoaPods. # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. s.vendored_frameworks = 'path/to/nothing' diff --git a/ios/Flutter/ephemeral/flutter_lldb_helper.py b/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 000000000..a88caf99d --- /dev/null +++ b/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/ios/Flutter/ephemeral/flutter_lldbinit b/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 000000000..e3ba6fbed --- /dev/null +++ b/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/ios/Podfile b/ios/Podfile index c2786089e..4ac67eb86 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,8 +1,8 @@ # Uncomment this line to define a global platform for your project -platform :ios, '9.0' +platform :ios, '13.0' # source 'https://github.com/CocoaPods/Specs.git' - +# source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -40,9 +40,11 @@ post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) target.build_configurations.each do |config| - if config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f < 9.0 - config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0' + if config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'].to_f < 12.0 + config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0' end + # https://github.com/flutter/flutter/issues/90504 #84562 + config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64 i386' end end end diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index f99394215..07dfd4ef9 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -13,7 +13,7 @@ 7273ADFF2478E21800309008 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 7273AE092478E42B00309008 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7273AE072478E42B00309008 /* LaunchScreen.storyboard */; }; 7273AE0A2478E42B00309008 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7273AE082478E42B00309008 /* Main.storyboard */; }; - BD4F247307C1B2EC10CEE075 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 242256812F0E5EC2FC55E432 /* Pods_Runner.framework */; }; + AEA69329A74DB4B25E2ECB87 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4D67B71C00D08ADD4FAADEDB /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -30,11 +30,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 0D45FAC02A7C4145DC023503 /* 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 = ""; }; - 242256812F0E5EC2FC55E432 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 37E8FC7E71F65C9DD832AA03 /* 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 = ""; }; + 02A7EA573B0D97D5A71DE380 /* 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 = ""; }; + 3AE98DAA649A86DB32C2E0E2 /* 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 = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 42FEB795BCD522EBDFCE3114 /* 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 = ""; }; + 4D67B71C00D08ADD4FAADEDB /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7273ADEE2478DB7900309008 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 7273ADEF2478DB7900309008 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 7273ADF02478DB7900309008 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; @@ -47,6 +46,7 @@ 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; }; + DC4B9C102740D28696650567 /* 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 = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -54,7 +54,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BD4F247307C1B2EC10CEE075 /* Pods_Runner.framework in Frameworks */, + AEA69329A74DB4B25E2ECB87 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -64,9 +64,9 @@ 9707CA0EB99412E1173056F5 /* Pods */ = { isa = PBXGroup; children = ( - 42FEB795BCD522EBDFCE3114 /* Pods-Runner.debug.xcconfig */, - 37E8FC7E71F65C9DD832AA03 /* Pods-Runner.release.xcconfig */, - 0D45FAC02A7C4145DC023503 /* Pods-Runner.profile.xcconfig */, + DC4B9C102740D28696650567 /* Pods-Runner.debug.xcconfig */, + 02A7EA573B0D97D5A71DE380 /* Pods-Runner.release.xcconfig */, + 3AE98DAA649A86DB32C2E0E2 /* Pods-Runner.profile.xcconfig */, ); path = Pods; sourceTree = ""; @@ -89,7 +89,7 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 9707CA0EB99412E1173056F5 /* Pods */, - EBF36AE8FFF8D2ECDFCC4D49 /* Frameworks */, + 9A9514DF7710535D7ECD0A61 /* Frameworks */, ); sourceTree = ""; }; @@ -116,10 +116,10 @@ path = Runner; sourceTree = ""; }; - EBF36AE8FFF8D2ECDFCC4D49 /* Frameworks */ = { + 9A9514DF7710535D7ECD0A61 /* Frameworks */ = { isa = PBXGroup; children = ( - 242256812F0E5EC2FC55E432 /* Pods_Runner.framework */, + 4D67B71C00D08ADD4FAADEDB /* Pods_Runner.framework */, ); name = Frameworks; sourceTree = ""; @@ -131,15 +131,15 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - C26F12588241FFAE76860EB0 /* [CP] Check Pods Manifest.lock */, + 832472DC3989AE8A4B64E55F /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - 24202F6F610BDCB07C0B5A7A /* [CP] Copy Pods Resources */, - 876E9B92C7728849627E2252 /* [CP] Embed Pods Frameworks */, + 8E838C9A100424E084A08A18 /* [CP] Embed Pods Frameworks */, + 3D5BFACDBCC1E59EE35581ED /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -156,7 +156,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1130; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Chromium Authors"; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -198,7 +198,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 24202F6F610BDCB07C0B5A7A /* [CP] Copy Pods Resources */ = { + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\n"; + }; + 3D5BFACDBCC1E59EE35581ED /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -216,54 +232,62 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + 832472DC3989AE8A4B64E55F /* [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 = ( ); - name = "Thin Binary"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin\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; }; - 876E9B92C7728849627E2252 /* [CP] Embed Pods Frameworks */ = { + 8E838C9A100424E084A08A18 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework", "${BUILT_PRODUCTS_DIR}/MTBBarcodeScanner/MTBBarcodeScanner.framework", - "${BUILT_PRODUCTS_DIR}/image_picker/image_picker.framework", + "${BUILT_PRODUCTS_DIR}/device_info_plus/device_info_plus.framework", + "${BUILT_PRODUCTS_DIR}/image_picker_ios/image_picker_ios.framework", "${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework", - "${BUILT_PRODUCTS_DIR}/path_provider/path_provider.framework", + "${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework", "${BUILT_PRODUCTS_DIR}/qr_code_scanner/qr_code_scanner.framework", - "${BUILT_PRODUCTS_DIR}/quick_actions/quick_actions.framework", - "${BUILT_PRODUCTS_DIR}/shared_preferences/shared_preferences.framework", + "${BUILT_PRODUCTS_DIR}/quick_actions_ios/quick_actions_ios.framework", + "${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework", "${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework", - "${BUILT_PRODUCTS_DIR}/url_launcher/url_launcher.framework", + "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", "${BUILT_PRODUCTS_DIR}/vibration/vibration.framework", - "${BUILT_PRODUCTS_DIR}/webview_flutter/webview_flutter.framework", + "${BUILT_PRODUCTS_DIR}/webview_flutter_wkwebview/webview_flutter_wkwebview.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MTBBarcodeScanner.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_picker_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/qr_code_scanner.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/quick_actions.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/quick_actions_ios.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/vibration.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/webview_flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/webview_flutter_wkwebview.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -272,6 +296,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -284,28 +309,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; - C26F12588241FFAE76860EB0 /* [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; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -363,7 +366,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -381,14 +384,18 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = ""; - ENABLE_BITCODE = YES; + ENABLE_BITCODE = NO; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -450,7 +457,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -500,7 +507,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; TARGETED_DEVICE_FAMILY = "1,2"; @@ -516,14 +523,18 @@ CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = ""; - ENABLE_BITCODE = YES; + ENABLE_BITCODE = NO; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", @@ -547,14 +558,18 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; DEVELOPMENT_TEAM = ""; - ENABLE_BITCODE = YES; + ENABLE_BITCODE = NO; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "i386 arm64"; FRAMEWORK_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", ); INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); LIBRARY_SEARCH_PATHS = ( "$(inherited)", "$(PROJECT_DIR)/Flutter", diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index ad6250a1c..9c12df59c 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index f95f83ac6..6057e504a 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,14 +1,14 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) - let controller:FlutterViewController = window.rootViewController as! FlutterViewController + let controller:FlutterViewController = window?.rootViewController as! FlutterViewController let versionChannel = FlutterMethodChannel(name: "version", binaryMessenger: controller as! FlutterBinaryMessenger) versionChannel.setMethodCallHandler { (call, result) in if "jumpAppStore" == call.method { diff --git a/ios/Runner/Assets.xcassets/flutter_dash_black.imageset/Contents.json b/ios/Runner/Assets.xcassets/flutter_dash_black.imageset/Contents.json new file mode 100644 index 000000000..dba3e411e --- /dev/null +++ b/ios/Runner/Assets.xcassets/flutter_dash_black.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "flutter_dash_black_1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "flutter_dash_black_2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "flutter_dash_black_3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/ios/Runner/Assets.xcassets/flutter_dash_black.imageset/flutter_dash_black_1x.png b/ios/Runner/Assets.xcassets/flutter_dash_black.imageset/flutter_dash_black_1x.png new file mode 100644 index 000000000..fb8b90151 Binary files /dev/null and b/ios/Runner/Assets.xcassets/flutter_dash_black.imageset/flutter_dash_black_1x.png differ diff --git a/ios/Runner/Assets.xcassets/flutter_dash_black.imageset/flutter_dash_black_2x.png b/ios/Runner/Assets.xcassets/flutter_dash_black.imageset/flutter_dash_black_2x.png new file mode 100644 index 000000000..9b05bc957 Binary files /dev/null and b/ios/Runner/Assets.xcassets/flutter_dash_black.imageset/flutter_dash_black_2x.png differ diff --git a/ios/Runner/Assets.xcassets/flutter_dash_black.imageset/flutter_dash_black_3x.png b/ios/Runner/Assets.xcassets/flutter_dash_black.imageset/flutter_dash_black_3x.png new file mode 100644 index 000000000..b14af061a Binary files /dev/null and b/ios/Runner/Assets.xcassets/flutter_dash_black.imageset/flutter_dash_black_3x.png differ diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index ad844017b..cbc408b3f 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -54,5 +54,9 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/ios/Runner/LaunchScreen.storyboard b/ios/Runner/LaunchScreen.storyboard index 42c84fd50..933b65471 100644 --- a/ios/Runner/LaunchScreen.storyboard +++ b/ios/Runner/LaunchScreen.storyboard @@ -1,9 +1,10 @@ - + - + + @@ -23,7 +24,7 @@ - + @@ -37,5 +38,8 @@ + + + diff --git a/l10n.yaml b/l10n.yaml index caf1d834b..f3b15f7dc 100644 --- a/l10n.yaml +++ b/l10n.yaml @@ -1,5 +1,4 @@ # 文档:https://flutter.dev/docs/development/accessibility-and-localization/internationalization - arb-dir: lib/l10n template-arb-file: intl_en.arb output-localization-file: deer_localizations.dart diff --git a/lib/account/account_router.dart b/lib/account/account_router.dart index b8cb22e39..4ebde5d55 100644 --- a/lib/account/account_router.dart +++ b/lib/account/account_router.dart @@ -1,4 +1,3 @@ - import 'package:fluro/fluro.dart'; import 'package:flutter_deer/routers/i_router.dart'; @@ -35,7 +34,7 @@ class AccountRouter implements IRouterProvider{ router.define(accountRecordListPage, handler: Handler(handlerFunc: (_, __) => const AccountRecordListPage())); router.define(addWithdrawalAccountPage, handler: Handler(handlerFunc: (_, __) => const AddWithdrawalAccountPage())); router.define(bankSelectPage, handler: Handler(handlerFunc: (_, Map> params) { - final int type = int.parse(params['type']?.first); + final int type = int.parse(params['type']?.first ?? '0'); return BankSelectPage(type: type); })); router.define(citySelectPage, handler: Handler(handlerFunc: (_, __) => const CitySelectPage())); @@ -47,4 +46,4 @@ class AccountRouter implements IRouterProvider{ router.define(withdrawalResultPage, handler: Handler(handlerFunc: (_, __) => const WithdrawalResultPage())); } -} \ No newline at end of file +} diff --git a/lib/account/models/bank_entity.dart b/lib/account/models/bank_entity.dart index 8ff4630e0..a537199e7 100644 --- a/lib/account/models/bank_entity.dart +++ b/lib/account/models/bank_entity.dart @@ -1,16 +1,22 @@ import 'package:azlistview/azlistview.dart'; -import 'package:flutter_deer/generated/json/base/json_convert_content.dart'; +import 'package:flutter_deer/generated/json/bank_entity.g.dart'; +import 'package:flutter_deer/generated/json/base/json_field.dart'; -class BankEntity with JsonConvert, ISuspensionBean { +@JsonSerializable() +class BankEntity with ISuspensionBean { BankEntity({this.id, this.bankName, this.firstLetter}); - int id; - String bankName; - String firstLetter; + factory BankEntity.fromJson(Map json) => $BankEntityFromJson(json); + + Map toJson() => $BankEntityToJson(this); + + int? id; + String? bankName; + String? firstLetter; @override String getSuspensionTag() { - return firstLetter; + return firstLetter ?? ''; } } diff --git a/lib/account/models/city_entity.dart b/lib/account/models/city_entity.dart index 29a692d98..5f89e5989 100644 --- a/lib/account/models/city_entity.dart +++ b/lib/account/models/city_entity.dart @@ -1,10 +1,19 @@ import 'package:azlistview/azlistview.dart'; -import 'package:flutter_deer/generated/json/base/json_convert_content.dart'; +import 'package:flutter_deer/generated/json/base/json_field.dart'; +import 'package:flutter_deer/generated/json/city_entity.g.dart'; -class CityEntity with JsonConvert, ISuspensionBean { - String name; - String cityCode; - String firstCharacter; +@JsonSerializable() +class CityEntity with ISuspensionBean { + + CityEntity(); + + factory CityEntity.fromJson(Map json) => $CityEntityFromJson(json); + + Map toJson() => $CityEntityToJson(this); + + late String name; + late String cityCode; + late String firstCharacter; @override String getSuspensionTag() { diff --git a/lib/account/page/account_page.dart b/lib/account/page/account_page.dart index f05f9dfdb..dde65345d 100644 --- a/lib/account/page/account_page.dart +++ b/lib/account/page/account_page.dart @@ -1,19 +1,18 @@ - import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_deer/account/widgets/rise_number_text.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/image_utils.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/click_item.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import '../account_router.dart'; /// design/6店铺-账户/index.html#artboard2 class AccountPage extends StatefulWidget { - const AccountPage({Key key}) : super(key: key); + const AccountPage({super.key}); @override _AccountPageState createState() => _AccountPageState(); @@ -62,9 +61,9 @@ class _AccountPageState extends State { fit: BoxFit.fill, ), ), - child: Column( + child: const Column( children: [ - const _AccountMoney( + _AccountMoney( title: '当前余额(元)', money: '30.12', alignment: MainAxisAlignment.end, @@ -72,7 +71,7 @@ class _AccountPageState extends State { ), Expanded( child: Row( - children: const [ + children: [ _AccountMoney(title: '累计结算金额', money: '20000'), _AccountMoney(title: '累计发放佣金', money: '0.02'), ], @@ -88,17 +87,16 @@ class _AccountPageState extends State { class _AccountMoney extends StatelessWidget { const _AccountMoney({ - Key key, - @required this.title, - @required this.money, + required this.title, + required this.money, this.alignment, this.moneyTextStyle - }): super(key: key); + }); final String title; final String money; - final MainAxisAlignment alignment; - final TextStyle moneyTextStyle; + final MainAxisAlignment? alignment; + final TextStyle? moneyTextStyle; @override Widget build(BuildContext context) { @@ -112,7 +110,7 @@ class _AccountMoney extends StatelessWidget { Text(title, style: const TextStyle(color: Colours.text_disabled, fontSize: Dimens.font_sp12)), Gaps.vGap8, RiseNumberText( - NumUtil.getDoubleByValueStr(money), + NumUtil.getDoubleByValueStr(money) ?? 0, style: moneyTextStyle ?? const TextStyle( color: Colours.text_disabled, fontSize: Dimens.font_sp14, diff --git a/lib/account/page/account_record_list_page.dart b/lib/account/page/account_record_list_page.dart index a19f9b66a..164f090ec 100644 --- a/lib/account/page/account_record_list_page.dart +++ b/lib/account/page/account_record_list_page.dart @@ -1,14 +1,13 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/widgets/my_app_bar.dart'; -import 'package:sticky_headers/sticky_headers.dart'; +import '../../order/page/order_page.dart'; /// design/6店铺-账户/index.html#artboard1 class AccountRecordListPage extends StatefulWidget { - const AccountRecordListPage({Key key}) : super(key: key); + const AccountRecordListPage({super.key}); @override _AccountRecordListPageState createState() => _AccountRecordListPageState(); @@ -21,70 +20,80 @@ class _AccountRecordListPageState extends State { appBar: const MyAppBar( centerTitle: '账户流水', ), - body: ListView.builder( - itemCount: 8, - /// 将item默认合并的语义拆开,自行组合, 另一种方式见 withdrawal_record_list_page.dart - addSemanticIndexes: false, - itemBuilder: (_, int index) { - return StickyHeader( - header: Container( + body: CustomScrollView( + slivers: [ + for (int i = 0; i < 8; i++) + _buildGroup(i) + ], + ), + ); + } + + Widget _buildGroup(int index) { + return SliverMainAxisGroup( + slivers: [ + SliverPersistentHeader( + pinned: true, + delegate: SliverAppBarDelegate( + Container( alignment: Alignment.centerLeft, width: double.infinity, color: ThemeUtils.getStickyHeaderColor(context), padding: const EdgeInsets.only(left: 16.0), - height: 34.0, child: Text('2021/06/0${index + 1}'), - ), - content: _buildItem(index), - ); - }, - ), + ) + , 34.0, + ), + ), + SliverList( + delegate: SliverChildBuilderDelegate((_, index) { + return _buildItem(index); + }, + childCount: index + 1, + ), + ), + ], ); } - - Widget _buildItem(int index) { - final List list = List.generate(index + 1, (int i) { - return Container( - height: 72.0, - width: double.infinity, - padding: const EdgeInsets.all(15.0), - decoration: BoxDecoration( - border: Border( - bottom: Divider.createBorderSide(context, width: 0.8), - ), + + Widget _buildItem(int i) { + return Container( + height: 72.0, + width: double.infinity, + padding: const EdgeInsets.all(15.0), + decoration: BoxDecoration( + border: Border( + bottom: Divider.createBorderSide(context, width: 0.8), ), - child: IndexedSemantics( - index: index, - child: Stack( - children: [ - Text(i.isEven ? '采购订单结算营收' : '提现'), - Positioned( - top: 0.0, - right: 0.0, - child: Text(i.isEven ? '+10.00' : '-10.00', - style: i.isEven ? TextStyle( - color: Theme.of(context).errorColor, - fontWeight: FontWeight.bold, - ) : TextStyles.textBold14, - ), - ), - Positioned( - bottom: 0.0, - left: 0.0, - child: Text(i.isEven ? '18:20:10' : '08:20:11', style: Theme.of(context).textTheme.subtitle2), - ), - Positioned( - bottom: 0.0, - right: 0.0, - child: Text('余额:20.00', style: Theme.of(context).textTheme.subtitle2), + ), + child: IndexedSemantics( + index: i, + child: Stack( + children: [ + Text(i.isEven ? '采购订单结算营收' : '提现'), + Positioned( + top: 0.0, + right: 0.0, + child: Text(i.isEven ? '+10.00' : '-10.00', + style: i.isEven ? TextStyle( + color: Theme.of(context).colorScheme.error, + fontWeight: FontWeight.bold, + ) : TextStyles.textBold14, ), - ], - ), + ), + Positioned( + bottom: 0.0, + left: 0.0, + child: Text(i.isEven ? '18:20:10' : '08:20:11', style: Theme.of(context).textTheme.titleSmall), + ), + Positioned( + bottom: 0.0, + right: 0.0, + child: Text('余额:20.00', style: Theme.of(context).textTheme.titleSmall), + ), + ], ), - ); - }); - return Column( - children: list + ), ); } } diff --git a/lib/account/page/add_withdrawal_account_page.dart b/lib/account/page/add_withdrawal_account_page.dart index db30a3be9..607fd2673 100644 --- a/lib/account/page/add_withdrawal_account_page.dart +++ b/lib/account/page/add_withdrawal_account_page.dart @@ -4,8 +4,8 @@ import 'package:flutter_deer/account/models/bank_entity.dart'; import 'package:flutter_deer/account/models/city_entity.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; -import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/util/other_utils.dart'; +import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; import 'package:flutter_deer/widgets/my_scroll_view.dart'; @@ -16,7 +16,7 @@ import 'package:flutter_deer/widgets/text_field_item.dart'; /// design/6店铺-账户/index.html#artboard29 class AddWithdrawalAccountPage extends StatefulWidget { - const AddWithdrawalAccountPage({Key key}) : super(key: key); + const AddWithdrawalAccountPage({super.key}); @override _AddWithdrawalAccountPageState createState() => _AddWithdrawalAccountPageState(); @@ -31,7 +31,7 @@ class _AddWithdrawalAccountPageState extends State { @override Widget build(BuildContext context) { - final TextStyle style = Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp14); + final TextStyle? style = Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp14); final List children = [ Gaps.vGap5, SelectedItem( @@ -56,7 +56,7 @@ class _AddWithdrawalAccountPageState extends State { SelectedItem( title: '开 户 地', content: _city.isEmpty ? '选择开户城市' : _city, - style: _city.isEmpty ? style: null, + style: _city.isEmpty ? style : null, onTap: () { NavigatorUtils.pushResult(context, AccountRouter.citySelectPage, (Object result) { setState(() { @@ -74,7 +74,7 @@ class _AddWithdrawalAccountPageState extends State { NavigatorUtils.pushResult(context, '${AccountRouter.bankSelectPage}?type=0', (Object result) { setState(() { final BankEntity model = result as BankEntity; - _bank = model.bankName; + _bank = model.bankName.nullSafe; }); }); }, @@ -87,7 +87,7 @@ class _AddWithdrawalAccountPageState extends State { NavigatorUtils.pushResult(context, '${AccountRouter.bankSelectPage}?type=1', (Object result) { setState(() { final BankEntity model = result as BankEntity; - _bank1 = model.bankName; + _bank1 = model.bankName.nullSafe; }); }); }, @@ -99,7 +99,7 @@ class _AddWithdrawalAccountPageState extends State { padding: const EdgeInsets.only(top: 8.0, left: 16.0), child: Text( _isWechat ? '绑定本机当前登录的微信号' : '绑定持卡人本人的银行卡', - style: Theme.of(context).textTheme.subtitle2, + style: Theme.of(context).textTheme.titleSmall, ), ), ]; @@ -110,14 +110,14 @@ class _AddWithdrawalAccountPageState extends State { title: '添加账号', ), body: MyScrollView( - children: children, bottomButton: Padding( padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 8.0), child: MyButton( onPressed: () => NavigatorUtils.goBackWithParams(context, 'add'), text: '确定', ), - ) + ), + children: children ), ); } @@ -136,7 +136,7 @@ class _AddWithdrawalAccountPageState extends State { showElasticDialog( context: context, builder: (BuildContext context) { - const OutlinedBorder buttonShape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(0))); + const OutlinedBorder buttonShape = RoundedRectangleBorder(); final Widget content = Column( children: [ @@ -190,9 +190,9 @@ class _AddWithdrawalAccountPageState extends State { data: TextButtonThemeData( style: TextButton.styleFrom( // 文字颜色 - primary: Theme.of(context).primaryColor, + foregroundColor: Theme.of(context).primaryColor, // 按钮大小 - minimumSize: const Size(double.infinity, double.infinity), + minimumSize: Size.infinite, // 修改默认圆角 shape: buttonShape, ), diff --git a/lib/account/page/bank_select_page.dart b/lib/account/page/bank_select_page.dart index ba5083337..7a834e7e0 100644 --- a/lib/account/page/bank_select_page.dart +++ b/lib/account/page/bank_select_page.dart @@ -1,20 +1,20 @@ - import 'dart:convert'; +import 'package:azlistview/azlistview.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_deer/account/models/bank_entity.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; +import 'package:flutter_deer/util/other_utils.dart'; import 'package:flutter_deer/util/theme_utils.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; -import 'package:azlistview/azlistview.dart'; import 'package:flutter_deer/widgets/load_image.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; /// design/6店铺-账户/index.html#artboard33 class BankSelectPage extends StatefulWidget { - const BankSelectPage({Key key, this.type}) : super(key: key); + const BankSelectPage({super.key, this.type = 0}); final int type; @@ -48,26 +48,30 @@ class _BankSelectPageState extends State { // 获取城市列表 rootBundle.loadString(widget.type == 0 ? 'assets/data/bank.json' : 'assets/data/bank_2.json').then((String value) { final List list = json.decode(value) as List; - list.forEach((dynamic value) { - _bankList.add(BankEntity().fromJson(value as Map)); - }); + list.forEach(_addBank); SuspensionUtil.sortListBySuspensionTag(_bankList); SuspensionUtil.setShowSuspensionStatus(_bankList); _indexBarData = _bankList.map((BankEntity e) { if (e.isShowSuspension) { - return e.firstLetter; + return e.firstLetter.nullSafe; } else { return ''; } }).where((String element) => element.isNotEmpty).toList(); - // add header. - _bankList.insert(0, BankEntity(firstLetter: '常用')); - _indexBarData.insert(0, '常用'); + if (widget.type == 0) { + // add header. + _bankList.insert(0, BankEntity(firstLetter: '常用')); + _indexBarData.insert(0, '常用'); + } setState(() { }); }); } + + void _addBank(dynamic value) { + _bankList.add(BankEntity.fromJson(value as Map)); + } @override Widget build(BuildContext context) { @@ -80,7 +84,7 @@ class _BankSelectPageState extends State { data: _bankList, itemCount: _bankList.length, itemBuilder: (_, int index) { - if (index == 0) { + if (index == 0 && widget.type == 0) { return _buildHeader(); } return _buildListItem(index); @@ -92,7 +96,7 @@ class _BankSelectPageState extends State { indexHintWidth: 96, indexHintHeight: 96, indexHintTextStyle: const TextStyle(fontSize: 26.0, color: Colors.white), - textStyle: Theme.of(context).textTheme.subtitle2, + textStyle: Theme.of(context).textTheme.titleSmall!, downTextStyle: context.isDark ? TextStyles.textSize12 : const TextStyle(fontSize: 12.0, color: Colors.black), ), ), @@ -108,7 +112,7 @@ class _BankSelectPageState extends State { children: [ Padding( padding: const EdgeInsets.only(top: 5.0, bottom: 5.0, left: 16.0), - child: Text('常用', style: Theme.of(context).textTheme.subtitle2), + child: Text('常用', style: Theme.of(context).textTheme.titleSmall), ), Expanded( child: ListView.builder( @@ -156,11 +160,11 @@ class _BankSelectPageState extends State { opacity: model.isShowSuspension ? 1 : 0, child: SizedBox( width: 28.0, - child: Text(model.firstLetter), + child: Text(model.firstLetter.nullSafe), ) ), Expanded( - child: Text(model.bankName), + child: Text(model.bankName.nullSafe), ) ], ), diff --git a/lib/account/page/city_select_page.dart b/lib/account/page/city_select_page.dart index 5f510cd24..da381e39a 100644 --- a/lib/account/page/city_select_page.dart +++ b/lib/account/page/city_select_page.dart @@ -1,20 +1,19 @@ - import 'dart:convert'; +import 'package:azlistview/azlistview.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_deer/account/models/city_entity.dart'; -import 'package:flutter_deer/common/common.dart'; +import 'package:flutter_deer/res/constant.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/widgets/my_app_bar.dart'; -import 'package:azlistview/azlistview.dart'; /// design/6店铺-账户/index.html#artboard34 class CitySelectPage extends StatefulWidget { - const CitySelectPage({Key key}) : super(key: key); + const CitySelectPage({super.key}); @override _CitySelectPageState createState() => _CitySelectPageState(); @@ -41,9 +40,7 @@ class _CitySelectPageState extends State { jsonStr = await rootBundle.loadString('assets/data/city.json'); } final List list = json.decode(jsonStr) as List; - list.forEach((dynamic value) { - _cityList.add(CityEntity().fromJson(value as Map)); - }); + list.forEach(_addCity); SuspensionUtil.setShowSuspensionStatus(_cityList); _indexBarData = _cityList.map((CityEntity e) { if (e.isShowSuspension) { @@ -57,12 +54,13 @@ class _CitySelectPageState extends State { }); } + void _addCity(dynamic value) { + _cityList.add(CityEntity.fromJson(value as Map)); + } + /// rootBundle.loadString源码修改 Future _loadString(String key) async { final ByteData data = await rootBundle.load(key); - if (data == null) { - throw FlutterError('Unable to load asset: $key'); - } return utf8.decode(data.buffer.asUint8List()); } @@ -84,7 +82,7 @@ class _CitySelectPageState extends State { indexHintWidth: 96, indexHintHeight: 96, indexHintTextStyle: const TextStyle(fontSize: 26.0, color: Colors.white), - textStyle: Theme.of(context).textTheme.subtitle2, + textStyle: Theme.of(context).textTheme.titleSmall!, downTextStyle: context.isDark ? TextStyles.textSize12 : const TextStyle(fontSize: 12.0, color: Colors.black), ), ), diff --git a/lib/account/page/withdrawal_account_list_page.dart b/lib/account/page/withdrawal_account_list_page.dart index 8b907fecf..ef90036eb 100644 --- a/lib/account/page/withdrawal_account_list_page.dart +++ b/lib/account/page/withdrawal_account_list_page.dart @@ -1,17 +1,16 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/account/models/withdrawal_account_model.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/load_image.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import '../account_router.dart'; /// design/6店铺-账户/index.html#artboard7 class WithdrawalAccountListPage extends StatefulWidget { - const WithdrawalAccountListPage({Key key}) : super(key: key); + const WithdrawalAccountListPage({super.key}); @override _WithdrawalAccountListPageState createState() => _WithdrawalAccountListPageState(); diff --git a/lib/account/page/withdrawal_account_page.dart b/lib/account/page/withdrawal_account_page.dart index be19d6765..655c1db3c 100644 --- a/lib/account/page/withdrawal_account_page.dart +++ b/lib/account/page/withdrawal_account_page.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/account/models/withdrawal_account_model.dart'; import 'package:flutter_deer/account/widgets/withdrawal_account_item.dart'; @@ -13,7 +12,7 @@ import '../account_router.dart'; /// design/6店铺-账户/index.html#artboard26 class WithdrawalAccountPage extends StatefulWidget { - const WithdrawalAccountPage({Key key}) : super(key: key); + const WithdrawalAccountPage({super.key}); @override _WithdrawalAccountPageState createState() => _WithdrawalAccountPageState(); @@ -31,7 +30,7 @@ class _WithdrawalAccountPageState extends State { _list.clear(); _list.add(WithdrawalAccountModel('唯鹿', '微信', 1, '')); _list.add(WithdrawalAccountModel('李*', '工商银行', 0, '**** **** **** 5236')); - _list.add(WithdrawalAccountModel('李*', '渤海银行', 0, '**** **** **** 2165')); + _list.add(WithdrawalAccountModel('李*', '工商银行', 0, '**** **** **** 2165')); } @override @@ -72,7 +71,7 @@ class _WithdrawalAccountPageState extends State { void _removeItem(int index) { /// 先移除数据 final WithdrawalAccountModel item = _list.removeAt(index); - _listKey.currentState.removeItem( + _listKey.currentState?.removeItem( index, (_, animation) => sizeItem(item, 0, animation), /// 构建移除Widget duration: _kDuration, ); @@ -95,7 +94,7 @@ class _WithdrawalAccountPageState extends State { }); } else { - _listKey.currentState.insertItem( + _listKey.currentState?.insertItem( index, duration: _kDuration, ); @@ -123,7 +122,7 @@ class _WithdrawalAccountPageState extends State { Gaps.line, MyButton( minHeight: 54.0, - textColor: Theme.of(context).errorColor, + textColor: Theme.of(context).colorScheme.error, text: '确认解绑', backgroundColor: Colors.transparent, onPressed: () { diff --git a/lib/account/page/withdrawal_page.dart b/lib/account/page/withdrawal_page.dart index 55eb58e79..f9fdbb1b3 100644 --- a/lib/account/page/withdrawal_page.dart +++ b/lib/account/page/withdrawal_page.dart @@ -1,12 +1,11 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/account/models/withdrawal_account_model.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/input_formatter/number_text_input_formatter.dart'; import 'package:flutter_deer/util/theme_utils.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/load_image.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; import 'package:flutter_deer/widgets/my_scroll_view.dart'; @@ -15,7 +14,7 @@ import '../account_router.dart'; /// design/6店铺-账户/index.html#artboard3 class WithdrawalPage extends StatefulWidget { - const WithdrawalPage({Key key}) : super(key: key); + const WithdrawalPage({super.key}); @override _WithdrawalPageState createState() => _WithdrawalPageState(); @@ -56,11 +55,10 @@ class _WithdrawalPageState extends State { @override Widget build(BuildContext context) { - return WillPopScope( - onWillPop: () { + return PopScope( + onPopInvoked: (_) { /// 拦截返回,关闭键盘,否则会造成上一页面短暂的组件溢出 FocusManager.instance.primaryFocus?.unfocus(); - return Future.value(true); }, child: Scaffold( appBar: const MyAppBar( @@ -93,7 +91,7 @@ class _WithdrawalPageState extends State { children: [ Text(_data.typeName), Gaps.vGap8, - Text(_data.name, style: Theme.of(context).textTheme.subtitle2), + Text(_data.name, style: Theme.of(context).textTheme.titleSmall), ], ), ), @@ -103,11 +101,11 @@ class _WithdrawalPageState extends State { ), ), Gaps.vGap16, - Row( + const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ + children: [ Text('提现金额', style: TextStyles.textBold14), - Text('单笔2万,单日2万', style: TextStyle(fontSize: Dimens.font_sp12, color: Color(0xFFFF8547))) + Text('单笔2万,单日2万', style: TextStyle(fontSize: Dimens.font_sp12, color: Colours.orange)) ], ), Gaps.vGap8, @@ -151,7 +149,7 @@ class _WithdrawalPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('最多可提现70元', style: Theme.of(context).textTheme.subtitle2), + Text('最多可提现70元', style: Theme.of(context).textTheme.titleSmall), GestureDetector( onTap: () { _controller.text = '70'; @@ -166,9 +164,9 @@ class _WithdrawalPageState extends State { ) ], ), - Row( + const Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ + children: [ Text('转出方式', style: TextStyles.textBold14), LoadAssetImage('account/sm', width: 16.0) ], @@ -220,16 +218,16 @@ class _WithdrawalPageState extends State { child: RichText( text: type == 0 ? TextSpan( text: '手续费按', - style: Theme.of(context).textTheme.bodyText2.copyWith(fontSize: Dimens.font_sp12), + style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontSize: Dimens.font_sp12), children: const [ - TextSpan(text: '0.3%', style: TextStyle(color: Color(0xFFFF8547))), + TextSpan(text: '0.3%', style: TextStyle(color: Colours.orange)), TextSpan(text: '收取'), ], ) : TextSpan( text: '预计', - style: Theme.of(context).textTheme.bodyText2.copyWith(fontSize: Dimens.font_sp12), + style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontSize: Dimens.font_sp12), children: const [ - TextSpan(text: 'T+1天到账(免手续费,T为工作日)', style: TextStyle(color: Color(0xFFFF8547))), + TextSpan(text: 'T+1天到账(免手续费,T为工作日)', style: TextStyle(color: Colours.orange)), ], ), ) diff --git a/lib/account/page/withdrawal_password_page.dart b/lib/account/page/withdrawal_password_page.dart index 43e49e731..d636bbebe 100644 --- a/lib/account/page/withdrawal_password_page.dart +++ b/lib/account/page/withdrawal_password_page.dart @@ -1,18 +1,17 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/account/widgets/sms_verify_dialog.dart'; import 'package:flutter_deer/account/widgets/withdrawal_password_setting.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/other_utils.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/base_dialog.dart'; import 'package:flutter_deer/widgets/click_item.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; /// design/6店铺-账户/index.html#artboard20 class WithdrawalPasswordPage extends StatefulWidget { - const WithdrawalPasswordPage({Key key}) : super(key: key); + const WithdrawalPasswordPage({super.key}); @override _WithdrawalPasswordPageState createState() => _WithdrawalPasswordPageState(); diff --git a/lib/account/page/withdrawal_record_list_page.dart b/lib/account/page/withdrawal_record_list_page.dart index 91d84abad..2af575d11 100644 --- a/lib/account/page/withdrawal_record_list_page.dart +++ b/lib/account/page/withdrawal_record_list_page.dart @@ -1,14 +1,13 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/widgets/my_app_bar.dart'; -import 'package:sticky_headers/sticky_headers.dart'; +import '../../order/page/order_page.dart'; /// design/6店铺-账户/index.html#artboard19 class WithdrawalRecordListPage extends StatefulWidget { - const WithdrawalRecordListPage({Key key}) : super(key: key); + const WithdrawalRecordListPage({super.key}); @override _WithdrawalRecordListPageState createState() => _WithdrawalRecordListPageState(); @@ -21,32 +20,75 @@ class _WithdrawalRecordListPageState extends State { appBar: const MyAppBar( title: '提现记录', ), - body: ListView.builder( - itemCount: 8, - itemBuilder: (_, index) { - return Semantics( - /// 将item默认合并的语义拆开,自行组合, 另一种方式见 account_record_list_page.dart - explicitChildNodes: true, - child: StickyHeader( - header: Container( - alignment: Alignment.centerLeft, - width: double.infinity, - color: ThemeUtils.getStickyHeaderColor(context), - padding: const EdgeInsets.only(left: 16.0), - height: 34.0, - child: Text('2021/06/0${index + 1}'), - ), - content: _buildItem(index), - ), - ); - }, + body: CustomScrollView( + slivers: [ + for (int i = 0; i < 8; i++) + _buildGroup(i) + ], ), ); } - - Widget _buildItem(int index) { - final list = List.generate(index + 1, (i) { - return Container( + + Widget _buildGroup(int index) { + return SliverMainAxisGroup( + slivers: [ + SliverPersistentHeader( + pinned: true, + delegate: SliverAppBarDelegate( + Container( + alignment: Alignment.centerLeft, + width: double.infinity, + color: ThemeUtils.getStickyHeaderColor(context), + padding: const EdgeInsets.only(left: 16.0), + child: Text('2021/06/0${index + 1}'), + ) + , 34.0, + ), + ), + SliverList( + delegate: SliverChildBuilderDelegate((_, index) { + return _buildItem(index); + }, + childCount: index + 1, + ), + ), + ], + ); + } + + Widget _buildItem(int i) { + final Widget content = Stack( + children: [ + Text(i.isEven ? '微信(唯鹿)' : '工商(尾号:4562 李一)'), + const Positioned( + top: 0.0, + right: 0.0, + child: Text('-10.00', style: TextStyles.textBold14), + ), + Positioned( + bottom: 0.0, + left: 0.0, + child: Text(i.isEven ? '12:40:20' : '12:50:20', style: Theme.of(context).textTheme.titleSmall), + ), + Positioned( + bottom: 0.0, + right: 0.0, + child: Text( + i.isEven ? '审核失败' : '待审核', + style: i.isEven ? TextStyle( + fontSize: Dimens.font_sp12, + color: Theme.of(context).colorScheme.error, + ) : const TextStyle( + fontSize: Dimens.font_sp12, + color: Colours.orange, + ), + ), + ), + ], + ); + + return MergeSemantics( + child: Container( height: 72.0, width: double.infinity, padding: const EdgeInsets.all(15.0), @@ -55,41 +97,8 @@ class _WithdrawalRecordListPageState extends State { bottom: Divider.createBorderSide(context, width: 0.8), ), ), - child: MergeSemantics( - child: Stack( - children: [ - Text(i.isEven ? '微信(唯鹿)' : '工商(尾号:4562 李一)'), - const Positioned( - top: 0.0, - right: 0.0, - child: Text('-10.00', style: TextStyles.textBold14), - ), - Positioned( - bottom: 0.0, - left: 0.0, - child: Text(i.isEven ? '12:40:20' : '12:50:20', style: Theme.of(context).textTheme.subtitle2), - ), - Positioned( - bottom: 0.0, - right: 0.0, - child: Text( - i.isEven ? '审核失败' : '待审核', - style: i.isEven ? TextStyle( - fontSize: Dimens.font_sp12, - color: Theme.of(context).errorColor - ) : const TextStyle( - fontSize: Dimens.font_sp12, - color: Color(0xFFFF8547) - ), - ), - ), - ], - ), - ), - ); - }); - return Column( - children: list + child: content, + ), ); } } diff --git a/lib/account/page/withdrawal_result_page.dart b/lib/account/page/withdrawal_result_page.dart index 8383a9840..313d62da5 100644 --- a/lib/account/page/withdrawal_result_page.dart +++ b/lib/account/page/withdrawal_result_page.dart @@ -1,15 +1,14 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/load_image.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; /// design/6店铺-账户/index.html#artboard5 class WithdrawalResultPage extends StatefulWidget { - const WithdrawalResultPage({Key key}) : super(key: key); + const WithdrawalResultPage({super.key}); @override _WithdrawalResultPageState createState() => _WithdrawalResultPageState(); @@ -25,7 +24,6 @@ class _WithdrawalResultPageState extends State { body: Padding( padding: const EdgeInsets.all(16.0), child: Column( - crossAxisAlignment: CrossAxisAlignment.center, children: [ Gaps.vGap50, const LoadAssetImage('account/sqsb', @@ -40,12 +38,12 @@ class _WithdrawalResultPageState extends State { Gaps.vGap8, Text( '2021-02-21 15:20:10', - style: Theme.of(context).textTheme.subtitle2, + style: Theme.of(context).textTheme.titleSmall, ), Gaps.vGap8, Text( '5秒后返回提现页面', - style: Theme.of(context).textTheme.subtitle2, + style: Theme.of(context).textTheme.titleSmall, ), Gaps.vGap24, MyButton( diff --git a/lib/account/widgets/rise_number_text.dart b/lib/account/widgets/rise_number_text.dart index 1bf2ee3cb..a19e8366e 100644 --- a/lib/account/widgets/rise_number_text.dart +++ b/lib/account/widgets/rise_number_text.dart @@ -1,17 +1,16 @@ - import 'package:flutter/material.dart'; // 简易实现数字滚动效果 class RiseNumberText extends StatefulWidget { const RiseNumberText(this.number,{ - Key key, + super.key, this.style, this.duration = 1200 - }): super(key: key); + }); final num number; - final TextStyle style; + final TextStyle? style; final int duration; @override @@ -20,8 +19,8 @@ class RiseNumberText extends StatefulWidget { class _RiseNumberTextState extends State with SingleTickerProviderStateMixin { - Animation _animation; - AnimationController _controller; + late Animation _animation; + late AnimationController _controller; num _fromNumber = 0; @override @@ -46,7 +45,7 @@ class _RiseNumberTextState extends State with SingleTickerProvid @override void dispose() { - _controller?.dispose(); + _controller.dispose(); super.dispose(); } @@ -57,12 +56,10 @@ class _RiseNumberTextState extends State with SingleTickerProvid builder: (_, __) { // 数字默认从0增长。数据变化时,由之前数字为基础变化。 return Text( - (_fromNumber + (_animation.value * (widget.number - _fromNumber))).toStringAsFixed(2).toString(), + (_fromNumber + (_animation.value * (widget.number - _fromNumber))).toStringAsFixed(2), style: widget.style, ); }, ); } } - - diff --git a/lib/account/widgets/sms_verify_dialog.dart b/lib/account/widgets/sms_verify_dialog.dart index 70be10395..b6cc15e58 100644 --- a/lib/account/widgets/sms_verify_dialog.dart +++ b/lib/account/widgets/sms_verify_dialog.dart @@ -1,4 +1,3 @@ - import 'dart:async'; import 'package:flutter/material.dart'; @@ -17,7 +16,7 @@ import 'package:flutter_deer/widgets/my_button.dart'; /// 骚操作:借腹生子 class SMSVerifyDialog extends StatefulWidget { - const SMSVerifyDialog({Key key}) : super(key: key); + const SMSVerifyDialog({super.key}); @override _SMSVerifyDialogState createState() => _SMSVerifyDialogState(); @@ -28,8 +27,8 @@ class _SMSVerifyDialogState extends State { /// 倒计时秒数 final int _second = 60; /// 当前秒数 - int _currentSecond; - StreamSubscription _subscription; + late int _currentSecond; + StreamSubscription? _subscription; bool _clickable = true; final FocusNode _focusNode = FocusNode(); diff --git a/lib/account/widgets/withdrawal_account_item.dart b/lib/account/widgets/withdrawal_account_item.dart index 11b8d5098..d420cbd06 100644 --- a/lib/account/widgets/withdrawal_account_item.dart +++ b/lib/account/widgets/withdrawal_account_item.dart @@ -1,4 +1,3 @@ - import 'dart:math'; import 'package:flutter/material.dart'; @@ -11,10 +10,10 @@ import 'package:flutter_deer/widgets/load_image.dart'; class WithdrawalAccountItem extends StatefulWidget { const WithdrawalAccountItem({ - Key key, - @required this.data, - this.onLongPress, - }): super(key: key); + super.key, + required this.data, + required this.onLongPress, + }); final WithdrawalAccountModel data; final GestureLongPressCallback onLongPress; @@ -26,8 +25,8 @@ class WithdrawalAccountItem extends StatefulWidget { /// 3D翻转动画 https://medium.com/flutterpub/flutter-flip-card-animation-with-3d-effect-4284af04f5a class _WithdrawalAccountItemState extends State with SingleTickerProviderStateMixin { - AnimationController _animationController; - Animation _animation; + late AnimationController _animationController; + late Animation _animation; AnimationStatus _animationStatus = AnimationStatus.dismissed; @override @@ -142,10 +141,10 @@ class _WithdrawalAccountItemState extends State with Sing class AccountCard extends StatefulWidget { const AccountCard({ - Key key, - @required this.child, - this.type - }): super(key: key); + super.key, + required this.child, + required this.type + }); final Widget child; final int type; @@ -164,10 +163,9 @@ class _AccountCardState extends State { borderRadius: BorderRadius.circular(8.0), boxShadow: context.isDark ? null : [ BoxShadow( - color: widget.type == 1 ? const Color(0x804EE07A) : const Color(0x805793FA), + color: widget.type == 1 ? const Color(0x804EE07A) : Colours.shadow_blue, offset: const Offset(0.0, 2.0), blurRadius: 8.0, - spreadRadius: 0.0, ), ], gradient: LinearGradient( diff --git a/lib/account/widgets/withdrawal_password_setting.dart b/lib/account/widgets/withdrawal_password_setting.dart index 205233ade..149f0eda8 100644 --- a/lib/account/widgets/withdrawal_password_setting.dart +++ b/lib/account/widgets/withdrawal_password_setting.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; @@ -12,7 +11,7 @@ import 'package:vibration/vibration.dart'; /// design/6店铺-账户/index.html#artboard13 class WithdrawalPasswordSetting extends StatefulWidget { - const WithdrawalPasswordSetting({Key key}) : super(key: key); + const WithdrawalPasswordSetting({super.key}); @override _WithdrawalPasswordSettingState createState() => _WithdrawalPasswordSettingState(); @@ -77,7 +76,7 @@ class _WithdrawalPasswordSettingState extends State { ), ), Gaps.vGap10, - Text('提现密码不可为连续、重复的数字。', style: Theme.of(context).textTheme.subtitle2), + Text('提现密码不可为连续、重复的数字。', style: Theme.of(context).textTheme.titleSmall), ], ), ), @@ -124,11 +123,6 @@ class _WithdrawalPasswordSettingState extends State { return; } - /// 点击时给予振动反馈 - if (!Device.isDesktop && await Vibration.hasVibrator()) { - Vibration.vibrate(duration: 10); - } - if (index == 11) { if (_index == 0) { return; @@ -157,6 +151,11 @@ class _WithdrawalPasswordSettingState extends State { setState(() { }); + + /// 点击时给予振动反馈 + if (!Device.isDesktop && await Vibration.hasVibrator()) { + Vibration.vibrate(duration: 10); + } }, ), ); diff --git a/lib/demo/demo_page.dart b/lib/demo/demo_page.dart index e4560ff4a..2751cfb7f 100644 --- a/lib/demo/demo_page.dart +++ b/lib/demo/demo_page.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_deer/demo/focus/focus_demo_page.dart'; +import 'package:flutter_deer/demo/lottie/lottie_demo.dart'; import 'package:flutter_deer/demo/navigator/books_main.dart'; import 'package:flutter_deer/demo/overlay/overlay_main.dart'; -import 'package:flutter_deer/demo/ripple/home_page.dart'; +import 'package:flutter_deer/demo/ripple/ripples_animation_page.dart'; import 'package:flutter_deer/demo/scratcher/scratch_card_demo_page.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/util/app_navigator.dart'; @@ -12,13 +14,23 @@ import 'package:flutter_deer/widgets/my_app_bar.dart'; class DemoPage extends StatefulWidget { - const DemoPage({Key key}) : super(key: key); + const DemoPage({super.key}); @override _DemoPageState createState() => _DemoPageState(); } class _DemoPageState extends State { + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + /// 显示状态栏和导航栏(使用QuickActions进入demo页) + SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]); + }); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -38,7 +50,7 @@ class _DemoPageState extends State { ), ClickItem( title: 'RipplesAnimation', - onTap: () => AppNavigator.push(context, const RipplesAnimation()), + onTap: () => AppNavigator.push(context, const RipplesAnimationPage()), ), ClickItem( title: 'Navigator 2.0', @@ -48,6 +60,10 @@ class _DemoPageState extends State { title: 'ScratchCard', onTap: () => AppNavigator.push(context, const ScratchCardDemoPage()), ), + ClickItem( + title: 'Lottie', + onTap: () => AppNavigator.push(context, const LottieDemo()), + ), ], ), ); diff --git a/lib/demo/focus/focus_demo_page.dart b/lib/demo/focus/focus_demo_page.dart index d5f694440..3119ca47a 100644 --- a/lib/demo/focus/focus_demo_page.dart +++ b/lib/demo/focus/focus_demo_page.dart @@ -5,7 +5,7 @@ import 'package:flutter_deer/util/theme_utils.dart'; /// 博客:https://weilu.blog.csdn.net/article/details/107132031 class FocusDemoPage extends StatefulWidget { - const FocusDemoPage({Key key, this.title}) : super(key: key); + const FocusDemoPage({super.key, required this.title}); final String title; @@ -26,7 +26,7 @@ class _FocusDemoPageState extends State { @override Widget build(BuildContext context) { - print('${widget.title} build'); + debugPrint('${widget.title} build'); return Scaffold( appBar: AppBar( backgroundColor: context.isDark ? Colours.dark_bg_color : Colors.blue, diff --git a/lib/demo/lottie/bunny.dart b/lib/demo/lottie/bunny.dart new file mode 100644 index 000000000..3557ddb71 --- /dev/null +++ b/lib/demo/lottie/bunny.dart @@ -0,0 +1,132 @@ + +import 'package:flutter/material.dart'; + +class Bunny { + + Bunny(this.controller) { + setNeutralState(); + } + + AnimationController controller; + + /// 各种状态过渡的起始帧数 + static const List _neutral_to_tracking = [4, 22]; + static const List _tracking_to_neutral = [0, 0]; + + static const List _neutral_to_shy = [29, 39]; + static const List _shy_to_neutral = [44, 54]; + + static const List _neutral_to_peek = [76, 68]; + static const List _peek_to_neutral = [68, 76]; + + static const List _shy_to_peek = [59, 68]; + static const List _peek_to_shy = [68, 59]; + + BunnyState currentState = BunnyState.neutral; + + void setNeutralState() { + switch(currentState) { + case BunnyState.neutral: + return; + case BunnyState.tracking: + setMinMaxFrame(_tracking_to_neutral); + break; + case BunnyState.shy: + setMinMaxFrame(_shy_to_neutral); + break; + case BunnyState.peek: + setMinMaxFrame(_peek_to_neutral); + break; + } + + currentState = BunnyState.neutral; + } + + void setShyState() { + switch(currentState) { + case BunnyState.neutral: + case BunnyState.tracking: + setMinMaxFrame(_neutral_to_shy); + break; + case BunnyState.shy: + return; + case BunnyState.peek: + setMinMaxFrame(_peek_to_shy); + break; + } + + currentState = BunnyState.shy; + } + + void setPeekState() { + switch(currentState) { + case BunnyState.neutral: + case BunnyState.tracking: + setMinMaxFrame(_neutral_to_peek); + break; + case BunnyState.shy: + setMinMaxFrame(_shy_to_peek); + break; + case BunnyState.peek: + return; + } + + currentState = BunnyState.peek; + } + + void setTrackingState() { + switch(currentState) { + case BunnyState.neutral: + setMinMaxFrame(_tracking_to_neutral); + break; + case BunnyState.tracking: + return; + case BunnyState.shy: + setMinMaxFrame(_shy_to_neutral); + break; + case BunnyState.peek: + setMinMaxFrame(_peek_to_neutral); + break; + } + + currentState = BunnyState.tracking; + } + + void setEyesPosition(double progress) { + if (currentState != BunnyState.tracking) { + setMinMaxFrame(_tracking_to_neutral); + currentState = BunnyState.tracking; + return; + } + if (progress > 1) { + return; + } + + final double frame = (_neutral_to_tracking[1] - _neutral_to_tracking[0]) * progress; + controller.animateTo(framesToPercentage(frame.toInt() + _neutral_to_tracking[0]), duration: Duration.zero); + } + + void setMinMaxFrame(List frames) { + /// 移动至起始帧 + controller.animateTo(framesToPercentage(frames[0]), duration: Duration.zero); + /// 动画至结束帧 + controller.animateTo(framesToPercentage(frames[1])); + } + + /// 共77帧。将已知帧数转为百分比 + double framesToPercentage(int frame) { + return frame / 77; + } + +} + +enum BunnyState { + /// 默认状态 + neutral, + /// 跟踪(文字输入) + tracking, + /// 害羞(密码不可见) + shy, + /// 偷看(密码可见) + peek +} diff --git a/lib/demo/lottie/lottie_demo.dart b/lib/demo/lottie/lottie_demo.dart new file mode 100644 index 000000000..f7bdbf02d --- /dev/null +++ b/lib/demo/lottie/lottie_demo.dart @@ -0,0 +1,236 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_deer/demo/lottie/bunny.dart'; +import 'package:lottie/lottie.dart'; + +/// Android版实现:https://github.com/omarsahl/Flopsy +/// 感谢Flopsy项目提供的思路及素材 +class LottieDemo extends StatefulWidget { + + const LottieDemo({super.key,}); + + @override + _LottieDemoState createState() => _LottieDemoState(); +} + +const Color _primaryColor = Color(0xFFFFBCBF); +const Color _backgroundColor = Color(0xFF37474F); +const Color _textColor = Color(0xFFCCCCCC); + +class _LottieDemoState extends State with TickerProviderStateMixin { + + late AnimationController _controller; + late Bunny _bunny; + + @override + void initState() { + super.initState(); + _controller = AnimationController(vsync: this); + _controller.stop(); + _bunny = Bunny(_controller); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + /// 屏幕宽度减去左右各16的padding,计算出输入框宽度。 + final double textFieldWidth = MediaQuery.of(context).size.width - 32; + + final Widget content = Scaffold( + appBar: AppBar( + systemOverlayStyle: SystemUiOverlayStyle.light, + backgroundColor: _backgroundColor, + title: const Text('Lottie Demo', style: TextStyle(color: _textColor),), + iconTheme: const IconThemeData(color: _textColor), + ), + backgroundColor: _backgroundColor, + body: SingleChildScrollView( + child: Column( + children: [ + const SizedBox(height: 32.0), + Lottie.asset( + 'assets/lottie/bunny_new_mouth.json', + width: 250, + height: 250, + controller: _controller, + fit: BoxFit.fill, + onLoaded: (composition) { + setState(() { + // 计算帧数 composition.endFrame - composition.startFrame; + /// 设置动画时长 + _controller.duration = composition.duration; + }); + }, + ), + _MyTextField( + labelText: 'Email', + keyboardType: TextInputType.emailAddress, + onHasFocus: (isObscure) { + /// 获取焦点,开始文字跟踪状态 + _bunny.setTrackingState(); + }, + onChanged: (text) { + /// 计算输入文字宽度占输入框宽度的比例 + _bunny.setEyesPosition(_getTextSize(text) / textFieldWidth); + }, + ), + _MyTextField( + labelText: 'Password', + keyboardType: TextInputType.visiblePassword, + obscureText: true, + onHasFocus: (isObscure) { + /// 获取焦点,设置状态 + if (isObscure) { + _bunny.setShyState(); + } else { + _bunny.setPeekState(); + } + }, + onObscureText: (isObscure) { + if (isObscure) { + _bunny.setShyState(); + } else { + _bunny.setPeekState(); + } + }, + ), + ], + ), + ), + ); + + return Theme( + data: ThemeData( + primaryColor: _primaryColor, + textSelectionTheme: TextSelectionThemeData( + selectionColor: _primaryColor.withAlpha(70), + selectionHandleColor: _primaryColor, // 覆盖`selectionHandleColor`不起作用 https://github.com/flutter/flutter/issues/74890 + cursorColor: _primaryColor, + ), + colorScheme: ColorScheme.fromSwatch().copyWith(secondary: _primaryColor), + ), + child: content, + ); + } + + /// 获取文字宽度 + double _getTextSize(String text) { + final TextPainter textPainter = TextPainter( + text: TextSpan(text: text, style: const TextStyle(fontSize: 16.0,)), + maxLines: 1, + textDirection: TextDirection.ltr, + ) + ..layout(); + return textPainter.size.width; + } +} + +class _MyTextField extends StatefulWidget { + + const _MyTextField({ + required this.labelText, + this.obscureText = false, + this.keyboardType, + this.onHasFocus, + this.onObscureText, + this.onChanged + }); + + final String labelText; + final bool obscureText; + final TextInputType? keyboardType; + /// 获取焦点监听 + final void Function(bool isObscure)? onHasFocus; + /// 密码可见监听 + final void Function(bool isObscure)? onObscureText; + /// 文字输入监听 + final void Function(String text)? onChanged; + + @override + _MyTextFieldState createState() => _MyTextFieldState(); +} + +class _MyTextFieldState extends State<_MyTextField> { + + bool _isObscure = true; + final FocusNode _focusNode = FocusNode(); + + @override + void initState() { + super.initState(); + _focusNode.addListener(_refresh); + } + + void _refresh() { + if (_focusNode.hasFocus && widget.onHasFocus != null) { + widget.onHasFocus?.call(_isObscure); + } + setState(() { + + }); + } + + @override + void dispose() { + _focusNode.removeListener(_refresh); + _focusNode.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Listener( + onPointerDown: (e) => FocusScope.of(context).requestFocus(_focusNode), + child: TextField( + focusNode: _focusNode, + style: const TextStyle( + color: _textColor, + fontSize: 16.0, + ), + textInputAction: TextInputAction.next, + decoration: InputDecoration( + labelText: widget.labelText, + labelStyle: TextStyle( + color: _focusNode.hasFocus ? _primaryColor : _textColor, + ), + contentPadding: const EdgeInsets.only(left: 8.0), + enabledBorder: const UnderlineInputBorder( + borderSide: BorderSide( + color: _textColor, + ), + ), + focusedBorder: const UnderlineInputBorder( + borderSide: BorderSide( + color: _primaryColor, + ), + ), + suffixIcon: widget.obscureText ? IconButton( + icon: Icon( + _isObscure ? Icons.visibility_off : Icons.visibility, + color: _focusNode.hasFocus ? _primaryColor : _textColor, + ), + onPressed: () { + setState(() { + _isObscure = !_isObscure; + }); + if (widget.onObscureText != null) { + widget.onObscureText?.call(_isObscure); + } + }, + ) : null, + ), + keyboardType: widget.keyboardType, + obscureText: widget.obscureText ? _isObscure : widget.obscureText, + onChanged: widget.onChanged, + ), + ), + ); + } +} diff --git a/lib/demo/navigator/book_entity.dart b/lib/demo/navigator/book_entity.dart index dfa16e586..3452ca37e 100644 --- a/lib/demo/navigator/book_entity.dart +++ b/lib/demo/navigator/book_entity.dart @@ -18,4 +18,4 @@ class BooksDetailsPath extends BookRoutePath { BooksDetailsPath(this.id); final int id; -} \ No newline at end of file +} diff --git a/lib/demo/navigator/books_app_state.dart b/lib/demo/navigator/books_app_state.dart index cfc2685e1..45f94bd44 100644 --- a/lib/demo/navigator/books_app_state.dart +++ b/lib/demo/navigator/books_app_state.dart @@ -5,9 +5,9 @@ class BooksAppState extends ChangeNotifier { BooksAppState() : _selectedIndex = 0; - int _selectedIndex; + late int _selectedIndex; - Book _selectedBook; + Book? _selectedBook; final List books = [ Book('Stranger in a Strange Land', 'Robert A. Heinlein'), @@ -28,9 +28,9 @@ class BooksAppState extends ChangeNotifier { notifyListeners(); } - Book get selectedBook => _selectedBook; + Book? get selectedBook => _selectedBook; - set selectedBook(Book book) { + set selectedBook(Book? book) { _selectedBook = book; notifyListeners(); } @@ -39,7 +39,7 @@ class BooksAppState extends ChangeNotifier { if (!books.contains(_selectedBook)) { return 0; } - return books.indexOf(_selectedBook); + return books.indexOf(_selectedBook!); } void setSelectedBookById(int id) { @@ -50,4 +50,4 @@ class BooksAppState extends ChangeNotifier { _selectedBook = books[id]; notifyListeners(); } -} \ No newline at end of file +} diff --git a/lib/demo/navigator/books_main.dart b/lib/demo/navigator/books_main.dart index 0132263e8..f5a64203f 100644 --- a/lib/demo/navigator/books_main.dart +++ b/lib/demo/navigator/books_main.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; -import 'package:flutter_deer/demo/navigator/parser/route_information_parser.dart'; import 'package:flutter_deer/demo/navigator/delegate/router_delegate.dart'; +import 'package:flutter_deer/demo/navigator/parser/route_information_parser.dart'; /// https://gist.github.com/johnpryan/bbca91e23bbb4d39247fa922533be7c9 /// https://weilu.blog.csdn.net/article/details/108902282 class NestedRouterDemo extends StatefulWidget { - const NestedRouterDemo({Key key}) : super(key: key); + const NestedRouterDemo({super.key}); @override _NestedRouterDemoState createState() => _NestedRouterDemoState(); @@ -25,4 +25,4 @@ class _NestedRouterDemoState extends State { routeInformationParser: _routeInformationParser, ); } -} \ No newline at end of file +} diff --git a/lib/demo/navigator/delegate/inner_router_delegate.dart b/lib/demo/navigator/delegate/inner_router_delegate.dart index 10020a459..f2ebb506a 100644 --- a/lib/demo/navigator/delegate/inner_router_delegate.dart +++ b/lib/demo/navigator/delegate/inner_router_delegate.dart @@ -38,7 +38,7 @@ class InnerRouterDelegate extends RouterDelegate if (appState.selectedBook != null) MaterialPage( key: ValueKey(appState.selectedBook), - child: BookDetailsScreen(book: appState.selectedBook), + child: BookDetailsScreen(book: appState.selectedBook!), ), ] else const FadeAnimationPage( @@ -69,12 +69,12 @@ class InnerRouterDelegate extends RouterDelegate class FadeAnimationPage extends Page { - const FadeAnimationPage({LocalKey key, this.child}) : super(key: key); + const FadeAnimationPage({super.key, required this.child}); final Widget child; @override - Route createRoute(BuildContext context) { + Route createRoute(BuildContext context) { return PageRouteBuilder( settings: this, pageBuilder: (context, animation, animation2) { diff --git a/lib/demo/navigator/delegate/router_delegate.dart b/lib/demo/navigator/delegate/router_delegate.dart index 7708b4b42..97661e413 100644 --- a/lib/demo/navigator/delegate/router_delegate.dart +++ b/lib/demo/navigator/delegate/router_delegate.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; -import 'package:flutter_deer/demo/navigator/screen/app_shell.dart'; import 'package:flutter_deer/demo/navigator/book_entity.dart'; import 'package:flutter_deer/demo/navigator/books_app_state.dart'; +import 'package:flutter_deer/demo/navigator/screen/app_shell.dart'; class BookRouterDelegate extends RouterDelegate with ChangeNotifier, PopNavigatorRouterDelegateMixin { @@ -62,4 +62,4 @@ class BookRouterDelegate extends RouterDelegate appState.setSelectedBookById(configuration.id); } } -} \ No newline at end of file +} diff --git a/lib/demo/navigator/parser/route_information_parser.dart b/lib/demo/navigator/parser/route_information_parser.dart index 8814fca38..8475cef2f 100644 --- a/lib/demo/navigator/parser/route_information_parser.dart +++ b/lib/demo/navigator/parser/route_information_parser.dart @@ -6,14 +6,14 @@ class BookRouteInformationParser extends RouteInformationParser { @override Future parseRouteInformation( RouteInformation routeInformation) async { - final uri = Uri.parse(routeInformation.location); + final uri = routeInformation.uri; if (uri.pathSegments.isNotEmpty && uri.pathSegments.first == 'settings') { return BooksSettingsPath(); } else { if (uri.pathSegments.length >= 2) { if (uri.pathSegments[0] == 'book') { - return BooksDetailsPath(int.tryParse(uri.pathSegments[1])); + return BooksDetailsPath(int.tryParse(uri.pathSegments[1])!); } } return BooksListPath(); @@ -21,16 +21,16 @@ class BookRouteInformationParser extends RouteInformationParser { } @override - RouteInformation restoreRouteInformation(BookRoutePath configuration) { + RouteInformation? restoreRouteInformation(BookRoutePath configuration) { if (configuration is BooksListPath) { - return const RouteInformation(location: '/home'); + return RouteInformation(uri: Uri.parse('/home')); } if (configuration is BooksSettingsPath) { - return const RouteInformation(location: '/settings'); + return RouteInformation(uri: Uri.parse('/settings')); } if (configuration is BooksDetailsPath) { - return RouteInformation(location: '/book/${configuration.id}'); + return RouteInformation(uri: Uri.parse('/book/${configuration.id}')); } return null; } -} \ No newline at end of file +} diff --git a/lib/demo/navigator/screen/app_shell.dart b/lib/demo/navigator/screen/app_shell.dart index 777abcf99..da454c1bf 100644 --- a/lib/demo/navigator/screen/app_shell.dart +++ b/lib/demo/navigator/screen/app_shell.dart @@ -6,9 +6,9 @@ import 'package:flutter_deer/demo/navigator/delegate/inner_router_delegate.dart' class AppShell extends StatefulWidget { const AppShell({ - Key key, - @required this.appState, - }): super(key: key); + super.key, + required this.appState, + }); final BooksAppState appState; @@ -17,8 +17,8 @@ class AppShell extends StatefulWidget { } class _AppShellState extends State { - InnerRouterDelegate _routerDelegate; - ChildBackButtonDispatcher _backButtonDispatcher; + late InnerRouterDelegate _routerDelegate; + late ChildBackButtonDispatcher _backButtonDispatcher; @override void initState() { @@ -37,7 +37,7 @@ class _AppShellState extends State { super.didChangeDependencies(); // Defer back button dispatching to the child router _backButtonDispatcher = Router.of(context) - .backButtonDispatcher + .backButtonDispatcher! .createChildBackButtonDispatcher(); } @@ -70,4 +70,4 @@ class _AppShellState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/demo/navigator/screen/book_details_screen.dart b/lib/demo/navigator/screen/book_details_screen.dart index abe2dd217..1c44aa930 100644 --- a/lib/demo/navigator/screen/book_details_screen.dart +++ b/lib/demo/navigator/screen/book_details_screen.dart @@ -4,9 +4,9 @@ import 'package:flutter_deer/demo/navigator/book_entity.dart'; class BookDetailsScreen extends StatelessWidget { const BookDetailsScreen({ - Key key, - @required this.book, - }): super(key: key); + super.key, + required this.book, + }); final Book book; @@ -24,13 +24,11 @@ class BookDetailsScreen extends StatelessWidget { }, child: const Text('Back'), ), - if (book != null) ...[ - Text(book.title, style: Theme.of(context).textTheme.headline6), - Text(book.author, style: Theme.of(context).textTheme.subtitle1), - ], + Text(book.title, style: Theme.of(context).textTheme.titleLarge), + Text(book.author, style: Theme.of(context).textTheme.titleMedium), ], ), ), ); } -} \ No newline at end of file +} diff --git a/lib/demo/navigator/screen/books_list_screen.dart b/lib/demo/navigator/screen/books_list_screen.dart index cde1821f5..d04c11e9b 100644 --- a/lib/demo/navigator/screen/books_list_screen.dart +++ b/lib/demo/navigator/screen/books_list_screen.dart @@ -4,10 +4,10 @@ import 'package:flutter_deer/demo/navigator/book_entity.dart'; class BooksListScreen extends StatelessWidget { const BooksListScreen({ - Key key, - @required this.books, - @required this.onTapped, - }): super(key: key); + super.key, + required this.books, + required this.onTapped, + }); final List books; final ValueChanged onTapped; @@ -17,7 +17,7 @@ class BooksListScreen extends StatelessWidget { return Scaffold( body: ListView( children: [ - for (var book in books) + for (final book in books) ListTile( title: Text(book.title), subtitle: Text(book.author), @@ -27,4 +27,4 @@ class BooksListScreen extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/demo/navigator/screen/setting_screen.dart b/lib/demo/navigator/screen/setting_screen.dart index 564426e09..a8bd4a361 100644 --- a/lib/demo/navigator/screen/setting_screen.dart +++ b/lib/demo/navigator/screen/setting_screen.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; class SettingsScreen extends StatelessWidget { - const SettingsScreen({Key key}) : super(key: key); + const SettingsScreen({super.key}); @override Widget build(BuildContext context) { @@ -12,4 +12,4 @@ class SettingsScreen extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/demo/overlay/bottom_navigation/my_bottom_navigation_bar.dart b/lib/demo/overlay/bottom_navigation/my_bottom_navigation_bar.dart index 6dda31afe..06d5a0d15 100644 --- a/lib/demo/overlay/bottom_navigation/my_bottom_navigation_bar.dart +++ b/lib/demo/overlay/bottom_navigation/my_bottom_navigation_bar.dart @@ -4,16 +4,16 @@ import 'package:flutter/material.dart'; class MyBottomNavigationBar extends StatefulWidget { const MyBottomNavigationBar({ - Key key, + super.key, this.selectedPosition = 0, this.isShowIndicator = true, - @required this.selectedCallback, - }) : super(key: key); + required this.selectedCallback, + }); /// 选中下标 final int selectedPosition; final bool isShowIndicator; - final Function(int selectedPosition) selectedCallback; + final void Function(int selectedPosition) selectedCallback; @override _MyBottomNavigationBarState createState() => _MyBottomNavigationBarState(); @@ -42,14 +42,14 @@ class _MyBottomNavigationBarState extends State with Tick double itemWidth = 0; - AnimationController controller; - Animation animation; + late AnimationController controller; + late Animation animation; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { - itemWidth = (context.size.width - barHeight) / 3; + itemWidth = (context.size!.width - barHeight) / 3; setState(() {}); }); @@ -75,7 +75,7 @@ class _MyBottomNavigationBarState extends State with Tick color: Colors.white, borderRadius: BorderRadius.circular(barHeight / 2), boxShadow: const [ - BoxShadow(color: Colors.grey, offset: Offset(0.0, 1.0), blurRadius: 4.0, spreadRadius: 0.0), + BoxShadow(color: Colors.grey, offset: Offset(0.0, 1.0), blurRadius: 4.0), ], ), ); @@ -89,6 +89,8 @@ class _MyBottomNavigationBarState extends State with Tick if (widget.isShowIndicator) { /// 指示器 children.add(Positioned( + left: 6.0 + animation.value * itemWidth, + top: (barHeight - indicatorHeight) / 2, child: Container( width: indicatorHeight, height: indicatorHeight, @@ -96,12 +98,10 @@ class _MyBottomNavigationBarState extends State with Tick shape: BoxShape.circle, color: Colors.white, boxShadow: [ - BoxShadow(color: Colors.grey, offset: Offset(0.0, 0.0), blurRadius: 1.0, spreadRadius: 0.0), + BoxShadow(color: Colors.grey, blurRadius: 1.0), ], ), ), - left: 6.0 + animation.value * itemWidth, - top: (barHeight - indicatorHeight) / 2, )); } @@ -114,6 +114,7 @@ class _MyBottomNavigationBarState extends State with Tick ); children.add(Positioned.fromRect( + rect: rect, child: GestureDetector( child: Container( decoration: BoxDecoration( @@ -126,7 +127,6 @@ class _MyBottomNavigationBarState extends State with Tick _selectedPosition(i); }, ), - rect: rect, )); } @@ -148,9 +148,7 @@ class _MyBottomNavigationBarState extends State with Tick }); controller.forward(from: 0.0); - if (widget.selectedCallback != null) { - widget.selectedCallback(selectedPosition); - } + widget.selectedCallback(selectedPosition); } @override diff --git a/lib/demo/overlay/overlay_main.dart b/lib/demo/overlay/overlay_main.dart index 0a4ccc380..739ddd3f3 100644 --- a/lib/demo/overlay/overlay_main.dart +++ b/lib/demo/overlay/overlay_main.dart @@ -6,7 +6,7 @@ import 'package:flutter_deer/demo/overlay/route/my_navigator_observer.dart'; class OverlayDemo extends StatelessWidget { - OverlayDemo({Key key}): super(key: key) { + OverlayDemo({super.key}) { Application.navigatorObserver = MyNavigatorObserver(); } // This widget is the root of your application. @@ -23,4 +23,4 @@ class OverlayDemo extends StatelessWidget { ], ); } -} \ No newline at end of file +} diff --git a/lib/demo/overlay/page/overlay_demo_page.dart b/lib/demo/overlay/page/overlay_demo_page.dart index 4958b7576..11839c734 100644 --- a/lib/demo/overlay/page/overlay_demo_page.dart +++ b/lib/demo/overlay/page/overlay_demo_page.dart @@ -11,7 +11,7 @@ import 'package:flutter_deer/demo/overlay/route/application.dart'; /// 本例包含自定义BottomNavigationBar,路由监听及Overlay悬浮用法。 class OverlayDemoPage extends StatefulWidget { - const OverlayDemoPage({Key key}) : super(key: key); + const OverlayDemoPage({super.key}); @override _OverlayDemoPageState createState() => _OverlayDemoPageState(); @@ -19,7 +19,7 @@ class OverlayDemoPage extends StatefulWidget { class _OverlayDemoPageState extends State { - OverlayEntry _overlayEntry; + OverlayEntry? _overlayEntry; @override void initState() { @@ -29,7 +29,7 @@ class _OverlayDemoPageState extends State { builder: (context) => _buildBottomNavigation(context), ); /// 添加悬浮 - Overlay.of(context).insert(_overlayEntry); + Overlay.of(context).insert(_overlayEntry!); }); } @@ -47,7 +47,7 @@ class _OverlayDemoPageState extends State { appBar: AppBar( title: const Text('Overlay Demo'), ), - body: Container( + body: ColoredBox( color: Colors.amber, child: Center( child: GestureDetector( @@ -83,9 +83,10 @@ class _OverlayDemoPageState extends State { isShowIndicator: Application.navigatorObserver.list.isEmpty, selectedCallback: (position) { /// 返回主页 - Application.navigatorObserver.list.forEach((route) { + void removeRoute(Route route) { Navigator.removeRoute(context, route); - }); + } + Application.navigatorObserver.list.forEach(removeRoute); /// 手动清空 Application.navigatorObserver.list = []; }, diff --git a/lib/demo/overlay/page/test_page.dart b/lib/demo/overlay/page/test_page.dart index 2e33c72aa..851b336c3 100644 --- a/lib/demo/overlay/page/test_page.dart +++ b/lib/demo/overlay/page/test_page.dart @@ -4,7 +4,7 @@ import 'package:flutter_deer/demo/widgets/neumorphic.dart'; class TestPage extends StatefulWidget { - const TestPage({Key key}) : super(key: key); + const TestPage({super.key}); @override _TestPageState createState() => _TestPageState(); @@ -21,7 +21,26 @@ class _TestPageState extends State { body: Center( child: NeumorphicContainer( child: GestureDetector( - child: const Text('点击跳转', style: TextStyle(fontSize: 18.0, color: Colors.white70),), + child: Text( + '点击跳转', + style: TextStyle( + fontSize: 18.0, + color: Colors.white, + fontWeight: FontWeight.bold, + shadows: [ + const Shadow( + offset: Offset(3, 3), + color: Colors.black38, + blurRadius: 10, + ), + Shadow( + offset: const Offset(-3, -3), + color: Colors.white.withOpacity(0.85), + blurRadius: 10, + ) + ], + ), + ), onTap: () { Navigator.push( context, diff --git a/lib/demo/overlay/route/application.dart b/lib/demo/overlay/route/application.dart index fbfe84d45..8f584cbc5 100644 --- a/lib/demo/overlay/route/application.dart +++ b/lib/demo/overlay/route/application.dart @@ -3,5 +3,5 @@ import 'package:flutter_deer/demo/overlay/route/my_navigator_observer.dart'; class Application { - static MyNavigatorObserver navigatorObserver; + static late MyNavigatorObserver navigatorObserver; } diff --git a/lib/demo/overlay/route/my_navigator_observer.dart b/lib/demo/overlay/route/my_navigator_observer.dart index e778d0255..16b572862 100644 --- a/lib/demo/overlay/route/my_navigator_observer.dart +++ b/lib/demo/overlay/route/my_navigator_observer.dart @@ -6,18 +6,18 @@ class MyNavigatorObserver extends NavigatorObserver { List> list = []; @override - void didPush(Route route, Route previousRoute) { + void didPush(Route route, Route? previousRoute) { /// 首页不添加 if (route.settings.name != '/') { list.add(route); - print(list.length); + debugPrint(list.length.toString()); } } @override - void didPop(Route route, Route previousRoute) { + void didPop(Route route, Route? previousRoute) { list.remove(route); - print(list.length); + debugPrint(list.length.toString()); } } diff --git a/lib/demo/ripple/home_page.dart b/lib/demo/ripple/ripples_animation_page.dart similarity index 87% rename from lib/demo/ripple/home_page.dart rename to lib/demo/ripple/ripples_animation_page.dart index 46103ba8e..1d4fd1ca2 100644 --- a/lib/demo/ripple/home_page.dart +++ b/lib/demo/ripple/ripples_animation_page.dart @@ -5,12 +5,12 @@ import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/util/theme_utils.dart'; /// https://medium.com/flutterdevs/ripple-animation-in-flutter-3421cbd66a18 -class RipplesAnimation extends StatefulWidget { - const RipplesAnimation({ - Key key, +class RipplesAnimationPage extends StatefulWidget { + const RipplesAnimationPage({ + super.key, this.size = 80.0, this.color = Colors.red, - }) : super(key: key); + }); final double size; final Color color; @@ -19,9 +19,9 @@ class RipplesAnimation extends StatefulWidget { _RipplesAnimationState createState() => _RipplesAnimationState(); } -class _RipplesAnimationState extends State with TickerProviderStateMixin { +class _RipplesAnimationState extends State with TickerProviderStateMixin { - AnimationController _controller; + late AnimationController _controller; @override void initState() { @@ -47,7 +47,7 @@ class _RipplesAnimationState extends State with TickerProvider gradient: RadialGradient( colors: [ widget.color, - Color.lerp(widget.color, Colors.black, .05) + Color.lerp(widget.color, Colors.black, .05)! ], ), ), @@ -92,19 +92,18 @@ class _RipplesAnimationState extends State with TickerProvider class CirclePainter extends CustomPainter { CirclePainter(this._animation, { - @required this.color, + required this.color, }) : super(repaint: _animation); final Color color; final Animation _animation; void circle(Canvas canvas, Rect rect, double value) { - final double opacity = (1.0 - (value / 4.0)).clamp(0.0, 1.0) as double; - final Color _color = color.withOpacity(opacity); + final double opacity = (1.0 - (value / 4.0)).clamp(0.0, 1.0); final double size = rect.width / 2; final double area = size * size; final double radius = math.sqrt(area * value / 4); - final Paint paint = Paint()..color = _color; + final Paint paint = Paint()..color = color.withOpacity(opacity); canvas.drawCircle(rect.center, radius, paint); } @@ -129,4 +128,4 @@ class PulsateCurve extends Curve { } return math.sin(t * math.pi); } -} \ No newline at end of file +} diff --git a/lib/demo/scratcher/scratch_card_demo_page.dart b/lib/demo/scratcher/scratch_card_demo_page.dart index 316c1f4d5..a84baed5e 100755 --- a/lib/demo/scratcher/scratch_card_demo_page.dart +++ b/lib/demo/scratcher/scratch_card_demo_page.dart @@ -8,7 +8,7 @@ import 'package:scratcher/scratcher.dart'; class ScratchCardDemoPage extends StatefulWidget { - const ScratchCardDemoPage({Key key}) : super(key: key); + const ScratchCardDemoPage({super.key}); @override _ScratchCardDemoPageState createState() => _ScratchCardDemoPageState(); @@ -33,11 +33,11 @@ class _ScratchCardDemoPageState extends State { brushSize: 20, threshold: 50, color: Colors.grey, - onChange: (value) => print('Scratch progress: ${value.toStringAsFixed(2)}%'), + onChange: (value) => debugPrint('Scratch progress: ${value.toStringAsFixed(2)}%'), onThreshold: () { /// 这里设置刮开50%,就揭开所有。 - print('Threshold reached!'); - scratchKey.currentState.reveal( + debugPrint('Threshold reached!'); + scratchKey.currentState!.reveal( duration: const Duration(milliseconds: 1000), ); }, @@ -56,7 +56,7 @@ class _ScratchCardDemoPageState extends State { OutlinedButton( child: const Text('Reset'), onPressed: () { - scratchKey.currentState.reset( + scratchKey.currentState!.reset( duration: const Duration(milliseconds: 2000), ); }, @@ -64,7 +64,7 @@ class _ScratchCardDemoPageState extends State { ElevatedButton( child: const Text('Reveal'), onPressed: () { - scratchKey.currentState.reveal( + scratchKey.currentState!.reveal( duration: const Duration(milliseconds: 2000), ); }, diff --git a/lib/demo/widgets/neumorphic.dart b/lib/demo/widgets/neumorphic.dart index 8babd53c3..7b90c52d9 100644 --- a/lib/demo/widgets/neumorphic.dart +++ b/lib/demo/widgets/neumorphic.dart @@ -6,17 +6,16 @@ import 'package:flutter/material.dart'; class NeumorphicContainer extends StatefulWidget { NeumorphicContainer({ - Key key, - this.child, + super.key, + required this.child, this.bevel = 10.0, this.color, - }) : blurOffset = Offset(bevel / 2, bevel / 2), - super(key: key); + }) : blurOffset = Offset(bevel / 2, bevel / 2); final Widget child; final double bevel; final Offset blurOffset; - final Color color; + final Color? color; @override _NeumorphicContainerState createState() => _NeumorphicContainerState(); @@ -81,6 +80,6 @@ class _NeumorphicContainerState extends State { extension ColorUtils on Color { Color mix(Color another, double amount) { - return Color.lerp(this, another, amount); + return Color.lerp(this, another, amount)!; } -} \ No newline at end of file +} diff --git a/lib/generated/json/bank_entity.g.dart b/lib/generated/json/bank_entity.g.dart new file mode 100644 index 000000000..573b6e9c5 --- /dev/null +++ b/lib/generated/json/bank_entity.g.dart @@ -0,0 +1,29 @@ +import 'package:flutter_deer/generated/json/base/json_convert_content.dart'; +import 'package:flutter_deer/account/models/bank_entity.dart'; +import 'package:azlistview/azlistview.dart'; + + +BankEntity $BankEntityFromJson(Map json) { + final BankEntity bankEntity = BankEntity(); + final int? id = jsonConvert.convert(json['id']); + if (id != null) { + bankEntity.id = id; + } + final String? bankName = jsonConvert.convert(json['bankName']); + if (bankName != null) { + bankEntity.bankName = bankName; + } + final String? firstLetter = jsonConvert.convert(json['firstLetter']); + if (firstLetter != null) { + bankEntity.firstLetter = firstLetter; + } + return bankEntity; +} + +Map $BankEntityToJson(BankEntity entity) { + final Map data = {}; + data['id'] = entity.id; + data['bankName'] = entity.bankName; + data['firstLetter'] = entity.firstLetter; + return data; +} \ No newline at end of file diff --git a/lib/generated/json/bank_entity_helper.dart b/lib/generated/json/bank_entity_helper.dart deleted file mode 100644 index 1c451a914..000000000 --- a/lib/generated/json/bank_entity_helper.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter_deer/account/models/bank_entity.dart'; - -bankEntityFromJson(BankEntity data, Map json) { - if (json['id'] != null) { - data.id = json['id']?.toInt(); - } - if (json['bankName'] != null) { - data.bankName = json['bankName']?.toString(); - } - if (json['firstLetter'] != null) { - data.firstLetter = json['firstLetter']?.toString(); - } - return data; -} - -Map bankEntityToJson(BankEntity entity) { - final Map data = new Map(); - data['id'] = entity.id; - data['bankName'] = entity.bankName; - data['firstLetter'] = entity.firstLetter; - return data; -} \ No newline at end of file diff --git a/lib/generated/json/base/json_convert_content.dart b/lib/generated/json/base/json_convert_content.dart index 4529e2655..4ddbc5eff 100644 --- a/lib/generated/json/base/json_convert_content.dart +++ b/lib/generated/json/base/json_convert_content.dart @@ -4,76 +4,155 @@ // This file is automatically generated. DO NOT EDIT, all your changes would be lost. import 'package:flutter_deer/account/models/bank_entity.dart'; -import 'package:flutter_deer/generated/json/bank_entity_helper.dart'; +import 'package:flutter_deer/generated/json/bank_entity.g.dart'; import 'package:flutter_deer/account/models/city_entity.dart'; -import 'package:flutter_deer/generated/json/city_entity_helper.dart'; -import 'package:flutter_deer/shop/models/user_entity.dart'; -import 'package:flutter_deer/generated/json/user_entity_helper.dart'; +import 'package:flutter_deer/generated/json/city_entity.g.dart'; +import 'package:flutter_deer/goods/models/goods_sort_entity.dart'; +import 'package:flutter_deer/generated/json/goods_sort_entity.g.dart'; import 'package:flutter_deer/order/models/search_entity.dart'; -import 'package:flutter_deer/generated/json/search_entity_helper.dart'; +import 'package:flutter_deer/generated/json/search_entity.g.dart'; +import 'package:flutter_deer/shop/models/user_entity.dart'; +import 'package:flutter_deer/generated/json/user_entity.g.dart'; -class JsonConvert { - T fromJson(Map json) { - return _getFromJson(runtimeType, this, json); - } +JsonConvert jsonConvert = JsonConvert(); - Map toJson() { - return _getToJson(runtimeType, this); - } +class JsonConvert { - static _getFromJson(Type type, data, json) { - switch (type) { case BankEntity: - return bankEntityFromJson(data as BankEntity, json) as T; case CityEntity: - return cityEntityFromJson(data as CityEntity, json) as T; case UserEntity: - return userEntityFromJson(data as UserEntity, json) as T; case SearchEntity: - return searchEntityFromJson(data as SearchEntity, json) as T; case SearchItem: - return searchItemFromJson(data as SearchItem, json) as T; } - return data as T; - } - - static _getToJson(Type type, data) { - switch (type) { case BankEntity: - return bankEntityToJson(data as BankEntity); case CityEntity: - return cityEntityToJson(data as CityEntity); case UserEntity: - return userEntityToJson(data as UserEntity); case SearchEntity: - return searchEntityToJson(data as SearchEntity); case SearchItem: - return searchItemToJson(data as SearchItem); } - return data as T; - } - //Go back to a single instance by type - static _fromJsonSingle(String type, json) { - switch (type) { case 'BankEntity': - return BankEntity().fromJson(json); case 'CityEntity': - return CityEntity().fromJson(json); case 'UserEntity': - return UserEntity().fromJson(json); case 'SearchEntity': - return SearchEntity().fromJson(json); case 'SearchItem': - return SearchItem().fromJson(json); } - return null; + T? convert(dynamic value) { + if (value == null) { + return null; + } + return asT(value); } - //empty list is returned by type - static _getListFromType(String type) { - switch (type) { case 'BankEntity': - return List(); case 'CityEntity': - return List(); case 'UserEntity': - return List(); case 'SearchEntity': - return List(); case 'SearchItem': - return List(); } - return null; + List? convertList(List? value) { + if (value == null) { + return null; + } + try { + return value.map((dynamic e) => asT(e)).toList(); + } catch (e, stackTrace) { + print('asT<$T> $e $stackTrace'); + return []; + } } - static M fromJsonAsT(json) { - String type = M.toString(); - if (json is List && type.contains("List<")) { - String itemType = type.substring(5, type.length - 1); - List tempList = _getListFromType(itemType); - json.forEach((itemJson) { - tempList - .add(_fromJsonSingle(type.substring(5, type.length - 1), itemJson)); - }); - return tempList as M; - } else { - return _fromJsonSingle(M.toString(), json) as M; + List? convertListNotNull(dynamic value) { + if (value == null) { + return null; + } + try { + return (value as List).map((dynamic e) => asT(e)!).toList(); + } catch (e, stackTrace) { + print('asT<$T> $e $stackTrace'); + return []; } } + T? asT(dynamic value) { + if (value is T) { + return value; + } + final String type = T.toString(); + try { + final String valueS = value.toString(); + if (type == "String") { + return valueS as T; + } else if (type == "int") { + final int? intValue = int.tryParse(valueS); + if (intValue == null) { + return double.tryParse(valueS)?.toInt() as T?; + } else { + return intValue as T; + } } else if (type == "double") { + return double.parse(valueS) as T; + } else if (type == "DateTime") { + return DateTime.parse(valueS) as T; + } else if (type == "bool") { + if (valueS == '0' || valueS == '1') { + return (valueS == '1') as T; + } + return (valueS == 'true') as T; + } else { + return JsonConvert.fromJsonAsT(value); + } + } catch (e, stackTrace) { + print('asT<$T> $e $stackTrace'); + return null; + } + } + //Go back to a single instance by type + static M? _fromJsonSingle(Map json) { + final String type = M.toString(); + if(type == (BankEntity).toString()){ + return BankEntity.fromJson(json) as M; + } + if(type == (CityEntity).toString()){ + return CityEntity.fromJson(json) as M; + } + if(type == (GoodsSortEntity).toString()){ + return GoodsSortEntity.fromJson(json) as M; + } + if(type == (SearchEntity).toString()){ + return SearchEntity.fromJson(json) as M; + } + if(type == (SearchItems).toString()){ + return SearchItems.fromJson(json) as M; + } + if(type == (SearchItemsOwner).toString()){ + return SearchItemsOwner.fromJson(json) as M; + } + if(type == (SearchItemsLicense).toString()){ + return SearchItemsLicense.fromJson(json) as M; + } + if(type == (UserEntity).toString()){ + return UserEntity.fromJson(json) as M; + } + + print("$type not found"); + + return null; +} + + //list is returned by type + static M? _getListChildType(List data) { + if([] is M){ + return data.map((e) => BankEntity.fromJson(e)).toList() as M; + } + if([] is M){ + return data.map((e) => CityEntity.fromJson(e)).toList() as M; + } + if([] is M){ + return data.map((e) => GoodsSortEntity.fromJson(e)).toList() as M; + } + if([] is M){ + return data.map((e) => SearchEntity.fromJson(e)).toList() as M; + } + if([] is M){ + return data.map((e) => SearchItems.fromJson(e)).toList() as M; + } + if([] is M){ + return data.map((e) => SearchItemsOwner.fromJson(e)).toList() as M; + } + if([] is M){ + return data.map((e) => SearchItemsLicense.fromJson(e)).toList() as M; + } + if([] is M){ + return data.map((e) => UserEntity.fromJson(e)).toList() as M; + } + + print("${M.toString()} not found"); + + return null; +} + + static M? fromJsonAsT(dynamic json) { + if(json == null){ + return null; + } + if (json is List) { + return _getListChildType(json); + } else { + return _fromJsonSingle(json as Map); + } + } } \ No newline at end of file diff --git a/lib/generated/json/base/json_filed.dart b/lib/generated/json/base/json_field.dart similarity index 62% rename from lib/generated/json/base/json_filed.dart rename to lib/generated/json/base/json_field.dart index 3c7431347..260ab1421 100644 --- a/lib/generated/json/base/json_filed.dart +++ b/lib/generated/json/base/json_field.dart @@ -4,18 +4,19 @@ // This file is automatically generated. DO NOT EDIT, all your changes would be lost. +class JsonSerializable{ + const JsonSerializable(); +} + class JSONField { //Specify the parse field name - final String name; - - //Specify the time resolution format - final String format; + final String? name; //Whether to participate in toJson - final bool serialize; + final bool? serialize; //Whether to participate in fromMap - final bool deserialize; + final bool? deserialize; - const JSONField({this.name, this.format, this.serialize, this.deserialize}); + const JSONField({this.name, this.serialize, this.deserialize}); } diff --git a/lib/generated/json/city_entity.g.dart b/lib/generated/json/city_entity.g.dart new file mode 100644 index 000000000..b2b1f3530 --- /dev/null +++ b/lib/generated/json/city_entity.g.dart @@ -0,0 +1,29 @@ +import 'package:flutter_deer/generated/json/base/json_convert_content.dart'; +import 'package:flutter_deer/account/models/city_entity.dart'; +import 'package:azlistview/azlistview.dart'; + + +CityEntity $CityEntityFromJson(Map json) { + final CityEntity cityEntity = CityEntity(); + final String? name = jsonConvert.convert(json['name']); + if (name != null) { + cityEntity.name = name; + } + final String? cityCode = jsonConvert.convert(json['cityCode']); + if (cityCode != null) { + cityEntity.cityCode = cityCode; + } + final String? firstCharacter = jsonConvert.convert(json['firstCharacter']); + if (firstCharacter != null) { + cityEntity.firstCharacter = firstCharacter; + } + return cityEntity; +} + +Map $CityEntityToJson(CityEntity entity) { + final Map data = {}; + data['name'] = entity.name; + data['cityCode'] = entity.cityCode; + data['firstCharacter'] = entity.firstCharacter; + return data; +} \ No newline at end of file diff --git a/lib/generated/json/city_entity_helper.dart b/lib/generated/json/city_entity_helper.dart deleted file mode 100644 index 6141a1c4a..000000000 --- a/lib/generated/json/city_entity_helper.dart +++ /dev/null @@ -1,22 +0,0 @@ -import 'package:flutter_deer/account/models/city_entity.dart'; - -cityEntityFromJson(CityEntity data, Map json) { - if (json['name'] != null) { - data.name = json['name']?.toString(); - } - if (json['cityCode'] != null) { - data.cityCode = json['cityCode']?.toString(); - } - if (json['firstCharacter'] != null) { - data.firstCharacter = json['firstCharacter']?.toString(); - } - return data; -} - -Map cityEntityToJson(CityEntity entity) { - final Map data = new Map(); - data['name'] = entity.name; - data['cityCode'] = entity.cityCode; - data['firstCharacter'] = entity.firstCharacter; - return data; -} \ No newline at end of file diff --git a/lib/generated/json/goods_sort_entity.g.dart b/lib/generated/json/goods_sort_entity.g.dart new file mode 100644 index 000000000..983ca7d11 --- /dev/null +++ b/lib/generated/json/goods_sort_entity.g.dart @@ -0,0 +1,22 @@ +import 'package:flutter_deer/generated/json/base/json_convert_content.dart'; +import 'package:flutter_deer/goods/models/goods_sort_entity.dart'; + +GoodsSortEntity $GoodsSortEntityFromJson(Map json) { + final GoodsSortEntity goodsSortEntity = GoodsSortEntity(); + final String? id = jsonConvert.convert(json['id']); + if (id != null) { + goodsSortEntity.id = id; + } + final String? name = jsonConvert.convert(json['name']); + if (name != null) { + goodsSortEntity.name = name; + } + return goodsSortEntity; +} + +Map $GoodsSortEntityToJson(GoodsSortEntity entity) { + final Map data = {}; + data['id'] = entity.id; + data['name'] = entity.name; + return data; +} \ No newline at end of file diff --git a/lib/generated/json/search_entity.g.dart b/lib/generated/json/search_entity.g.dart new file mode 100644 index 000000000..510ea2259 --- /dev/null +++ b/lib/generated/json/search_entity.g.dart @@ -0,0 +1,537 @@ +import 'package:flutter_deer/generated/json/base/json_convert_content.dart'; +import 'package:flutter_deer/order/models/search_entity.dart'; + +SearchEntity $SearchEntityFromJson(Map json) { + final SearchEntity searchEntity = SearchEntity(); + final int? totalCount = jsonConvert.convert(json['total_count']); + if (totalCount != null) { + searchEntity.totalCount = totalCount; + } + final bool? incompleteResults = jsonConvert.convert(json['incomplete_results']); + if (incompleteResults != null) { + searchEntity.incompleteResults = incompleteResults; + } + final List? items = jsonConvert.convertListNotNull(json['items']); + if (items != null) { + searchEntity.items = items; + } + return searchEntity; +} + +Map $SearchEntityToJson(SearchEntity entity) { + final Map data = {}; + data['total_count'] = entity.totalCount; + data['incomplete_results'] = entity.incompleteResults; + data['items'] = entity.items?.map((v) => v.toJson()).toList(); + return data; +} + +SearchItems $SearchItemsFromJson(Map json) { + final SearchItems searchItems = SearchItems(); + final int? id = jsonConvert.convert(json['id']); + if (id != null) { + searchItems.id = id; + } + final String? nodeId = jsonConvert.convert(json['node_id']); + if (nodeId != null) { + searchItems.nodeId = nodeId; + } + final String? name = jsonConvert.convert(json['name']); + if (name != null) { + searchItems.name = name; + } + final String? fullName = jsonConvert.convert(json['full_name']); + if (fullName != null) { + searchItems.fullName = fullName; + } + final bool? private = jsonConvert.convert(json['private']); + if (private != null) { + searchItems.private = private; + } + final SearchItemsOwner? owner = jsonConvert.convert(json['owner']); + if (owner != null) { + searchItems.owner = owner; + } + final String? htmlUrl = jsonConvert.convert(json['html_url']); + if (htmlUrl != null) { + searchItems.htmlUrl = htmlUrl; + } + final String? description = jsonConvert.convert(json['description']); + if (description != null) { + searchItems.description = description; + } + final bool? fork = jsonConvert.convert(json['fork']); + if (fork != null) { + searchItems.fork = fork; + } + final String? url = jsonConvert.convert(json['url']); + if (url != null) { + searchItems.url = url; + } + final String? forksUrl = jsonConvert.convert(json['forks_url']); + if (forksUrl != null) { + searchItems.forksUrl = forksUrl; + } + final String? keysUrl = jsonConvert.convert(json['keys_url']); + if (keysUrl != null) { + searchItems.keysUrl = keysUrl; + } + final String? collaboratorsUrl = jsonConvert.convert(json['collaborators_url']); + if (collaboratorsUrl != null) { + searchItems.collaboratorsUrl = collaboratorsUrl; + } + final String? teamsUrl = jsonConvert.convert(json['teams_url']); + if (teamsUrl != null) { + searchItems.teamsUrl = teamsUrl; + } + final String? hooksUrl = jsonConvert.convert(json['hooks_url']); + if (hooksUrl != null) { + searchItems.hooksUrl = hooksUrl; + } + final String? issueEventsUrl = jsonConvert.convert(json['issue_events_url']); + if (issueEventsUrl != null) { + searchItems.issueEventsUrl = issueEventsUrl; + } + final String? eventsUrl = jsonConvert.convert(json['events_url']); + if (eventsUrl != null) { + searchItems.eventsUrl = eventsUrl; + } + final String? assigneesUrl = jsonConvert.convert(json['assignees_url']); + if (assigneesUrl != null) { + searchItems.assigneesUrl = assigneesUrl; + } + final String? branchesUrl = jsonConvert.convert(json['branches_url']); + if (branchesUrl != null) { + searchItems.branchesUrl = branchesUrl; + } + final String? tagsUrl = jsonConvert.convert(json['tags_url']); + if (tagsUrl != null) { + searchItems.tagsUrl = tagsUrl; + } + final String? blobsUrl = jsonConvert.convert(json['blobs_url']); + if (blobsUrl != null) { + searchItems.blobsUrl = blobsUrl; + } + final String? gitTagsUrl = jsonConvert.convert(json['git_tags_url']); + if (gitTagsUrl != null) { + searchItems.gitTagsUrl = gitTagsUrl; + } + final String? gitRefsUrl = jsonConvert.convert(json['git_refs_url']); + if (gitRefsUrl != null) { + searchItems.gitRefsUrl = gitRefsUrl; + } + final String? treesUrl = jsonConvert.convert(json['trees_url']); + if (treesUrl != null) { + searchItems.treesUrl = treesUrl; + } + final String? statusesUrl = jsonConvert.convert(json['statuses_url']); + if (statusesUrl != null) { + searchItems.statusesUrl = statusesUrl; + } + final String? languagesUrl = jsonConvert.convert(json['languages_url']); + if (languagesUrl != null) { + searchItems.languagesUrl = languagesUrl; + } + final String? stargazersUrl = jsonConvert.convert(json['stargazers_url']); + if (stargazersUrl != null) { + searchItems.stargazersUrl = stargazersUrl; + } + final String? contributorsUrl = jsonConvert.convert(json['contributors_url']); + if (contributorsUrl != null) { + searchItems.contributorsUrl = contributorsUrl; + } + final String? subscribersUrl = jsonConvert.convert(json['subscribers_url']); + if (subscribersUrl != null) { + searchItems.subscribersUrl = subscribersUrl; + } + final String? subscriptionUrl = jsonConvert.convert(json['subscription_url']); + if (subscriptionUrl != null) { + searchItems.subscriptionUrl = subscriptionUrl; + } + final String? commitsUrl = jsonConvert.convert(json['commits_url']); + if (commitsUrl != null) { + searchItems.commitsUrl = commitsUrl; + } + final String? gitCommitsUrl = jsonConvert.convert(json['git_commits_url']); + if (gitCommitsUrl != null) { + searchItems.gitCommitsUrl = gitCommitsUrl; + } + final String? commentsUrl = jsonConvert.convert(json['comments_url']); + if (commentsUrl != null) { + searchItems.commentsUrl = commentsUrl; + } + final String? issueCommentUrl = jsonConvert.convert(json['issue_comment_url']); + if (issueCommentUrl != null) { + searchItems.issueCommentUrl = issueCommentUrl; + } + final String? contentsUrl = jsonConvert.convert(json['contents_url']); + if (contentsUrl != null) { + searchItems.contentsUrl = contentsUrl; + } + final String? compareUrl = jsonConvert.convert(json['compare_url']); + if (compareUrl != null) { + searchItems.compareUrl = compareUrl; + } + final String? mergesUrl = jsonConvert.convert(json['merges_url']); + if (mergesUrl != null) { + searchItems.mergesUrl = mergesUrl; + } + final String? archiveUrl = jsonConvert.convert(json['archive_url']); + if (archiveUrl != null) { + searchItems.archiveUrl = archiveUrl; + } + final String? downloadsUrl = jsonConvert.convert(json['downloads_url']); + if (downloadsUrl != null) { + searchItems.downloadsUrl = downloadsUrl; + } + final String? issuesUrl = jsonConvert.convert(json['issues_url']); + if (issuesUrl != null) { + searchItems.issuesUrl = issuesUrl; + } + final String? pullsUrl = jsonConvert.convert(json['pulls_url']); + if (pullsUrl != null) { + searchItems.pullsUrl = pullsUrl; + } + final String? milestonesUrl = jsonConvert.convert(json['milestones_url']); + if (milestonesUrl != null) { + searchItems.milestonesUrl = milestonesUrl; + } + final String? notificationsUrl = jsonConvert.convert(json['notifications_url']); + if (notificationsUrl != null) { + searchItems.notificationsUrl = notificationsUrl; + } + final String? labelsUrl = jsonConvert.convert(json['labels_url']); + if (labelsUrl != null) { + searchItems.labelsUrl = labelsUrl; + } + final String? releasesUrl = jsonConvert.convert(json['releases_url']); + if (releasesUrl != null) { + searchItems.releasesUrl = releasesUrl; + } + final String? deploymentsUrl = jsonConvert.convert(json['deployments_url']); + if (deploymentsUrl != null) { + searchItems.deploymentsUrl = deploymentsUrl; + } + final String? createdAt = jsonConvert.convert(json['created_at']); + if (createdAt != null) { + searchItems.createdAt = createdAt; + } + final String? updatedAt = jsonConvert.convert(json['updated_at']); + if (updatedAt != null) { + searchItems.updatedAt = updatedAt; + } + final String? pushedAt = jsonConvert.convert(json['pushed_at']); + if (pushedAt != null) { + searchItems.pushedAt = pushedAt; + } + final String? gitUrl = jsonConvert.convert(json['git_url']); + if (gitUrl != null) { + searchItems.gitUrl = gitUrl; + } + final String? sshUrl = jsonConvert.convert(json['ssh_url']); + if (sshUrl != null) { + searchItems.sshUrl = sshUrl; + } + final String? cloneUrl = jsonConvert.convert(json['clone_url']); + if (cloneUrl != null) { + searchItems.cloneUrl = cloneUrl; + } + final String? svnUrl = jsonConvert.convert(json['svn_url']); + if (svnUrl != null) { + searchItems.svnUrl = svnUrl; + } + final String? homepage = jsonConvert.convert(json['homepage']); + if (homepage != null) { + searchItems.homepage = homepage; + } + final int? size = jsonConvert.convert(json['size']); + if (size != null) { + searchItems.size = size; + } + final int? stargazersCount = jsonConvert.convert(json['stargazers_count']); + if (stargazersCount != null) { + searchItems.stargazersCount = stargazersCount; + } + final int? watchersCount = jsonConvert.convert(json['watchers_count']); + if (watchersCount != null) { + searchItems.watchersCount = watchersCount; + } + final String? language = jsonConvert.convert(json['language']); + if (language != null) { + searchItems.language = language; + } + final bool? hasIssues = jsonConvert.convert(json['has_issues']); + if (hasIssues != null) { + searchItems.hasIssues = hasIssues; + } + final bool? hasProjects = jsonConvert.convert(json['has_projects']); + if (hasProjects != null) { + searchItems.hasProjects = hasProjects; + } + final bool? hasDownloads = jsonConvert.convert(json['has_downloads']); + if (hasDownloads != null) { + searchItems.hasDownloads = hasDownloads; + } + final bool? hasWiki = jsonConvert.convert(json['has_wiki']); + if (hasWiki != null) { + searchItems.hasWiki = hasWiki; + } + final bool? hasPages = jsonConvert.convert(json['has_pages']); + if (hasPages != null) { + searchItems.hasPages = hasPages; + } + final int? forksCount = jsonConvert.convert(json['forks_count']); + if (forksCount != null) { + searchItems.forksCount = forksCount; + } + final bool? archived = jsonConvert.convert(json['archived']); + if (archived != null) { + searchItems.archived = archived; + } + final bool? disabled = jsonConvert.convert(json['disabled']); + if (disabled != null) { + searchItems.disabled = disabled; + } + final int? openIssuesCount = jsonConvert.convert(json['open_issues_count']); + if (openIssuesCount != null) { + searchItems.openIssuesCount = openIssuesCount; + } + final SearchItemsLicense? license = jsonConvert.convert(json['license']); + if (license != null) { + searchItems.license = license; + } + final int? forks = jsonConvert.convert(json['forks']); + if (forks != null) { + searchItems.forks = forks; + } + final int? openIssues = jsonConvert.convert(json['open_issues']); + if (openIssues != null) { + searchItems.openIssues = openIssues; + } + final int? watchers = jsonConvert.convert(json['watchers']); + if (watchers != null) { + searchItems.watchers = watchers; + } + final String? defaultBranch = jsonConvert.convert(json['default_branch']); + if (defaultBranch != null) { + searchItems.defaultBranch = defaultBranch; + } + final double? score = jsonConvert.convert(json['score']); + if (score != null) { + searchItems.score = score; + } + return searchItems; +} + +Map $SearchItemsToJson(SearchItems entity) { + final Map data = {}; + data['id'] = entity.id; + data['node_id'] = entity.nodeId; + data['name'] = entity.name; + data['full_name'] = entity.fullName; + data['private'] = entity.private; + data['owner'] = entity.owner?.toJson(); + data['html_url'] = entity.htmlUrl; + data['description'] = entity.description; + data['fork'] = entity.fork; + data['url'] = entity.url; + data['forks_url'] = entity.forksUrl; + data['keys_url'] = entity.keysUrl; + data['collaborators_url'] = entity.collaboratorsUrl; + data['teams_url'] = entity.teamsUrl; + data['hooks_url'] = entity.hooksUrl; + data['issue_events_url'] = entity.issueEventsUrl; + data['events_url'] = entity.eventsUrl; + data['assignees_url'] = entity.assigneesUrl; + data['branches_url'] = entity.branchesUrl; + data['tags_url'] = entity.tagsUrl; + data['blobs_url'] = entity.blobsUrl; + data['git_tags_url'] = entity.gitTagsUrl; + data['git_refs_url'] = entity.gitRefsUrl; + data['trees_url'] = entity.treesUrl; + data['statuses_url'] = entity.statusesUrl; + data['languages_url'] = entity.languagesUrl; + data['stargazers_url'] = entity.stargazersUrl; + data['contributors_url'] = entity.contributorsUrl; + data['subscribers_url'] = entity.subscribersUrl; + data['subscription_url'] = entity.subscriptionUrl; + data['commits_url'] = entity.commitsUrl; + data['git_commits_url'] = entity.gitCommitsUrl; + data['comments_url'] = entity.commentsUrl; + data['issue_comment_url'] = entity.issueCommentUrl; + data['contents_url'] = entity.contentsUrl; + data['compare_url'] = entity.compareUrl; + data['merges_url'] = entity.mergesUrl; + data['archive_url'] = entity.archiveUrl; + data['downloads_url'] = entity.downloadsUrl; + data['issues_url'] = entity.issuesUrl; + data['pulls_url'] = entity.pullsUrl; + data['milestones_url'] = entity.milestonesUrl; + data['notifications_url'] = entity.notificationsUrl; + data['labels_url'] = entity.labelsUrl; + data['releases_url'] = entity.releasesUrl; + data['deployments_url'] = entity.deploymentsUrl; + data['created_at'] = entity.createdAt; + data['updated_at'] = entity.updatedAt; + data['pushed_at'] = entity.pushedAt; + data['git_url'] = entity.gitUrl; + data['ssh_url'] = entity.sshUrl; + data['clone_url'] = entity.cloneUrl; + data['svn_url'] = entity.svnUrl; + data['homepage'] = entity.homepage; + data['size'] = entity.size; + data['stargazers_count'] = entity.stargazersCount; + data['watchers_count'] = entity.watchersCount; + data['language'] = entity.language; + data['has_issues'] = entity.hasIssues; + data['has_projects'] = entity.hasProjects; + data['has_downloads'] = entity.hasDownloads; + data['has_wiki'] = entity.hasWiki; + data['has_pages'] = entity.hasPages; + data['forks_count'] = entity.forksCount; + data['archived'] = entity.archived; + data['disabled'] = entity.disabled; + data['open_issues_count'] = entity.openIssuesCount; + data['license'] = entity.license?.toJson(); + data['forks'] = entity.forks; + data['open_issues'] = entity.openIssues; + data['watchers'] = entity.watchers; + data['default_branch'] = entity.defaultBranch; + data['score'] = entity.score; + return data; +} + +SearchItemsOwner $SearchItemsOwnerFromJson(Map json) { + final SearchItemsOwner searchItemsOwner = SearchItemsOwner(); + final String? login = jsonConvert.convert(json['login']); + if (login != null) { + searchItemsOwner.login = login; + } + final int? id = jsonConvert.convert(json['id']); + if (id != null) { + searchItemsOwner.id = id; + } + final String? nodeId = jsonConvert.convert(json['node_id']); + if (nodeId != null) { + searchItemsOwner.nodeId = nodeId; + } + final String? avatarUrl = jsonConvert.convert(json['avatar_url']); + if (avatarUrl != null) { + searchItemsOwner.avatarUrl = avatarUrl; + } + final String? gravatarId = jsonConvert.convert(json['gravatar_id']); + if (gravatarId != null) { + searchItemsOwner.gravatarId = gravatarId; + } + final String? url = jsonConvert.convert(json['url']); + if (url != null) { + searchItemsOwner.url = url; + } + final String? htmlUrl = jsonConvert.convert(json['html_url']); + if (htmlUrl != null) { + searchItemsOwner.htmlUrl = htmlUrl; + } + final String? followersUrl = jsonConvert.convert(json['followers_url']); + if (followersUrl != null) { + searchItemsOwner.followersUrl = followersUrl; + } + final String? followingUrl = jsonConvert.convert(json['following_url']); + if (followingUrl != null) { + searchItemsOwner.followingUrl = followingUrl; + } + final String? gistsUrl = jsonConvert.convert(json['gists_url']); + if (gistsUrl != null) { + searchItemsOwner.gistsUrl = gistsUrl; + } + final String? starredUrl = jsonConvert.convert(json['starred_url']); + if (starredUrl != null) { + searchItemsOwner.starredUrl = starredUrl; + } + final String? subscriptionsUrl = jsonConvert.convert(json['subscriptions_url']); + if (subscriptionsUrl != null) { + searchItemsOwner.subscriptionsUrl = subscriptionsUrl; + } + final String? organizationsUrl = jsonConvert.convert(json['organizations_url']); + if (organizationsUrl != null) { + searchItemsOwner.organizationsUrl = organizationsUrl; + } + final String? reposUrl = jsonConvert.convert(json['repos_url']); + if (reposUrl != null) { + searchItemsOwner.reposUrl = reposUrl; + } + final String? eventsUrl = jsonConvert.convert(json['events_url']); + if (eventsUrl != null) { + searchItemsOwner.eventsUrl = eventsUrl; + } + final String? receivedEventsUrl = jsonConvert.convert(json['received_events_url']); + if (receivedEventsUrl != null) { + searchItemsOwner.receivedEventsUrl = receivedEventsUrl; + } + final String? type = jsonConvert.convert(json['type']); + if (type != null) { + searchItemsOwner.type = type; + } + final bool? siteAdmin = jsonConvert.convert(json['site_admin']); + if (siteAdmin != null) { + searchItemsOwner.siteAdmin = siteAdmin; + } + return searchItemsOwner; +} + +Map $SearchItemsOwnerToJson(SearchItemsOwner entity) { + final Map data = {}; + data['login'] = entity.login; + data['id'] = entity.id; + data['node_id'] = entity.nodeId; + data['avatar_url'] = entity.avatarUrl; + data['gravatar_id'] = entity.gravatarId; + data['url'] = entity.url; + data['html_url'] = entity.htmlUrl; + data['followers_url'] = entity.followersUrl; + data['following_url'] = entity.followingUrl; + data['gists_url'] = entity.gistsUrl; + data['starred_url'] = entity.starredUrl; + data['subscriptions_url'] = entity.subscriptionsUrl; + data['organizations_url'] = entity.organizationsUrl; + data['repos_url'] = entity.reposUrl; + data['events_url'] = entity.eventsUrl; + data['received_events_url'] = entity.receivedEventsUrl; + data['type'] = entity.type; + data['site_admin'] = entity.siteAdmin; + return data; +} + +SearchItemsLicense $SearchItemsLicenseFromJson(Map json) { + final SearchItemsLicense searchItemsLicense = SearchItemsLicense(); + final String? key = jsonConvert.convert(json['key']); + if (key != null) { + searchItemsLicense.key = key; + } + final String? name = jsonConvert.convert(json['name']); + if (name != null) { + searchItemsLicense.name = name; + } + final String? spdxId = jsonConvert.convert(json['spdx_id']); + if (spdxId != null) { + searchItemsLicense.spdxId = spdxId; + } + final String? url = jsonConvert.convert(json['url']); + if (url != null) { + searchItemsLicense.url = url; + } + final String? nodeId = jsonConvert.convert(json['node_id']); + if (nodeId != null) { + searchItemsLicense.nodeId = nodeId; + } + return searchItemsLicense; +} + +Map $SearchItemsLicenseToJson(SearchItemsLicense entity) { + final Map data = {}; + data['key'] = entity.key; + data['name'] = entity.name; + data['spdx_id'] = entity.spdxId; + data['url'] = entity.url; + data['node_id'] = entity.nodeId; + return data; +} \ No newline at end of file diff --git a/lib/generated/json/search_entity_helper.dart b/lib/generated/json/search_entity_helper.dart deleted file mode 100644 index 9a9bff64b..000000000 --- a/lib/generated/json/search_entity_helper.dart +++ /dev/null @@ -1,320 +0,0 @@ -import 'package:flutter_deer/order/models/search_entity.dart'; - -searchEntityFromJson(SearchEntity data, Map json) { - if (json['total_count'] != null) { - data.totalCount = json['total_count']?.toInt(); - } - if (json['incomplete_results'] != null) { - data.incompleteResults = json['incomplete_results']; - } - if (json['items'] != null) { - data.items = new List(); - (json['items'] as List).forEach((v) { - data.items.add(new SearchItem().fromJson(v)); - }); - } - return data; -} - -Map searchEntityToJson(SearchEntity entity) { - final Map data = new Map(); - data['total_count'] = entity.totalCount; - data['incomplete_results'] = entity.incompleteResults; - if (entity.items != null) { - data['items'] = entity.items.map((v) => v.toJson()).toList(); - } - return data; -} - -searchItemFromJson(SearchItem data, Map json) { - if (json['id'] != null) { - data.id = json['id']?.toInt(); - } - if (json['node_id'] != null) { - data.nodeId = json['node_id']?.toString(); - } - if (json['name'] != null) { - data.name = json['name']?.toString(); - } - if (json['full_name'] != null) { - data.fullName = json['full_name']?.toString(); - } - if (json['private'] != null) { - data.private = json['private']; - } - if (json['html_url'] != null) { - data.htmlUrl = json['html_url']?.toString(); - } - if (json['description'] != null) { - data.description = json['description']?.toString(); - } - if (json['fork'] != null) { - data.fork = json['fork']; - } - if (json['url'] != null) { - data.url = json['url']?.toString(); - } - if (json['forks_url'] != null) { - data.forksUrl = json['forks_url']?.toString(); - } - if (json['keys_url'] != null) { - data.keysUrl = json['keys_url']?.toString(); - } - if (json['collaborators_url'] != null) { - data.collaboratorsUrl = json['collaborators_url']?.toString(); - } - if (json['teams_url'] != null) { - data.teamsUrl = json['teams_url']?.toString(); - } - if (json['hooks_url'] != null) { - data.hooksUrl = json['hooks_url']?.toString(); - } - if (json['issue_events_url'] != null) { - data.issueEventsUrl = json['issue_events_url']?.toString(); - } - if (json['events_url'] != null) { - data.eventsUrl = json['events_url']?.toString(); - } - if (json['assignees_url'] != null) { - data.assigneesUrl = json['assignees_url']?.toString(); - } - if (json['branches_url'] != null) { - data.branchesUrl = json['branches_url']?.toString(); - } - if (json['tags_url'] != null) { - data.tagsUrl = json['tags_url']?.toString(); - } - if (json['blobs_url'] != null) { - data.blobsUrl = json['blobs_url']?.toString(); - } - if (json['git_tags_url'] != null) { - data.gitTagsUrl = json['git_tags_url']?.toString(); - } - if (json['git_refs_url'] != null) { - data.gitRefsUrl = json['git_refs_url']?.toString(); - } - if (json['trees_url'] != null) { - data.treesUrl = json['trees_url']?.toString(); - } - if (json['statuses_url'] != null) { - data.statusesUrl = json['statuses_url']?.toString(); - } - if (json['languages_url'] != null) { - data.languagesUrl = json['languages_url']?.toString(); - } - if (json['stargazers_url'] != null) { - data.stargazersUrl = json['stargazers_url']?.toString(); - } - if (json['contributors_url'] != null) { - data.contributorsUrl = json['contributors_url']?.toString(); - } - if (json['subscribers_url'] != null) { - data.subscribersUrl = json['subscribers_url']?.toString(); - } - if (json['subscription_url'] != null) { - data.subscriptionUrl = json['subscription_url']?.toString(); - } - if (json['commits_url'] != null) { - data.commitsUrl = json['commits_url']?.toString(); - } - if (json['git_commits_url'] != null) { - data.gitCommitsUrl = json['git_commits_url']?.toString(); - } - if (json['comments_url'] != null) { - data.commentsUrl = json['comments_url']?.toString(); - } - if (json['issue_comment_url'] != null) { - data.issueCommentUrl = json['issue_comment_url']?.toString(); - } - if (json['contents_url'] != null) { - data.contentsUrl = json['contents_url']?.toString(); - } - if (json['compare_url'] != null) { - data.compareUrl = json['compare_url']?.toString(); - } - if (json['merges_url'] != null) { - data.mergesUrl = json['merges_url']?.toString(); - } - if (json['archive_url'] != null) { - data.archiveUrl = json['archive_url']?.toString(); - } - if (json['downloads_url'] != null) { - data.downloadsUrl = json['downloads_url']?.toString(); - } - if (json['issues_url'] != null) { - data.issuesUrl = json['issues_url']?.toString(); - } - if (json['pulls_url'] != null) { - data.pullsUrl = json['pulls_url']?.toString(); - } - if (json['milestones_url'] != null) { - data.milestonesUrl = json['milestones_url']?.toString(); - } - if (json['notifications_url'] != null) { - data.notificationsUrl = json['notifications_url']?.toString(); - } - if (json['labels_url'] != null) { - data.labelsUrl = json['labels_url']?.toString(); - } - if (json['releases_url'] != null) { - data.releasesUrl = json['releases_url']?.toString(); - } - if (json['deployments_url'] != null) { - data.deploymentsUrl = json['deployments_url']?.toString(); - } - if (json['created_at'] != null) { - data.createdAt = json['created_at']?.toString(); - } - if (json['updated_at'] != null) { - data.updatedAt = json['updated_at']?.toString(); - } - if (json['pushed_at'] != null) { - data.pushedAt = json['pushed_at']?.toString(); - } - if (json['git_url'] != null) { - data.gitUrl = json['git_url']?.toString(); - } - if (json['ssh_url'] != null) { - data.sshUrl = json['ssh_url']?.toString(); - } - if (json['clone_url'] != null) { - data.cloneUrl = json['clone_url']?.toString(); - } - if (json['svn_url'] != null) { - data.svnUrl = json['svn_url']?.toString(); - } - if (json['homepage'] != null) { - data.homepage = json['homepage']?.toString(); - } - if (json['size'] != null) { - data.size = json['size']?.toInt(); - } - if (json['stargazers_count'] != null) { - data.stargazersCount = json['stargazers_count']?.toInt(); - } - if (json['watchers_count'] != null) { - data.watchersCount = json['watchers_count']?.toInt(); - } - if (json['language'] != null) { - data.language = json['language']?.toString(); - } - if (json['has_issues'] != null) { - data.hasIssues = json['has_issues']; - } - if (json['has_projects'] != null) { - data.hasProjects = json['has_projects']; - } - if (json['has_downloads'] != null) { - data.hasDownloads = json['has_downloads']; - } - if (json['has_wiki'] != null) { - data.hasWiki = json['has_wiki']; - } - if (json['has_pages'] != null) { - data.hasPages = json['has_pages']; - } - if (json['forks_count'] != null) { - data.forksCount = json['forks_count']?.toInt(); - } - if (json['archived'] != null) { - data.archived = json['archived']; - } - if (json['disabled'] != null) { - data.disabled = json['disabled']; - } - if (json['open_issues_count'] != null) { - data.openIssuesCount = json['open_issues_count']?.toInt(); - } - if (json['forks'] != null) { - data.forks = json['forks']?.toInt(); - } - if (json['open_issues'] != null) { - data.openIssues = json['open_issues']?.toInt(); - } - if (json['watchers'] != null) { - data.watchers = json['watchers']?.toInt(); - } - if (json['default_branch'] != null) { - data.defaultBranch = json['default_branch']?.toString(); - } - if (json['score'] != null) { - data.score = json['score']?.toDouble(); - } - return data; -} - -Map searchItemToJson(SearchItem entity) { - final Map data = new Map(); - data['id'] = entity.id; - data['node_id'] = entity.nodeId; - data['name'] = entity.name; - data['full_name'] = entity.fullName; - data['private'] = entity.private; - data['html_url'] = entity.htmlUrl; - data['description'] = entity.description; - data['fork'] = entity.fork; - data['url'] = entity.url; - data['forks_url'] = entity.forksUrl; - data['keys_url'] = entity.keysUrl; - data['collaborators_url'] = entity.collaboratorsUrl; - data['teams_url'] = entity.teamsUrl; - data['hooks_url'] = entity.hooksUrl; - data['issue_events_url'] = entity.issueEventsUrl; - data['events_url'] = entity.eventsUrl; - data['assignees_url'] = entity.assigneesUrl; - data['branches_url'] = entity.branchesUrl; - data['tags_url'] = entity.tagsUrl; - data['blobs_url'] = entity.blobsUrl; - data['git_tags_url'] = entity.gitTagsUrl; - data['git_refs_url'] = entity.gitRefsUrl; - data['trees_url'] = entity.treesUrl; - data['statuses_url'] = entity.statusesUrl; - data['languages_url'] = entity.languagesUrl; - data['stargazers_url'] = entity.stargazersUrl; - data['contributors_url'] = entity.contributorsUrl; - data['subscribers_url'] = entity.subscribersUrl; - data['subscription_url'] = entity.subscriptionUrl; - data['commits_url'] = entity.commitsUrl; - data['git_commits_url'] = entity.gitCommitsUrl; - data['comments_url'] = entity.commentsUrl; - data['issue_comment_url'] = entity.issueCommentUrl; - data['contents_url'] = entity.contentsUrl; - data['compare_url'] = entity.compareUrl; - data['merges_url'] = entity.mergesUrl; - data['archive_url'] = entity.archiveUrl; - data['downloads_url'] = entity.downloadsUrl; - data['issues_url'] = entity.issuesUrl; - data['pulls_url'] = entity.pullsUrl; - data['milestones_url'] = entity.milestonesUrl; - data['notifications_url'] = entity.notificationsUrl; - data['labels_url'] = entity.labelsUrl; - data['releases_url'] = entity.releasesUrl; - data['deployments_url'] = entity.deploymentsUrl; - data['created_at'] = entity.createdAt; - data['updated_at'] = entity.updatedAt; - data['pushed_at'] = entity.pushedAt; - data['git_url'] = entity.gitUrl; - data['ssh_url'] = entity.sshUrl; - data['clone_url'] = entity.cloneUrl; - data['svn_url'] = entity.svnUrl; - data['homepage'] = entity.homepage; - data['size'] = entity.size; - data['stargazers_count'] = entity.stargazersCount; - data['watchers_count'] = entity.watchersCount; - data['language'] = entity.language; - data['has_issues'] = entity.hasIssues; - data['has_projects'] = entity.hasProjects; - data['has_downloads'] = entity.hasDownloads; - data['has_wiki'] = entity.hasWiki; - data['has_pages'] = entity.hasPages; - data['forks_count'] = entity.forksCount; - data['archived'] = entity.archived; - data['disabled'] = entity.disabled; - data['open_issues_count'] = entity.openIssuesCount; - data['forks'] = entity.forks; - data['open_issues'] = entity.openIssues; - data['watchers'] = entity.watchers; - data['default_branch'] = entity.defaultBranch; - data['score'] = entity.score; - return data; -} \ No newline at end of file diff --git a/lib/generated/json/user_entity.g.dart b/lib/generated/json/user_entity.g.dart new file mode 100644 index 000000000..d93d86d23 --- /dev/null +++ b/lib/generated/json/user_entity.g.dart @@ -0,0 +1,32 @@ +import 'package:flutter_deer/generated/json/base/json_convert_content.dart'; +import 'package:flutter_deer/shop/models/user_entity.dart'; + +UserEntity $UserEntityFromJson(Map json) { + final UserEntity userEntity = UserEntity(); + final String? avatarUrl = jsonConvert.convert(json['avatar_url']); + if (avatarUrl != null) { + userEntity.avatarUrl = avatarUrl; + } + final String? name = jsonConvert.convert(json['name']); + if (name != null) { + userEntity.name = name; + } + final int? id = jsonConvert.convert(json['id']); + if (id != null) { + userEntity.id = id; + } + final String? blog = jsonConvert.convert(json['blog']); + if (blog != null) { + userEntity.blog = blog; + } + return userEntity; +} + +Map $UserEntityToJson(UserEntity entity) { + final Map data = {}; + data['avatar_url'] = entity.avatarUrl; + data['name'] = entity.name; + data['id'] = entity.id; + data['blog'] = entity.blog; + return data; +} \ No newline at end of file diff --git a/lib/generated/json/user_entity_helper.dart b/lib/generated/json/user_entity_helper.dart deleted file mode 100644 index 560ecd871..000000000 --- a/lib/generated/json/user_entity_helper.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter_deer/shop/models/user_entity.dart'; - -userEntityFromJson(UserEntity data, Map json) { - if (json['avatar_url'] != null) { - data.avatarUrl = json['avatar_url']?.toString(); - } - if (json['name'] != null) { - data.name = json['name']?.toString(); - } - if (json['id'] != null) { - data.id = json['id']?.toInt(); - } - if (json['blog'] != null) { - data.blog = json['blog']?.toString(); - } - return data; -} - -Map userEntityToJson(UserEntity entity) { - final Map data = new Map(); - data['avatar_url'] = entity.avatarUrl; - data['name'] = entity.name; - data['id'] = entity.id; - data['blog'] = entity.blog; - return data; -} \ No newline at end of file diff --git a/lib/goods/goods_router.dart b/lib/goods/goods_router.dart index eb9771ff1..ac8e8eb55 100644 --- a/lib/goods/goods_router.dart +++ b/lib/goods/goods_router.dart @@ -1,4 +1,5 @@ +import 'package:common_utils/common_utils.dart'; import 'package:fluro/fluro.dart'; import 'package:flutter_deer/goods/page/qr_code_scanner_page.dart'; import 'package:flutter_deer/routers/i_router.dart'; @@ -9,7 +10,6 @@ import 'page/goods_search_page.dart'; import 'page/goods_size_edit_page.dart'; import 'page/goods_size_page.dart'; - class GoodsRouter implements IRouterProvider{ static String goodsPage = '/goods'; @@ -25,7 +25,9 @@ class GoodsRouter implements IRouterProvider{ router.define(goodsEditPage, handler: Handler(handlerFunc: (_, Map> params) { final bool isAdd = params['isAdd']?.first == 'true'; final bool isScan = params['isScan']?.first == 'true'; - return GoodsEditPage(isAdd: isAdd, isScan: isScan,); + final String url = EncryptUtil.decodeBase64(params['url']?.first ?? ''); + final String heroTag = params['heroTag']?.first ?? 'heroTag'; + return GoodsEditPage(isAdd: isAdd, isScan: isScan, goodsImageUrl: url, heroTag: heroTag,); })); router.define(goodsSearchPage, handler: Handler(handlerFunc: (_, __) => const GoodsSearchPage())); router.define(goodsSizePage, handler: Handler(handlerFunc: (_, __) => const GoodsSizePage())); @@ -33,4 +35,4 @@ class GoodsRouter implements IRouterProvider{ router.define(qrCodeScannerPage, handler: Handler(handlerFunc: (_, __) => const QrCodeScannerPage())); } -} \ No newline at end of file +} diff --git a/lib/goods/models/goods_item_entity.dart b/lib/goods/models/goods_item_entity.dart index 92af51495..d4f238fcb 100644 --- a/lib/goods/models/goods_item_entity.dart +++ b/lib/goods/models/goods_item_entity.dart @@ -1,6 +1,6 @@ class GoodsItemEntity { - GoodsItemEntity({this.icon, this.title, this.type}); + GoodsItemEntity({required this.icon, required this.title, required this.type}); GoodsItemEntity.fromJson(Map json) { icon = json['icon'] as String; @@ -8,9 +8,9 @@ class GoodsItemEntity { type = json['type'] as int; } - String icon; - String title; - int type; + late String icon; + late String title; + late int type; Map toJson() { final Map data = {}; diff --git a/lib/goods/models/goods_sort_entity.dart b/lib/goods/models/goods_sort_entity.dart new file mode 100644 index 000000000..9c28a230d --- /dev/null +++ b/lib/goods/models/goods_sort_entity.dart @@ -0,0 +1,16 @@ +import 'package:flutter_deer/generated/json/base/json_field.dart'; +import 'package:flutter_deer/generated/json/goods_sort_entity.g.dart'; + + +@JsonSerializable() +class GoodsSortEntity { + + GoodsSortEntity(); + + factory GoodsSortEntity.fromJson(Map json) => $GoodsSortEntityFromJson(json); + + Map toJson() => $GoodsSortEntityToJson(this); + + late String id; + late String name; +} diff --git a/lib/goods/page/goods_edit_page.dart b/lib/goods/page/goods_edit_page.dart index 11a6db2b6..b9d9b26fb 100644 --- a/lib/goods/page/goods_edit_page.dart +++ b/lib/goods/page/goods_edit_page.dart @@ -1,29 +1,36 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/goods/provider/goods_sort_provider.dart'; import 'package:flutter_deer/goods/widgets/goods_sort_bottom_sheet.dart'; +import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/device_utils.dart'; import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/util/toast_utils.dart'; import 'package:flutter_deer/widgets/click_item.dart'; import 'package:flutter_deer/widgets/load_image.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; import 'package:flutter_deer/widgets/my_scroll_view.dart'; import 'package:flutter_deer/widgets/selected_image.dart'; import 'package:flutter_deer/widgets/text_field_item.dart'; -import 'package:flutter_deer/res/resources.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import '../goods_router.dart'; /// design/4商品/index.html#artboard5 class GoodsEditPage extends StatefulWidget { - const GoodsEditPage({Key key, this.isAdd = true, this.isScan = false}) : super(key: key); + const GoodsEditPage({ + super.key, + this.isAdd = true, + this.isScan = false, + this.heroTag, + this.goodsImageUrl + }); final bool isAdd; final bool isScan; + final String? heroTag; + final String? goodsImageUrl; @override _GoodsEditPageState createState() => _GoodsEditPageState(); @@ -31,7 +38,7 @@ class GoodsEditPage extends StatefulWidget { class _GoodsEditPageState extends State { - String _goodsSortName; + String? _goodsSortName; final TextEditingController _codeController = TextEditingController(); @override @@ -44,10 +51,17 @@ class _GoodsEditPageState extends State { }); } - void _scan() { + Future _scan() async { if (Device.isMobile) { - NavigatorUtils.pushResult(context, GoodsRouter.qrCodeScannerPage, (Object code) { - _codeController.text = code.toString(); + NavigatorUtils.unfocus(); + // 延时保证键盘收起,否则进入扫码页会黑屏 + Future.delayed(const Duration(milliseconds: 500), (){ + if (!mounted) { + return; + } + NavigatorUtils.pushResult(context, GoodsRouter.qrCodeScannerPage, (Object code) { + _codeController.text = code.toString(); + }); }); } else { Toast.show('当前平台暂不支持'); @@ -63,6 +77,13 @@ class _GoodsEditPageState extends State { body: MyScrollView( key: const Key('goods_edit_page'), padding: const EdgeInsets.symmetric(vertical: 16.0), + bottomButton: Padding( + padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 8.0), + child: MyButton( + onPressed: () => NavigatorUtils.goBack(context), + text: '提交', + ), + ), children: [ Gaps.vGap5, const Padding( @@ -73,8 +94,10 @@ class _GoodsEditPageState extends State { ), ), Gaps.vGap16, - const Center( + Center( child: SelectedImage( + heroTag: widget.heroTag, + url: widget.goodsImageUrl, size: 96.0, ), ), @@ -82,7 +105,7 @@ class _GoodsEditPageState extends State { Center( child: Text( '点击添加商品图片', - style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp14), + style: Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp14), ), ), Gaps.vGap16, @@ -112,13 +135,13 @@ class _GoodsEditPageState extends State { child: Semantics( label: '扫码', child: GestureDetector( + onTap: _scan, child: Padding( padding: const EdgeInsets.all(16.0), child: context.isDark ? const LoadAssetImage('goods/icon_sm', width: 16.0, height: 16.0) : const LoadAssetImage('goods/scanning', width: 16.0, height: 16.0), ), - onTap: _scan, ), ), ) @@ -166,13 +189,6 @@ class _GoodsEditPageState extends State { ), Gaps.vGap8, ], - bottomButton: Padding( - padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 8.0), - child: MyButton( - onPressed: () => NavigatorUtils.goBack(context), - text: '提交', - ), - ), ) ); } diff --git a/lib/goods/page/goods_list_page.dart b/lib/goods/page/goods_list_page.dart index f8c40f132..4e0c013f6 100644 --- a/lib/goods/page/goods_list_page.dart +++ b/lib/goods/page/goods_list_page.dart @@ -1,8 +1,8 @@ - - +import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_deer/goods/models/goods_item_entity.dart'; import 'package:flutter_deer/goods/provider/goods_page_provider.dart'; +import 'package:flutter_deer/res/constant.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/toast_utils.dart'; import 'package:flutter_deer/widgets/my_refresh_list.dart'; @@ -14,34 +14,34 @@ import '../widgets/goods_delete_bottom_sheet.dart'; import '../widgets/goods_item.dart'; class GoodsListPage extends StatefulWidget { - + const GoodsListPage({ - Key key, - @required this.index - }): super(key: key); - + super.key, + required this.index + }); + final int index; - + @override _GoodsListPageState createState() => _GoodsListPageState(); } class _GoodsListPageState extends State with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin { - + int _selectIndex = -1; - Animation _animation; - AnimationController _controller; + late Animation _animation; + late AnimationController _controller; List _list = []; AnimationStatus _animationStatus = AnimationStatus.dismissed; - + @override void initState() { super.initState(); // 初始化动画控制 _controller = AnimationController(duration: const Duration(milliseconds: 450), vsync: this); // 动画曲线 - final _curvedAnimation = CurvedAnimation(parent: _controller, curve: Curves.easeOutSine); - _animation = Tween(begin: 0.0, end: 1.1).animate(_curvedAnimation) ..addStatusListener((status) { + final curvedAnimation = CurvedAnimation(parent: _controller, curve: Curves.easeOutSine); + _animation = Tween(begin: 0.0, end: 1.1).animate(curvedAnimation) ..addStatusListener((status) { _animationStatus = status; }); @@ -58,15 +58,18 @@ class _GoodsListPageState extends State with AutomaticKeepAliveCl } final List _imgList = [ - 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3130502839,1206722360&fm=26&gp=0.jpg', - 'https://xxx', // 故意使用一张无效链接,触发默认显示图片 - 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1762976310,1236462418&fm=26&gp=0.jpg', - 'https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3659255919,3211745976&fm=26&gp=0.jpg', - 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2085939314,235211629&fm=26&gp=0.jpg', - 'https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2441563887,1184810091&fm=26&gp=0.jpg' + 'https://hbimg.b0.upaiyun.com/29cdf569b916ec8b952804a93b0a77e8c9baf61a58e0b-A0orbz_fw658', + if (Constant.isDriverTest) + 'https://hbimg.huabanimg.com/a3947661524be662da9f40d95dddc73c66196816633e1-9bUI91_fw658' + else + 'https://xxx', // 可以使用一张无效链接,触发缺省、异常显示图片 + 'https://hbimg.huabanimg.com/528c11bba65e2b8c0b6ae56f05e66b68f78f545f4ff26-tkM2Lx_fw658', + 'https://img2.baidu.com/it/u=272387872,295674292&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500', + 'https://img0.baidu.com/it/u=2202484983,917817934&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500', + 'https://img0.baidu.com/it/u=2329453320,961102964&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500' ]; - Future _onRefresh() async { + Future _onRefresh() async { await Future.delayed(const Duration(seconds: 2), () { setState(() { _page = 1; @@ -77,7 +80,7 @@ class _GoodsListPageState extends State with AutomaticKeepAliveCl }); } - Future _loadMore() async { + Future _loadMore() async { await Future.delayed(const Duration(seconds: 2), () { setState(() { _list.addAll(List.generate(10, (i) => @@ -87,7 +90,7 @@ class _GoodsListPageState extends State with AutomaticKeepAliveCl _setGoodsCount(_list.length); }); } - + void _setGoodsCount(int count) { // Provider.of(context, listen: false).setGoodsCount(count); /// 与上方等价,provider 4.1.0添加的拓展方法 @@ -95,9 +98,9 @@ class _GoodsListPageState extends State with AutomaticKeepAliveCl } int _page = 1; - int _maxPage; + late int _maxPage; StateType _stateType = StateType.loading; - + @override Widget build(BuildContext context) { super.build(context); @@ -108,8 +111,10 @@ class _GoodsListPageState extends State with AutomaticKeepAliveCl loadMore: _loadMore, hasMore: _page < _maxPage, itemBuilder: (_, index) { + final String heroTag = 'goodsImg${widget.index}-$index'; return GoodsItem( index: index, + heroTag: heroTag, selectIndex: _selectIndex, item: _list[index], animation: _animation, @@ -137,7 +142,8 @@ class _GoodsListPageState extends State with AutomaticKeepAliveCl setState(() { _selectIndex = -1; }); - NavigatorUtils.push(context, '${GoodsRouter.goodsEditPage}?isAdd=false'); + final String url = EncryptUtil.encodeBase64(_list[index].icon); + NavigatorUtils.push(context, '${GoodsRouter.goodsEditPage}?isAdd=false&url=$url&heroTag=$heroTag'); }, onTapOperation: () { Toast.show('下架'); diff --git a/lib/goods/page/goods_page.dart b/lib/goods/page/goods_page.dart index 89ad59185..6797cf1da 100644 --- a/lib/goods/page/goods_page.dart +++ b/lib/goods/page/goods_page.dart @@ -1,7 +1,8 @@ - import 'package:flutter/material.dart'; +import 'package:flutter_deer/goods/goods_router.dart'; import 'package:flutter_deer/goods/page/goods_list_page.dart'; import 'package:flutter_deer/goods/provider/goods_page_provider.dart'; +import 'package:flutter_deer/goods/widgets/goods_add_menu.dart'; import 'package:flutter_deer/goods/widgets/goods_sort_menu.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; @@ -11,13 +12,11 @@ import 'package:flutter_deer/widgets/load_image.dart'; import 'package:flutter_deer/widgets/popup_window.dart'; import 'package:provider/provider.dart'; -import '../goods_router.dart'; - /// design/4商品/index.html class GoodsPage extends StatefulWidget { - const GoodsPage({Key key}) : super(key: key); + const GoodsPage({super.key}); @override _GoodsPageState createState() => _GoodsPageState(); @@ -26,8 +25,8 @@ class GoodsPage extends StatefulWidget { class _GoodsPageState extends State with SingleTickerProviderStateMixin, AutomaticKeepAliveClientMixin { final List _sortList = ['全部商品', '个人护理', '饮料', '沐浴洗护', '厨房用具', '休闲食品', '生鲜水果', '酒水', '家庭清洁']; - TabController _tabController; - final PageController _pageController = PageController(initialPage: 0); + TabController? _tabController; + final PageController _pageController = PageController(); final GlobalKey _addKey = GlobalKey(); final GlobalKey _bodyKey = GlobalKey(); @@ -43,14 +42,20 @@ class _GoodsPageState extends State with SingleTickerProviderStateMix @override void dispose() { - _tabController.dispose(); + _tabController?.dispose(); super.dispose(); } - + + /// https://github.com/flutter/flutter/issues/72908 + @override + // ignore: must_call_super + void didChangeDependencies() { + } + @override Widget build(BuildContext context) { super.build(context); - final Color _iconColor = ThemeUtils.getIconColor(context); + final Color? iconColor = ThemeUtils.getIconColor(context); return ChangeNotifierProvider( create: (_) => provider, child: Scaffold( @@ -64,7 +69,7 @@ class _GoodsPageState extends State with SingleTickerProviderStateMix key: const Key('search'), width: 24.0, height: 24.0, - color: _iconColor, + color: iconColor, ), ), IconButton( @@ -76,7 +81,7 @@ class _GoodsPageState extends State with SingleTickerProviderStateMix key: const Key('add'), width: 24.0, height: 24.0, - color: _iconColor, + color: iconColor, ), ) ], @@ -106,7 +111,7 @@ class _GoodsPageState extends State with SingleTickerProviderStateMix style: TextStyles.textBold24, ), Gaps.hGap8, - LoadAssetImage('goods/expand', width: 16.0, height: 16.0, color: _iconColor,) + LoadAssetImage('goods/expand', width: 16.0, height: 16.0, color: iconColor,) ], ); }, @@ -115,10 +120,8 @@ class _GoodsPageState extends State with SingleTickerProviderStateMix ), ), Gaps.vGap24, - Container( - // 隐藏点击效果 + Padding( padding: const EdgeInsets.only(left: 16.0), - color: context.backgroundColor, child: TabBar( onTap: (index) { if (!mounted) { @@ -130,10 +133,15 @@ class _GoodsPageState extends State with SingleTickerProviderStateMix controller: _tabController, labelStyle: TextStyles.textBold18, indicatorSize: TabBarIndicatorSize.label, - labelPadding: const EdgeInsets.only(left: 0.0), + labelPadding: EdgeInsets.zero, unselectedLabelColor: context.isDark ? Colours.text_gray : Colours.text, labelColor: Theme.of(context).primaryColor, indicatorPadding: const EdgeInsets.only(right: 98.0 - 36.0), + // 隐藏点击效果 + overlayColor: MaterialStateProperty.resolveWith((Set states) { + return Colors.transparent; + }, + ), tabs: const [ _TabView('在售', 0), _TabView('待售', 1), @@ -158,30 +166,20 @@ class _GoodsPageState extends State with SingleTickerProviderStateMix } void _onPageChange(int index) { - _tabController.animateTo(index); + _tabController?.animateTo(index); provider.setIndex(index); } /// design/4商品/index.html#artboard3 void _showSortMenu() { // 获取点击控件的坐标 - final RenderBox button = _buttonKey.currentContext.findRenderObject() as RenderBox; - final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox; - // 获得控件左下方的坐标 - final Offset a = button.localToGlobal(Offset(0.0, button.size.height + 12.0), ancestor: overlay); - // 获得控件右下方的坐标 - final Offset b = button.localToGlobal(button.size.bottomLeft(const Offset(0, 12.0)), ancestor: overlay); - final RelativeRect position = RelativeRect.fromRect( - Rect.fromPoints(a, b), - Offset.zero & overlay.size, - ); - final RenderBox body = _bodyKey.currentContext.findRenderObject() as RenderBox; + final RenderBox button = _buttonKey.currentContext!.findRenderObject()! as RenderBox; + final RenderBox body = _bodyKey.currentContext!.findRenderObject()! as RenderBox; showPopupWindow( context: context, - fullWidth: true, - position: position, - elevation: 0.0, + offset: const Offset(0.0, 12.0), + anchor: button, child: GoodsSortMenu( data: _sortList, height: body.size.height - button.size.height, @@ -189,7 +187,6 @@ class _GoodsPageState extends State with SingleTickerProviderStateMix onSelected: (index, name) { provider.setSortIndex(index); Toast.show('选择分类: $name'); - NavigatorUtils.goBack(context); }, ), ); @@ -197,72 +194,14 @@ class _GoodsPageState extends State with SingleTickerProviderStateMix /// design/4商品/index.html#artboard4 void _showAddMenu() { - final RenderBox button = _addKey.currentContext.findRenderObject() as RenderBox; - final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox; - final a = button.localToGlobal(Offset(button.size.width - 8.0, button.size.height - 12.0), ancestor: overlay); - final b = button.localToGlobal(button.size.bottomLeft(const Offset(0, - 12.0)), ancestor: overlay); - final RelativeRect position = RelativeRect.fromRect( - Rect.fromPoints(a, b), - Offset.zero & overlay.size, - ); - final Color backgroundColor = context.backgroundColor; - final Color _iconColor = ThemeUtils.getIconColor(context); + final RenderBox button = _addKey.currentContext!.findRenderObject()! as RenderBox; + showPopupWindow( context: context, - fullWidth: false, isShowBg: true, - position: position, - elevation: 0.0, - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Padding( - padding: const EdgeInsets.only(right: 12.0), - child: LoadAssetImage('goods/jt', width: 8.0, height: 4.0, - color: ThemeUtils.getDarkColor(context, Colours.dark_bg_color), - ), - ), - SizedBox( - width: 120.0, - height: 40.0, - child: TextButton.icon( - onPressed: () { - NavigatorUtils.push(context, '${GoodsRouter.goodsEditPage}?isAdd=true&isScan=true', replace: true); - }, - icon: LoadAssetImage('goods/scanning', width: 16.0, height: 16.0, color: _iconColor,), - label: const Text('扫码添加'), - style: TextButton.styleFrom( - primary: Theme.of(context).textTheme.bodyText2.color, - onSurface: Theme.of(context).textTheme.bodyText2.color.withOpacity(0.12), - backgroundColor: backgroundColor, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only(topLeft: Radius.circular(8.0), topRight: Radius.circular(8.0)), - ), - ), - ), - ), - Container(width: 120.0, height: 0.6, color: Colours.line), - SizedBox( - width: 120.0, - height: 40.0, - child: TextButton.icon( - onPressed: () { - NavigatorUtils.push(context, '${GoodsRouter.goodsEditPage}?isAdd=true', replace: true); - }, - icon: LoadAssetImage('goods/add2', width: 16.0, height: 16.0, color: _iconColor,), - label: const Text('添加商品'), - style: TextButton.styleFrom( - primary: Theme.of(context).textTheme.bodyText2.color, - onSurface: Theme.of(context).textTheme.bodyText2.color.withOpacity(0.12), - backgroundColor: backgroundColor, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only(bottomLeft: Radius.circular(8.0), bottomRight: Radius.circular(8.0)), - ), - ), - ), - ), - ], - ), + offset: Offset(button.size.width - 8.0, -12.0), + anchor: button, + child: const GoodsAddMenu(), ); } diff --git a/lib/goods/page/goods_search_page.dart b/lib/goods/page/goods_search_page.dart index 680010a84..7a19e6c34 100644 --- a/lib/goods/page/goods_search_page.dart +++ b/lib/goods/page/goods_search_page.dart @@ -1,12 +1,11 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/util/toast_utils.dart'; -import 'package:flutter_deer/widgets/search_bar.dart'; +import 'package:flutter_deer/widgets/my_search_bar.dart'; class GoodsSearchPage extends StatefulWidget { - const GoodsSearchPage({Key key}) : super(key: key); + const GoodsSearchPage({super.key}); @override _GoodsSearchPageState createState() => _GoodsSearchPageState(); @@ -16,7 +15,7 @@ class _GoodsSearchPageState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: SearchBar( + appBar: MySearchBar( hintText: '请输入商品名称查询', onPressed: (text) => Toast.show('搜索内容:$text'), ), diff --git a/lib/goods/page/goods_size_edit_page.dart b/lib/goods/page/goods_size_edit_page.dart index 795e1856b..94acb3ce1 100644 --- a/lib/goods/page/goods_size_edit_page.dart +++ b/lib/goods/page/goods_size_edit_page.dart @@ -1,17 +1,16 @@ - import 'package:flutter/material.dart'; +import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; import 'package:flutter_deer/widgets/my_scroll_view.dart'; import 'package:flutter_deer/widgets/selected_image.dart'; import 'package:flutter_deer/widgets/text_field_item.dart'; -import 'package:flutter_deer/res/resources.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; /// design/4商品/index.html#artboard14 class GoodsSizeEditPage extends StatefulWidget { - const GoodsSizeEditPage({Key key}) : super(key: key); + const GoodsSizeEditPage({super.key}); @override _GoodsSizeEditPageState createState() => _GoodsSizeEditPageState(); @@ -27,6 +26,13 @@ class _GoodsSizeEditPageState extends State { ), body: MyScrollView( padding: const EdgeInsets.symmetric(vertical: 16.0), + bottomButton: Padding( + padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 8.0), + child: MyButton( + onPressed: () => NavigatorUtils.goBack(context), + text: '确定', + ), + ), children: [ Gaps.vGap5, const Padding( @@ -43,7 +49,7 @@ class _GoodsSizeEditPageState extends State { Center( child: Text( '点击添加分类图片', - style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp14), + style: Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp14), ), ), Gaps.vGap16, @@ -85,13 +91,6 @@ class _GoodsSizeEditPageState extends State { ), Gaps.vGap8, ], - bottomButton: Padding( - padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 8.0), - child: MyButton( - onPressed: () => NavigatorUtils.goBack(context), - text: '确定', - ), - ), ) ); } diff --git a/lib/goods/page/goods_size_page.dart b/lib/goods/page/goods_size_page.dart index 66812493d..3586ec453 100644 --- a/lib/goods/page/goods_size_page.dart +++ b/lib/goods/page/goods_size_page.dart @@ -1,14 +1,15 @@ - +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_deer/goods/models/goods_size_model.dart'; import 'package:flutter_deer/goods/widgets/goods_size_dialog.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; +import 'package:flutter_deer/util/device_utils.dart'; import 'package:flutter_deer/util/image_utils.dart'; -import 'package:flutter_deer/util/toast_utils.dart'; import 'package:flutter_deer/util/other_utils.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; +import 'package:flutter_deer/util/toast_utils.dart'; import 'package:flutter_deer/widgets/load_image.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; import 'package:flutter_deer/widgets/popup_window.dart'; import 'package:flutter_deer/widgets/state_layout.dart'; @@ -19,21 +20,19 @@ import '../goods_router.dart'; /// design/4商品/index.html#artboard9 class GoodsSizePage extends StatefulWidget { - const GoodsSizePage({Key key}) : super(key: key); + const GoodsSizePage({super.key}); @override _GoodsSizePageState createState() => _GoodsSizePageState(); } class _GoodsSizePageState extends State { - + bool _isEdit = false; String _sizeName = '商品规格名称'; final GlobalKey _hintKey = GlobalKey(); final List _goodsSizeList = []; - // 保留一个Slidable打开 - final SlidableController _slidableController = SlidableController(); @override void initState() { @@ -55,20 +54,12 @@ class _GoodsSizePageState extends State { /// design/4商品/index.html#artboard18 void _showHint() { - final RenderBox hint = _hintKey.currentContext.findRenderObject() as RenderBox; - final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox; - final a = hint.localToGlobal(Offset(50.0, hint.size.height + 150.0), ancestor: overlay); - final b = hint.localToGlobal(hint.size.bottomLeft(const Offset(50.0, 150.0)), ancestor: overlay); - final RelativeRect position = RelativeRect.fromRect( - Rect.fromPoints(a, b), - Offset.zero & overlay.size, - ); + final RenderBox hint = _hintKey.currentContext!.findRenderObject()! as RenderBox; showPopupWindow( context: context, - fullWidth: false, isShowBg: true, - position: position, - elevation: 0.0, + offset: const Offset(50.0, 150.0), + anchor: hint, child: Semantics( label: '弹出引导页', hint: '向左滑动可删除列表,点击可关闭', @@ -80,14 +71,14 @@ class _GoodsSizePageState extends State { decoration: BoxDecoration( image: DecorationImage( image: ImageUtils.getAssetImage('goods/ydss'), - fit: BoxFit.fitWidth - ) + fit: BoxFit.fitWidth, + ), ), ), - ) + ), ); } - + @override Widget build(BuildContext context) { return Scaffold( @@ -100,56 +91,45 @@ class _GoodsSizePageState extends State { NavigatorUtils.goBack(context); }, ), + resizeToAvoidBottomInset: false, body: SafeArea( child: Column( - crossAxisAlignment: CrossAxisAlignment.center, children: [ Gaps.vGap16, Text( _sizeName, style: TextStyles.textBold24, ), - InkWell( - onTap: () { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext context) { - return GoodsSizeDialog( - onPressed: (name) { - setState(() { - _sizeName = name; - _isEdit = true; - }); + Gaps.vGap8, + RichText( + key: const Key('name_edit'), + text: TextSpan( + text: '先对名称进行', + style: Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp14), + children: [ + TextSpan( + text: '编辑', + semanticsLabel: '编辑', + style: TextStyle(color: Theme.of(context).primaryColor), + recognizer: TapGestureRecognizer() + ..onTap = () { + _showGoodsSizeDialog(); }, - ); - } - ); - }, - child: Padding( - // 扩大点击范围 - padding: const EdgeInsets.all(8.0), - child: RichText( - key: const Key('name_edit'), - text: TextSpan( - text: '先对名称进行', - style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp14), - children: [ - TextSpan(text: '编辑', style: TextStyle(color: Theme.of(context).primaryColor)), - ], - ) - ), + ), + ], ), ), - Gaps.vGap24, + Gaps.vGap32, Expanded( child: _goodsSizeList.isEmpty ? const StateLayout( type: StateType.goods, hintText: '暂无商品规格', - ) : ListView.builder( - itemCount: _goodsSizeList.length, - itemExtent: 107.0, - itemBuilder: (_, index) => _buildGoodsSizeItem(index), + ) : SlidableAutoCloseBehavior( + child: ListView.builder( + itemCount: _goodsSizeList.length, + itemExtent: 107.0, + itemBuilder: (_, index) => _buildGoodsSizeItem(index), + ), ), ), Padding( @@ -166,7 +146,7 @@ class _GoodsSizePageState extends State { ), ); } - + /// design/4商品/index.html#artboard19 Widget _buildGoodsSizeItem(int index) { @@ -199,7 +179,7 @@ class _GoodsSizePageState extends State { children: [ Offstage( offstage: _goodsSizeList[index].reducePrice.isEmpty, - child: _buildGoodsTag(Theme.of(context).errorColor, '立减${_goodsSizeList[index].reducePrice}元'), + child: _buildGoodsTag(Theme.of(context).colorScheme.error, '立减${_goodsSizeList[index].reducePrice}元'), ), Opacity( opacity: _goodsSizeList[index].currencyPrice.isEmpty ? 0.0 : 1.0, @@ -207,7 +187,7 @@ class _GoodsSizePageState extends State { ) ], ), - Gaps.vGap16, + const Spacer(), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -232,12 +212,7 @@ class _GoodsSizePageState extends State { // item装饰 widget = InkWell( onTap: () { - /// 如果侧滑菜单打开,关闭侧滑菜单。否则跳转 - if (_slidableController.activeState != null) { - _slidableController.activeState.close(); - } else { - NavigatorUtils.push(context, GoodsRouter.goodsSizeEditPage); - } + NavigatorUtils.push(context, GoodsRouter.goodsSizeEditPage); }, child: Padding( padding: const EdgeInsets.only(left: 16.0, top: 16.0), @@ -245,42 +220,41 @@ class _GoodsSizePageState extends State { decoration: BoxDecoration( border: Border( bottom: Divider.createBorderSide(context, width: 0.8), - ) + ), ), child: Padding( padding: const EdgeInsets.only(right: 16.0, bottom: 16.0), - child: widget + child: widget, ), ), ), ); - // 侧滑删除 return Slidable( key: Key(index.toString()), - controller: _slidableController, - actionPane: const SlidableDrawerActionPane(), - actionExtentRatio: 0.20, - ///右侧的action - secondaryActions: [ - SlideAction( - child: Semantics( - label: '删除', - child: Container( - width: 72.0, - padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: LoadAssetImage('goods/goods_delete', key: Key('delete_$index'),), + endActionPane: ActionPane( + motion: const DrawerMotion(), + extentRatio: 0.20, + children: [ + CustomSlidableAction( + backgroundColor: Theme.of(context).colorScheme.error, + child: Semantics( + label: '删除', + child: LoadAssetImage( + 'goods/goods_delete', + key: Key('delete_$index'), + width: 24.0, + ), ), + onPressed: (context) { + setState(() { + _goodsSizeList.removeAt(index); + }); + }, ), - color: Theme.of(context).errorColor, - onTap: () { - setState(() { - _goodsSizeList.removeAt(index); - }); - }, - ), - ], - child: widget + ], + ), + child: widget, ); } @@ -296,8 +270,25 @@ class _GoodsSizePageState extends State { alignment: Alignment.center, child: Text( text, - style: const TextStyle(color: Colors.white, fontSize: Dimens.font_sp10, height: 1.1,), + style: TextStyle(color: Colors.white, fontSize: Dimens.font_sp10, height: Device.isAndroid ? 1.1 : null,), ), ); } + + void _showGoodsSizeDialog() { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext context) { + return GoodsSizeDialog( + onPressed: (name) { + setState(() { + _sizeName = name; + _isEdit = true; + }); + }, + ); + }, + ); + } } diff --git a/lib/goods/page/qr_code_scanner_page.dart b/lib/goods/page/qr_code_scanner_page.dart index 205e01201..dfb6545a7 100644 --- a/lib/goods/page/qr_code_scanner_page.dart +++ b/lib/goods/page/qr_code_scanner_page.dart @@ -1,5 +1,4 @@ - import 'dart:io'; import 'package:flutter/material.dart'; @@ -9,7 +8,7 @@ import 'package:qr_code_scanner/qr_code_scanner.dart'; class QrCodeScannerPage extends StatefulWidget { - const QrCodeScannerPage({Key key}) : super(key: key); + const QrCodeScannerPage({super.key}); @override _QrCodeScannerPageState createState() => _QrCodeScannerPageState(); @@ -17,8 +16,7 @@ class QrCodeScannerPage extends StatefulWidget { class _QrCodeScannerPageState extends State { final GlobalKey qrKey = GlobalKey(debugLabel: 'QR'); - Barcode result; - QRViewController controller; + QRViewController? controller; /// In order to get hot reload to work we need to pause the camera if the platform /// is android, or resume the camera if the platform is iOS. @@ -26,9 +24,10 @@ class _QrCodeScannerPageState extends State { void reassemble() { super.reassemble(); if (Platform.isAndroid) { - controller.pauseCamera(); + controller?.pauseCamera(); + controller?.resumeCamera(); } else if (Platform.isIOS) { - controller.resumeCamera(); + controller?.resumeCamera(); } } @@ -46,7 +45,6 @@ class _QrCodeScannerPageState extends State { key: qrKey, onQRViewCreated: _onQRViewCreated, overlay: QrScannerOverlayShape( - borderColor: Colors.red, borderRadius: 10, borderLength: 20, borderWidth: 5, @@ -62,9 +60,7 @@ class _QrCodeScannerPageState extends State { child: IconButton( icon: const Icon(Icons.highlight_outlined, size: 32, color: Colors.white,), onPressed: () { - if (controller != null) { - controller.toggleFlash(); - } + controller?.toggleFlash(); }, ), ), @@ -83,10 +79,23 @@ class _QrCodeScannerPageState extends State { ); } - void _onQRViewCreated(QRViewController controller) { - this.controller = controller; - controller.scannedDataStream.listen((scanData) { - NavigatorUtils.goBackWithParams(context, result.code); + void _onQRViewCreated(QRViewController? controller) { + setState(() { + this.controller = controller; + if (Platform.isAndroid) { + controller?.pauseCamera(); + controller?.resumeCamera(); + } else if (Platform.isIOS) { + controller?.resumeCamera(); + } + }); + controller?.scannedDataStream.listen((scanData) { + /// 避免扫描结果多次回调 + controller.dispose(); + if (!mounted) { + return; + } + NavigatorUtils.goBackWithParams(context, scanData.code ?? ''); }); } diff --git a/lib/goods/provider/goods_page_provider.dart b/lib/goods/provider/goods_page_provider.dart index 20f6c8e97..59bb2cc73 100644 --- a/lib/goods/provider/goods_page_provider.dart +++ b/lib/goods/provider/goods_page_provider.dart @@ -27,4 +27,4 @@ class GoodsPageProvider extends ChangeNotifier { _goodsCountList[index] = count; notifyListeners(); } -} \ No newline at end of file +} diff --git a/lib/goods/provider/goods_sort_provider.dart b/lib/goods/provider/goods_sort_provider.dart index 7c9f1960e..d3ab3128c 100644 --- a/lib/goods/provider/goods_sort_provider.dart +++ b/lib/goods/provider/goods_sort_provider.dart @@ -2,6 +2,8 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_deer/generated/json/base/json_convert_content.dart'; +import 'package:flutter_deer/goods/models/goods_sort_entity.dart'; class GoodsSortProvider extends ChangeNotifier { @@ -12,13 +14,13 @@ class GoodsSortProvider extends ChangeNotifier { final List _myTabs = [const Tab(text: '请选择'), const Tab(text: ''), const Tab(text: '')]; List get myTabs => _myTabs; - List _mGoodsSort = []; - List _mGoodsSort1 = []; - List _mGoodsSort2 = []; + List _mGoodsSort = []; + List _mGoodsSort1 = []; + List _mGoodsSort2 = []; /// 当前列表数据 - List _mList = []; - List get mList => _mList; + List _mList = []; + List get mList => _mList; /// 三级联动选择的position final List _positions = [0, 0, 0]; @@ -74,15 +76,15 @@ class GoodsSortProvider extends ChangeNotifier { // 模拟数据,数据为固定的三个列表 rootBundle.loadString('assets/data/sort_0.json').then((String value) { - _mGoodsSort = json.decode(value) as List; + _mGoodsSort = JsonConvert.fromJsonAsT>(json.decode(value)) ?? []; _mList = _mGoodsSort; notifyListeners(); }); rootBundle.loadString('assets/data/sort_1.json').then((String value) { - _mGoodsSort1 = json.decode(value) as List; + _mGoodsSort1 = JsonConvert.fromJsonAsT>(json.decode(value)) ?? []; }); rootBundle.loadString('assets/data/sort_2.json').then((String value) { - _mGoodsSort2 = json.decode(value) as List; + _mGoodsSort2 = JsonConvert.fromJsonAsT>(json.decode(value)) ?? []; }); } -} \ No newline at end of file +} diff --git a/lib/goods/widgets/goods_add_menu.dart b/lib/goods/widgets/goods_add_menu.dart new file mode 100644 index 000000000..56e90e302 --- /dev/null +++ b/lib/goods/widgets/goods_add_menu.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_deer/goods/goods_router.dart'; +import 'package:flutter_deer/res/resources.dart'; +import 'package:flutter_deer/routers/fluro_navigator.dart'; +import 'package:flutter_deer/util/theme_utils.dart'; +import 'package:flutter_deer/widgets/load_image.dart'; + +class GoodsAddMenu extends StatefulWidget { + + const GoodsAddMenu({ + super.key, + }); + + @override + _GoodsAddMenuState createState() => _GoodsAddMenuState(); +} + +class _GoodsAddMenuState extends State with SingleTickerProviderStateMixin { + + late AnimationController _controller; + late Animation _scaleAnimation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(milliseconds: 200), + vsync: this, + ); + + _scaleAnimation = Tween(begin: 0.0, end: 1.0).animate(_controller); + _controller.forward(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final Color backgroundColor = context.backgroundColor; + final Color? iconColor = ThemeUtils.getIconColor(context); + + final Widget body = Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.only(right: 12.0), + child: LoadAssetImage('goods/jt', width: 8.0, height: 4.0, + color: ThemeUtils.getDarkColor(context, Colours.dark_bg_color), + ), + ), + SizedBox( + width: 120.0, + height: 40.0, + child: TextButton.icon( + onPressed: () { + NavigatorUtils.push(context, '${GoodsRouter.goodsEditPage}?isAdd=true&isScan=true', replace: true); + }, + icon: LoadAssetImage('goods/scanning', width: 16.0, height: 16.0, color: iconColor,), + label: const Text('扫码添加'), + style: TextButton.styleFrom( + foregroundColor: Theme.of(context).textTheme.bodyMedium?.color, + disabledForegroundColor: Theme.of(context).textTheme.bodyMedium?.color?.withOpacity(0.12), + backgroundColor: backgroundColor, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only(topLeft: Radius.circular(8.0), topRight: Radius.circular(8.0)), + ), + ), + ), + ), + Container(width: 120.0, height: 0.6, color: Colours.line), + SizedBox( + width: 120.0, + height: 40.0, + child: TextButton.icon( + onPressed: () { + NavigatorUtils.push(context, '${GoodsRouter.goodsEditPage}?isAdd=true', replace: true); + }, + icon: LoadAssetImage('goods/add2', width: 16.0, height: 16.0, color: iconColor,), + label: const Text('添加商品'), + style: TextButton.styleFrom( + foregroundColor: Theme.of(context).textTheme.bodyMedium?.color, + disabledForegroundColor: Theme.of(context).textTheme.bodyMedium?.color?.withOpacity(0.12), + backgroundColor: backgroundColor, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only(bottomLeft: Radius.circular(8.0), bottomRight: Radius.circular(8.0)), + ), + ), + ), + ), + ], + ); + + return AnimatedBuilder( + animation: _scaleAnimation, + builder: (_, child) { + return Transform.scale( + scale: _scaleAnimation.value, + alignment: Alignment.topRight, + child: child, + ); + }, + child: body, + ); + } + + +} diff --git a/lib/goods/widgets/goods_delete_bottom_sheet.dart b/lib/goods/widgets/goods_delete_bottom_sheet.dart index c5ec06052..7118667fd 100644 --- a/lib/goods/widgets/goods_delete_bottom_sheet.dart +++ b/lib/goods/widgets/goods_delete_bottom_sheet.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; @@ -8,11 +7,11 @@ import 'package:flutter_deer/widgets/my_button.dart'; class GoodsDeleteBottomSheet extends StatelessWidget { const GoodsDeleteBottomSheet({ - Key key, - @required this.onTapDelete, - }): super(key: key); + super.key, + required this.onTapDelete, + }); - final Function onTapDelete; + final VoidCallback onTapDelete; @override Widget build(BuildContext context) { @@ -33,7 +32,7 @@ class GoodsDeleteBottomSheet extends StatelessWidget { Gaps.line, MyButton( minHeight: 54.0, - textColor: Theme.of(context).errorColor, + textColor: Theme.of(context).colorScheme.error, text: '确认删除', backgroundColor: Colors.transparent, onPressed: () { @@ -56,4 +55,4 @@ class GoodsDeleteBottomSheet extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/goods/widgets/goods_item.dart b/lib/goods/widgets/goods_item.dart index bc04a43bb..752d2df0a 100644 --- a/lib/goods/widgets/goods_item.dart +++ b/lib/goods/widgets/goods_item.dart @@ -1,10 +1,10 @@ - import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_deer/goods/models/goods_item_entity.dart'; import 'package:flutter_deer/res/resources.dart'; -import 'package:flutter_deer/util/theme_utils.dart'; +import 'package:flutter_deer/util/device_utils.dart'; import 'package:flutter_deer/util/other_utils.dart'; +import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/widgets/load_image.dart'; import 'package:flutter_deer/widgets/my_button.dart'; @@ -15,17 +15,18 @@ import 'menu_reveal.dart'; class GoodsItem extends StatelessWidget { const GoodsItem({ - Key key, - @required this.item, - @required this.index, - @required this.selectIndex, - @required this.onTapMenu, - @required this.onTapEdit, - @required this.onTapOperation, - @required this.onTapDelete, - @required this.onTapMenuClose, - @required this.animation - }): super(key: key); + super.key, + required this.item, + required this.index, + required this.selectIndex, + required this.onTapMenu, + required this.onTapEdit, + required this.onTapOperation, + required this.onTapDelete, + required this.onTapMenuClose, + required this.animation, + required this.heroTag, + }); final GoodsItemEntity item; final int index; @@ -36,13 +37,19 @@ class GoodsItem extends StatelessWidget { final VoidCallback onTapDelete; final VoidCallback onTapMenuClose; final Animation animation; + final String heroTag; @override Widget build(BuildContext context) { final Row child = Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - ExcludeSemantics(child: LoadImage(item.icon, width: 72.0, height: 72.0)), + ExcludeSemantics( + child: Hero( + tag: heroTag, + child: LoadImage(item.icon, width: 72.0, height: 72.0), + ), + ), Gaps.hGap8, Expanded( child: Column( @@ -61,7 +68,7 @@ class GoodsItem extends StatelessWidget { visible: item.type % 3 == 0, child: _GoodsItemTag( text: '立减', - color: Theme.of(context).errorColor, + color: Theme.of(context).colorScheme.error, ), ), Opacity( @@ -87,6 +94,7 @@ class GoodsItem extends StatelessWidget { container: true, label: '商品操作菜单', child: GestureDetector( + onTap: onTapMenu, child: Container( key: Key('goods_menu_item_$index'), width: 44.0, @@ -95,14 +103,13 @@ class GoodsItem extends StatelessWidget { padding: const EdgeInsets.only(left: 28.0, bottom: 28.0), child: const LoadAssetImage('goods/ellipsis'), ), - onTap: onTapMenu, ), ), Padding( padding: const EdgeInsets.only(top: 10.0), child: Text( '特产美味', - style: Theme.of(context).textTheme.subtitle2, + style: Theme.of(context).textTheme.titleSmall, ), ) ], @@ -137,10 +144,10 @@ class GoodsItem extends StatelessWidget { child: AnimatedBuilder( animation: animation, child: _buildGoodsMenuContent(context), - builder:(_, Widget child) { + builder: (_, Widget? child) { return MenuReveal( revealPercent: animation.value, - child: child + child: child! ); } ), @@ -153,7 +160,7 @@ class GoodsItem extends StatelessWidget { return InkWell( onTap: onTapMenuClose, - child: Container( + child: ColoredBox( color: isDark ? const Color(0xB34D4D4D) : const Color(0x4D000000), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, @@ -206,12 +213,11 @@ class GoodsItem extends StatelessWidget { class _GoodsItemTag extends StatelessWidget { const _GoodsItemTag({ - Key key, - this.color, - this.text, - }): super(key: key); + required this.color, + required this.text, + }); - final Color color; + final Color? color; final String text; @override @@ -227,10 +233,10 @@ class _GoodsItemTag extends StatelessWidget { alignment: Alignment.center, child: Text( text, - style: const TextStyle( + style: TextStyle( color: Colors.white, fontSize: Dimens.font_sp10, - height: 1.1, + height: Device.isAndroid ? 1.1 : null, ), ), ); diff --git a/lib/goods/widgets/goods_size_dialog.dart b/lib/goods/widgets/goods_size_dialog.dart index 534943fcc..a034eb307 100644 --- a/lib/goods/widgets/goods_size_dialog.dart +++ b/lib/goods/widgets/goods_size_dialog.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/theme_utils.dart'; @@ -8,11 +7,11 @@ import 'package:flutter_deer/widgets/base_dialog.dart'; class GoodsSizeDialog extends StatefulWidget { const GoodsSizeDialog({ - Key key, + super.key, this.onPressed, - }) : super(key : key); + }); - final Function(String) onPressed; + final void Function(String)? onPressed; @override _GoodsSizeDialog createState() => _GoodsSizeDialog(); @@ -44,7 +43,6 @@ class _GoodsSizeDialog extends State { child: TextField( autofocus: true, controller: _controller, - maxLines: 1, decoration: const InputDecoration( isDense: true, contentPadding: EdgeInsets.symmetric(horizontal: 16.0), @@ -56,8 +54,8 @@ class _GoodsSizeDialog extends State { ), onPressed: () { NavigatorUtils.goBack(context); - widget.onPressed(_controller.text); + widget.onPressed?.call(_controller.text); }, ); } -} \ No newline at end of file +} diff --git a/lib/goods/widgets/goods_sort_bottom_sheet.dart b/lib/goods/widgets/goods_sort_bottom_sheet.dart index d9184f61a..291fbd75d 100644 --- a/lib/goods/widgets/goods_sort_bottom_sheet.dart +++ b/lib/goods/widgets/goods_sort_bottom_sheet.dart @@ -1,8 +1,8 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/goods/provider/goods_sort_provider.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; +import 'package:flutter_deer/util/other_utils.dart'; import 'package:flutter_deer/util/screen_utils.dart'; import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/widgets/load_image.dart'; @@ -13,12 +13,12 @@ import 'package:provider/provider.dart'; class GoodsSortBottomSheet extends StatefulWidget { const GoodsSortBottomSheet({ - Key key, - @required this.provider, - @required this.onSelected, - }): super(key: key); + super.key, + required this.provider, + required this.onSelected, + }); - final Function(String, String) onSelected; + final void Function(String, String) onSelected; /// 临时状态 final GoodsSortProvider provider; @@ -28,7 +28,7 @@ class GoodsSortBottomSheet extends StatefulWidget { class GoodsSortBottomSheetState extends State with SingleTickerProviderStateMixin { - TabController _tabController; + TabController? _tabController; final ScrollController _controller = ScrollController(); @override @@ -37,13 +37,13 @@ class GoodsSortBottomSheetState extends State with SingleT _tabController = TabController(vsync: this, length: 3); WidgetsBinding.instance.addPostFrameCallback((_) { widget.provider.initData(); - _tabController.animateTo(widget.provider.index, duration: const Duration(microseconds: 0)); + _tabController?.animateTo(widget.provider.index, duration: Duration.zero); }); } @override void dispose() { - _tabController.dispose(); + _tabController?.dispose(); _controller.dispose(); super.dispose(); } @@ -61,33 +61,33 @@ class GoodsSortBottomSheetState extends State with SingleT return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - child, + child!, Gaps.line, - Container( + TabBar( + controller: _tabController, + isScrollable: true, + onTap: (index) { + if (provider.myTabs[index].text.nullSafe.isEmpty) { + // 拦截点击事件 + _tabController?.animateTo(provider.index); + return; + } + provider.setList(index); + provider.setIndex(index); + _controller.animateTo( + provider.positions[provider.index] * 48.0, + duration: const Duration(milliseconds: 10), + curve: Curves.ease, + ); + }, + indicatorSize: TabBarIndicatorSize.label, + unselectedLabelColor: context.isDark ? Colours.text_gray : Colours.text, + labelColor: Theme.of(context).primaryColor, // 隐藏点击效果 - color: context.dialogBackgroundColor, - child: TabBar( - controller: _tabController, - isScrollable: true, - onTap: (index) { - if (provider.myTabs[index].text.isEmpty) { - // 拦截点击事件 - _tabController.animateTo(provider.index); - return; - } - provider.setList(index); - provider.setIndex(index); - _controller.animateTo( - provider.positions[provider.index] * 48.0, - duration: const Duration(milliseconds: 10), - curve: Curves.ease, - ); - }, - indicatorSize: TabBarIndicatorSize.label, - unselectedLabelColor: context.isDark ? Colours.text_gray : Colours.text, - labelColor: Theme.of(context).primaryColor, - tabs: provider.myTabs, - ), + overlayColor: MaterialStateProperty.resolveWith((Set states) { + return Colors.transparent; + },), + tabs: provider.myTabs, ), Gaps.line, Expanded( @@ -115,6 +115,9 @@ class GoodsSortBottomSheetState extends State with SingleT ), ), Positioned( + right: 16.0, + top: 16.0, + bottom: 16.0, child: InkWell( onTap: () => NavigatorUtils.goBack(context), child: const SizedBox( @@ -123,9 +126,6 @@ class GoodsSortBottomSheetState extends State with SingleT child: LoadAssetImage('goods/icon_dialog_close'), ), ), - right: 16.0, - top: 16.0, - bottom: 16.0, ) ], ), @@ -136,7 +136,7 @@ class GoodsSortBottomSheetState extends State with SingleT } Widget _buildItem(GoodsSortProvider provider, int index) { - final bool flag = provider.mList[index]['name'] == provider.myTabs[provider.index].text; + final bool flag = provider.mList[index].name == provider.myTabs[provider.index].text; return InkWell( child: Container( padding: const EdgeInsets.symmetric(horizontal: 16.0), @@ -144,7 +144,7 @@ class GoodsSortBottomSheetState extends State with SingleT child: Row( children: [ Text( - provider.mList[index]['name'] as String, + provider.mList[index].name, style: flag ? TextStyle( fontSize: Dimens.font_sp14, color: Theme.of(context).primaryColor, @@ -158,18 +158,18 @@ class GoodsSortBottomSheetState extends State with SingleT ), ), onTap: () { - provider.myTabs[provider.index] = Tab(text: provider.mList[index]['name'] as String); + provider.myTabs[provider.index] = Tab(text: provider.mList[index].name); provider.positions[provider.index] = index; provider.indexIncrement(); provider.setListAndChangeTab(); if (provider.index > 2) { provider.setIndex(2); - widget.onSelected(provider.mList[index]['id'] as String, provider.mList[index]['name'] as String); + widget.onSelected(provider.mList[index].id, provider.mList[index].name); NavigatorUtils.goBack(context); } _controller.animateTo(0.0, duration: const Duration(milliseconds: 100), curve: Curves.ease); - _tabController.animateTo(provider.index); + _tabController?.animateTo(provider.index); }, ); } diff --git a/lib/goods/widgets/goods_sort_menu.dart b/lib/goods/widgets/goods_sort_menu.dart index 0ee3627ea..969e77f80 100644 --- a/lib/goods/widgets/goods_sort_menu.dart +++ b/lib/goods/widgets/goods_sort_menu.dart @@ -1,23 +1,23 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; +import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/theme_utils.dart'; class GoodsSortMenu extends StatefulWidget { const GoodsSortMenu({ - Key key, - @required this.data, - @required this.sortIndex, - @required this.height, - @required this.onSelected, - }): super(key: key); + super.key, + required this.data, + required this.sortIndex, + required this.height, + required this.onSelected, + }); final List data; final int sortIndex; final double height; - final Function(int, String) onSelected; + final void Function(int, String) onSelected; @override _GoodsSortMenuState createState() => _GoodsSortMenuState(); @@ -25,35 +25,41 @@ class GoodsSortMenu extends StatefulWidget { class _GoodsSortMenuState extends State with SingleTickerProviderStateMixin { - AnimationController _controller; + late final AnimationController _controller = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + )..forward(); + + late final Animation _animation = CurvedAnimation( + parent: _controller, + curve: Curves.easeInCubic, + reverseCurve: Curves.easeOutCubic, + ); @override void initState() { super.initState(); - _controller = AnimationController( - duration: const Duration(milliseconds: 300), - vsync: this, - ); - _controller.forward(); + _animation.addStatusListener(_statusListener); + } + + void _statusListener(AnimationStatus status) { + if (status == AnimationStatus.dismissed) { + /// 菜单动画停止,关闭菜单。 + NavigatorUtils.goBack(context); + } } @override void dispose() { + _animation.removeStatusListener(_statusListener); _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { - final Color backgroundColor = context.backgroundColor; - final Animation animation = CurvedAnimation( - parent: _controller, - curve: Curves.easeInCubic, - reverseCurve: Curves.easeOutCubic, - ); - final Widget listView = ListView.builder( physics: const ClampingScrollPhysics(), itemCount: widget.data.length + 1, @@ -66,15 +72,13 @@ class _GoodsSortMenuState extends State with SingleTickerProvider ); return FadeTransition( - opacity: animation, + opacity: _animation, child: Container( color: const Color(0x99000000), height: widget.height - 12.0, - child: SlideTransition( - position: Tween( - begin: const Offset(0.0, -1.0), - end: Offset.zero, - ).animate(animation), + child: ScaleTransition( + scale: _animation, + alignment: Alignment.topCenter, child: listView, ), ), diff --git a/lib/goods/widgets/menu_reveal.dart b/lib/goods/widgets/menu_reveal.dart index 05148d275..946da6cbb 100644 --- a/lib/goods/widgets/menu_reveal.dart +++ b/lib/goods/widgets/menu_reveal.dart @@ -7,10 +7,10 @@ import 'package:flutter/material.dart'; class MenuReveal extends StatelessWidget { const MenuReveal({ - Key key, - this.revealPercent, - this.child - }): super(key: key); + super.key, + required this.revealPercent, + required this.child + }); final double revealPercent; final Widget child; @@ -50,4 +50,4 @@ class CircleRevealClipper extends CustomClipper { return true; } -} \ No newline at end of file +} diff --git a/lib/home/home_page.dart b/lib/home/home_page.dart index bcc701b63..91722e8ac 100644 --- a/lib/home/home_page.dart +++ b/lib/home/home_page.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/goods/page/goods_page.dart'; import 'package:flutter_deer/home/provider/home_provider.dart'; @@ -13,24 +12,24 @@ import 'package:provider/provider.dart'; class Home extends StatefulWidget { - const Home({Key key}) : super(key: key); + const Home({super.key}); @override _HomeState createState() => _HomeState(); } -class _HomeState extends State { +class _HomeState extends State with RestorationMixin{ static const double _imageSize = 25.0; - List _pageList; + late List _pageList; final List _appBarTitles = ['订单', '商品', '统计', '店铺']; final PageController _pageController = PageController(); HomeProvider provider = HomeProvider(); - List _list; - List _listDark; + List? _list; + List? _listDark; @override void initState() { @@ -55,7 +54,7 @@ class _HomeState extends State { List _buildBottomNavigationBarItem() { if (_list == null) { - const _tabImages = [ + const tabImages = [ [ LoadAssetImage('home/icon_order', width: _imageSize, color: Colours.unselected_item_color,), LoadAssetImage('home/icon_order', width: _imageSize, color: Colours.app_main,), @@ -73,20 +72,21 @@ class _HomeState extends State { LoadAssetImage('home/icon_shop', width: _imageSize, color: Colours.app_main,), ] ]; - _list = List.generate(_tabImages.length, (i) { + _list = List.generate(tabImages.length, (i) { return BottomNavigationBarItem( - icon: _tabImages[i][0], - activeIcon: _tabImages[i][1], + icon: tabImages[i][0], + activeIcon: tabImages[i][1], label: _appBarTitles[i], + tooltip: _appBarTitles[i], ); }); } - return _list; + return _list!; } List _buildDarkBottomNavigationBarItem() { if (_listDark == null) { - const _tabImagesDark = [ + const tabImagesDark = [ [ LoadAssetImage('home/icon_order', width: _imageSize), LoadAssetImage('home/icon_order', width: _imageSize, color: Colours.dark_app_main,), @@ -105,15 +105,16 @@ class _HomeState extends State { ] ]; - _listDark = List.generate(_tabImagesDark.length, (i) { + _listDark = List.generate(tabImagesDark.length, (i) { return BottomNavigationBarItem( - icon: _tabImagesDark[i][0], - activeIcon: _tabImagesDark[i][1], + icon: tabImagesDark[i][0], + activeIcon: tabImagesDark[i][1], label: _appBarTitles[i], + tooltip: _appBarTitles[i], ); }); } - return _listDark; + return _listDark!; } @override @@ -152,4 +153,12 @@ class _HomeState extends State { ); } + @override + String? get restorationId => 'home'; + + @override + void restoreState(RestorationBucket? oldBucket, bool initialRestore) { + registerForRestoration(provider, 'BottomNavigationBarCurrentIndex'); + } + } diff --git a/lib/home/provider/home_provider.dart b/lib/home/provider/home_provider.dart index eb891bde6..6b2700ac4 100644 --- a/lib/home/provider/home_provider.dart +++ b/lib/home/provider/home_provider.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -class HomeProvider extends ValueNotifier { +class HomeProvider extends RestorableInt { HomeProvider() : super(0); -} \ No newline at end of file +} diff --git a/lib/home/splash_page.dart b/lib/home/splash_page.dart index c28e768aa..1a0b378a9 100644 --- a/lib/home/splash_page.dart +++ b/lib/home/splash_page.dart @@ -1,35 +1,32 @@ - import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter_deer/common/common.dart'; import 'package:flutter_deer/demo/demo_page.dart'; import 'package:flutter_deer/login/login_router.dart'; +import 'package:flutter_deer/res/constant.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/app_navigator.dart'; import 'package:flutter_deer/util/device_utils.dart'; import 'package:flutter_deer/util/image_utils.dart'; import 'package:flutter_deer/util/theme_utils.dart'; +import 'package:flutter_deer/widgets/fractionally_aligned_sized_box.dart'; import 'package:flutter_deer/widgets/load_image.dart'; -import 'package:flutter_slidable/flutter_slidable.dart'; +import 'package:flutter_swiper_null_safety_flutter3/flutter_swiper_null_safety_flutter3.dart'; import 'package:quick_actions/quick_actions.dart'; import 'package:rxdart/rxdart.dart'; -import 'package:flutter_swiper/flutter_swiper.dart'; import 'package:sp_util/sp_util.dart'; class SplashPage extends StatefulWidget { - - const SplashPage({Key key}) : super(key: key); + const SplashPage({super.key}); @override _SplashPageState createState() => _SplashPageState(); } class _SplashPageState extends State { - int _status = 0; final List _guideList = ['app_start_1', 'app_start_2', 'app_start_3']; - StreamSubscription _subscription; + StreamSubscription? _subscription; @override void initState() { @@ -39,17 +36,18 @@ class _SplashPageState extends State { /// 两种方法各有优劣 await SpUtil.getInstance(); await Device.initDeviceInfo(); - if (SpUtil.getBool(Constant.keyGuide, defValue: true)) { + if (SpUtil.getBool(Constant.keyGuide, defValue: true)!) { /// 预先缓存图片,避免直接使用时因为首次加载造成闪动 - _guideList.forEach((image) { + void precacheImages(String image) { precacheImage(ImageUtils.getAssetImage(image, format: ImageFormat.webp), context); - }); + } + _guideList.forEach(precacheImages); } _initSplash(); }); if (Device.isAndroid) { - final QuickActions quickActions = QuickActions(); + const QuickActions quickActions = QuickActions(); quickActions.initialize((String shortcutType) async { if (shortcutType == 'demo') { AppNavigator.pushReplacement(context, const DemoPage()); @@ -73,7 +71,7 @@ class _SplashPageState extends State { void _initSplash() { _subscription = Stream.value(1).delay(const Duration(milliseconds: 1500)).listen((_) { - if (SpUtil.getBool(Constant.keyGuide, defValue: true) || Constant.isDriverTest) { + if (SpUtil.getBool(Constant.keyGuide, defValue: true)! || Constant.isDriverTest) { SpUtil.putBool(Constant.keyGuide, false); _initGuide(); } else { @@ -90,13 +88,13 @@ class _SplashPageState extends State { Widget build(BuildContext context) { return Material( color: context.backgroundColor, - child: _status == 0 ? - FractionallyAlignedSizedBox( + child: _status == 0 ? + const FractionallyAlignedSizedBox( heightFactor: 0.3, widthFactor: 0.33, leftFactor: 0.33, bottomFactor: 0, - child: const LoadAssetImage('logo') + child: LoadAssetImage('logo') ) : Swiper( key: const Key('swiper'), diff --git a/lib/home/webview_page.dart b/lib/home/webview_page.dart index 18f996ea7..06e2dfd74 100644 --- a/lib/home/webview_page.dart +++ b/lib/home/webview_page.dart @@ -1,19 +1,17 @@ - import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_deer/res/gaps.dart'; -import 'package:flutter_deer/util/device_utils.dart'; import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:webview_flutter/webview_flutter.dart'; class WebViewPage extends StatefulWidget { const WebViewPage({ - Key key, - @required this.title, - @required this.url, - }) : super(key: key); + super.key, + required this.title, + required this.url, + }); final String title; final String url; @@ -24,65 +22,59 @@ class WebViewPage extends StatefulWidget { class _WebViewPageState extends State { - final Completer _controller = Completer(); + late final WebViewController _controller; int _progressValue = 0; @override void initState() { super.initState(); - // Enable hybrid composition. - if (Device.isAndroid) { - WebView.platform = SurfaceAndroidWebView(); - } + _controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setNavigationDelegate( + NavigationDelegate( + onProgress: (int progress) { + if (!mounted) { + return; + } + debugPrint('WebView is loading (progress : $progress%)'); + setState(() { + _progressValue = progress; + }); + }, + ), + ) + ..loadRequest(Uri.parse(widget.url)); } @override Widget build(BuildContext context) { - return FutureBuilder( - future: _controller.future, - builder: (context, snapshot) { - return WillPopScope( - onWillPop: () async { - if (snapshot.hasData) { - final bool canGoBack = await snapshot.data.canGoBack(); - if (canGoBack) { - // 网页可以返回时,优先返回上一页 - await snapshot.data.goBack(); - return Future.value(false); - } - } - return Future.value(true); - }, - child: Scaffold( - appBar: MyAppBar( - centerTitle: widget.title, - ), - body: Stack( - children: [ - WebView( - initialUrl: widget.url, - javascriptMode: JavascriptMode.unrestricted, - allowsInlineMediaPlayback: true, - onWebViewCreated: (WebViewController webViewController) { - _controller.complete(webViewController); - }, - onProgress: (int progress) { - print('WebView is loading (progress : $progress%)'); - setState(() { - _progressValue = progress; - }); - }, - ), - if (_progressValue != 100) LinearProgressIndicator( - value: _progressValue / 100, - backgroundColor: Colors.transparent, - minHeight: 2, - ) else Gaps.empty, - ], + return WillPopScope( + onWillPop: () async { + final bool canGoBack = await _controller.canGoBack(); + if (canGoBack) { + // 网页可以返回时,优先返回上一页 + await _controller.goBack(); + return Future.value(false); + } + return Future.value(true); + }, + child: Scaffold( + appBar: MyAppBar( + centerTitle: widget.title, + ), + body: Stack( + children: [ + WebViewWidget( + controller: _controller, ), - ), - ); - } + if (_progressValue != 100) LinearProgressIndicator( + value: _progressValue / 100, + backgroundColor: Colors.transparent, + minHeight: 2, + ) else Gaps.empty, + ], + ), + ), ); } diff --git a/lib/l10n/deer_localizations.dart b/lib/l10n/deer_localizations.dart new file mode 100644 index 000000000..477d7cde2 --- /dev/null +++ b/lib/l10n/deer_localizations.dart @@ -0,0 +1,234 @@ +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 'deer_localizations_en.dart'; +import 'deer_localizations_zh.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of DeerLocalizations +/// returned by `DeerLocalizations.of(context)`. +/// +/// Applications need to include `DeerLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/deer_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: DeerLocalizations.localizationsDelegates, +/// supportedLocales: DeerLocalizations.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 DeerLocalizations.supportedLocales +/// property. +abstract class DeerLocalizations { + DeerLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static DeerLocalizations? of(BuildContext context) { + return Localizations.of(context, DeerLocalizations); + } + + static const LocalizationsDelegate delegate = _DeerLocalizationsDelegate(); + + /// 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('zh')]; + + /// Title for the application + /// + /// In en, this message translates to: + /// **'Flutter Deer'** + String get title; + + /// Title for the Login page + /// + /// In en, this message translates to: + /// **'Verification Code Login'** + String get verificationCodeLogin; + + /// Password Login + /// + /// In en, this message translates to: + /// **'Password Login'** + String get passwordLogin; + + /// Login + /// + /// In en, this message translates to: + /// **'Login'** + String get login; + + /// Forgot Password + /// + /// In en, this message translates to: + /// **'Forgot Password'** + String get forgotPasswordLink; + + /// Please enter the password + /// + /// In en, this message translates to: + /// **'Please enter the password'** + String get inputPasswordHint; + + /// Please input username + /// + /// In en, this message translates to: + /// **'Please input username'** + String get inputUsernameHint; + + /// No account yet? Register now + /// + /// In en, this message translates to: + /// **'No account yet? Register now'** + String get noAccountRegisterLink; + + /// Register + /// + /// In en, this message translates to: + /// **'Register'** + String get register; + + /// Open your account + /// + /// In en, this message translates to: + /// **'Open your account'** + String get openYourAccount; + + /// Please enter phone number + /// + /// In en, this message translates to: + /// **'Please enter phone number'** + String get inputPhoneHint; + + /// Please enter verification code + /// + /// In en, this message translates to: + /// **'Please enter verification code'** + String get inputVerificationCodeHint; + + /// Please input valid mobile phone number + /// + /// In en, this message translates to: + /// **'Please input valid mobile phone number'** + String get inputPhoneInvalid; + + /// Not really sent, just log in! + /// + /// In en, this message translates to: + /// **'Not really sent, just log in!'** + String get verificationButton; + + /// Get verification code + /// + /// In en, this message translates to: + /// **'Get code'** + String get getVerificationCode; + + /// No description provided for @confirm. + /// + /// In en, this message translates to: + /// **'Confirm'** + String get confirm; + + /// Reset login password + /// + /// In en, this message translates to: + /// **'Reset Login Password'** + String get resetLoginPassword; + + /// Registered Tips + /// + /// In en, this message translates to: + /// **'Unregistered mobile phone number, please '** + String get registeredTips; +} + +class _DeerLocalizationsDelegate extends LocalizationsDelegate { + const _DeerLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupDeerLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode); + + @override + bool shouldReload(_DeerLocalizationsDelegate old) => false; +} + +DeerLocalizations lookupDeerLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'en': + return DeerLocalizationsEn(); + case 'zh': + return DeerLocalizationsZh(); + } + + throw FlutterError( + 'DeerLocalizations.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/l10n/deer_localizations_en.dart b/lib/l10n/deer_localizations_en.dart new file mode 100644 index 000000000..edf6f0d0b --- /dev/null +++ b/lib/l10n/deer_localizations_en.dart @@ -0,0 +1,64 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'deer_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class DeerLocalizationsEn extends DeerLocalizations { + DeerLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get title => 'Flutter Deer'; + + @override + String get verificationCodeLogin => 'Verification Code Login'; + + @override + String get passwordLogin => 'Password Login'; + + @override + String get login => 'Login'; + + @override + String get forgotPasswordLink => 'Forgot Password'; + + @override + String get inputPasswordHint => 'Please enter the password'; + + @override + String get inputUsernameHint => 'Please input username'; + + @override + String get noAccountRegisterLink => 'No account yet? Register now'; + + @override + String get register => 'Register'; + + @override + String get openYourAccount => 'Open your account'; + + @override + String get inputPhoneHint => 'Please enter phone number'; + + @override + String get inputVerificationCodeHint => 'Please enter verification code'; + + @override + String get inputPhoneInvalid => 'Please input valid mobile phone number'; + + @override + String get verificationButton => 'Not really sent, just log in!'; + + @override + String get getVerificationCode => 'Get code'; + + @override + String get confirm => 'Confirm'; + + @override + String get resetLoginPassword => 'Reset Login Password'; + + @override + String get registeredTips => 'Unregistered mobile phone number, please '; +} diff --git a/lib/l10n/deer_localizations_zh.dart b/lib/l10n/deer_localizations_zh.dart new file mode 100644 index 000000000..4c28d7a33 --- /dev/null +++ b/lib/l10n/deer_localizations_zh.dart @@ -0,0 +1,64 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'deer_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Chinese (`zh`). +class DeerLocalizationsZh extends DeerLocalizations { + DeerLocalizationsZh([String locale = 'zh']) : super(locale); + + @override + String get title => 'Flutter Deer'; + + @override + String get verificationCodeLogin => '验证码登录'; + + @override + String get passwordLogin => '密码登录'; + + @override + String get login => '登录'; + + @override + String get forgotPasswordLink => '忘记密码'; + + @override + String get inputPasswordHint => '请输入密码'; + + @override + String get inputUsernameHint => '请输入账号'; + + @override + String get noAccountRegisterLink => '还没账号?快去注册'; + + @override + String get register => '注册'; + + @override + String get openYourAccount => '开启你的账号'; + + @override + String get inputPhoneHint => '请输入手机号'; + + @override + String get inputVerificationCodeHint => '请输入验证码'; + + @override + String get inputPhoneInvalid => '请输入有效的手机号'; + + @override + String get verificationButton => '并没有真正发送哦,直接登录吧!'; + + @override + String get getVerificationCode => '获取验证码'; + + @override + String get confirm => '确认'; + + @override + String get resetLoginPassword => '重置登录密码'; + + @override + String get registeredTips => '提示:未注册账号的手机号,请先'; +} diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index 39d54b921..ca0de9b95 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -84,7 +84,7 @@ "type": "text", "placeholders": {} }, - "getVerificationCode": "Get verification code", + "getVerificationCode": "Get code", "@getVerificationCode": { "description": "Get verification code", "type": "text", diff --git a/lib/login/login_router.dart b/lib/login/login_router.dart index 1da01b71f..f192fa3ad 100644 --- a/lib/login/login_router.dart +++ b/lib/login/login_router.dart @@ -1,4 +1,3 @@ - import 'package:fluro/fluro.dart'; import 'package:flutter_deer/routers/i_router.dart'; @@ -26,4 +25,4 @@ class LoginRouter implements IRouterProvider{ router.define(updatePasswordPage, handler: Handler(handlerFunc: (_, __) => const UpdatePasswordPage())); } -} \ No newline at end of file +} diff --git a/lib/login/page/login_page.dart b/lib/login/page/login_page.dart index 5d2d69285..1c4c9416c 100644 --- a/lib/login/page/login_page.dart +++ b/lib/login/page/login_page.dart @@ -1,11 +1,7 @@ - -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:sp_util/sp_util.dart'; -import 'package:flutter_deer/common/common.dart'; -import 'package:flutter_gen/gen_l10n/deer_localizations.dart'; import 'package:flutter_deer/login/widgets/my_text_field.dart'; +import 'package:flutter_deer/res/constant.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/store/store_router.dart'; @@ -14,13 +10,15 @@ import 'package:flutter_deer/util/other_utils.dart'; import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; import 'package:flutter_deer/widgets/my_scroll_view.dart'; +import 'package:sp_util/sp_util.dart'; +import '../../l10n/deer_localizations.dart'; import '../login_router.dart'; /// design/1注册登录/index.html class LoginPage extends StatefulWidget { - const LoginPage({Key key}) : super(key: key); + const LoginPage({super.key}); @override _LoginPageState createState() => _LoginPageState(); @@ -35,9 +33,9 @@ class _LoginPageState extends State with ChangeNotifierMixin> changeNotifier() { + Map?>? changeNotifier() { final List callbacks = [_verify]; - return >{ + return ?>{ _nameController: callbacks, _passwordController: callbacks, _nodeText1: null, @@ -50,9 +48,9 @@ class _LoginPageState extends State with ChangeNotifierMixin with ChangeNotifierMixin with ChangeNotifierMixin get _buildBody => [ Text( - DeerLocalizations.of(context).passwordLogin, + DeerLocalizations.of(context)!.passwordLogin, style: TextStyles.textBold26, ), Gaps.vGap16, @@ -109,7 +107,7 @@ class _LoginPageState extends State with ChangeNotifierMixin with ChangeNotifierMixin NavigatorUtils.push(context, LoginRouter.resetPasswordPage), ), @@ -145,7 +142,7 @@ class _LoginPageState extends State with ChangeNotifierMixin _RegisterPageState(); @@ -32,9 +29,9 @@ class _RegisterPageState extends State with ChangeNotifierMixin> changeNotifier() { + Map?>? changeNotifier() { final List callbacks = [_verify]; - return >{ + return ?>{ _nameController: callbacks, _vCodeController: callbacks, _passwordController: callbacks, @@ -73,7 +70,7 @@ class _RegisterPageState extends State with ChangeNotifierMixin[_nodeText1, _nodeText2, _nodeText3]), @@ -87,7 +84,7 @@ class _RegisterPageState extends State with ChangeNotifierMixin _buildBody() { return [ Text( - DeerLocalizations.of(context).openYourAccount, + DeerLocalizations.of(context)!.openYourAccount, style: TextStyles.textBold26, ), Gaps.vGap16, @@ -97,7 +94,7 @@ class _RegisterPageState extends State with ChangeNotifierMixin with ChangeNotifierMixin with ChangeNotifierMixin _ResetPasswordPageState(); @@ -32,9 +30,9 @@ class _ResetPasswordPageState extends State with ChangeNotifi bool _clickable = false; @override - Map> changeNotifier() { + Map?>? changeNotifier() { final List callbacks = [_verify]; - return >{ + return ?>{ _nameController: callbacks, _vCodeController: callbacks, _passwordController: callbacks, @@ -66,14 +64,14 @@ class _ResetPasswordPageState extends State with ChangeNotifi } void _reset() { - Toast.show(DeerLocalizations.of(context).confirm); + Toast.show(DeerLocalizations.of(context)!.confirm); } @override Widget build(BuildContext context) { return Scaffold( appBar: MyAppBar( - title: DeerLocalizations.of(context).forgotPasswordLink, + title: DeerLocalizations.of(context)!.forgotPasswordLink, ), body: MyScrollView( keyboardConfig: Utils.getKeyboardActionsConfig(context, [_nodeText1, _nodeText2, _nodeText3]), @@ -87,7 +85,7 @@ class _ResetPasswordPageState extends State with ChangeNotifi List _buildBody() { return [ Text( - DeerLocalizations.of(context).resetLoginPassword, + DeerLocalizations.of(context)!.resetLoginPassword, style: TextStyles.textBold26, ), Gaps.vGap16, @@ -96,7 +94,7 @@ class _ResetPasswordPageState extends State with ChangeNotifi controller: _nameController, maxLength: 11, keyboardType: TextInputType.phone, - hintText: DeerLocalizations.of(context).inputPhoneHint, + hintText: DeerLocalizations.of(context)!.inputPhoneHint, ), Gaps.vGap8, MyTextField( @@ -107,21 +105,20 @@ class _ResetPasswordPageState extends State with ChangeNotifi return Future.value(true); }, maxLength: 6, - hintText: DeerLocalizations.of(context).inputVerificationCodeHint, + hintText: DeerLocalizations.of(context)!.inputVerificationCodeHint, ), Gaps.vGap8, MyTextField( focusNode: _nodeText3, isInputPwd: true, controller: _passwordController, - maxLength: 16, keyboardType: TextInputType.visiblePassword, - hintText: DeerLocalizations.of(context).inputPasswordHint, + hintText: DeerLocalizations.of(context)!.inputPasswordHint, ), Gaps.vGap24, MyButton( onPressed: _clickable ? _reset : null, - text: DeerLocalizations.of(context).confirm, + text: DeerLocalizations.of(context)!.confirm, ) ]; } diff --git a/lib/login/page/sms_login_page.dart b/lib/login/page/sms_login_page.dart index d5ec76296..bad41ae48 100644 --- a/lib/login/page/sms_login_page.dart +++ b/lib/login/page/sms_login_page.dart @@ -1,24 +1,21 @@ - -import 'dart:ui'; - +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/deer_localizations.dart'; -import 'package:flutter_deer/util/change_notifier_manage.dart'; +import 'package:flutter_deer/login/widgets/my_text_field.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; -import 'package:flutter_deer/util/toast_utils.dart'; +import 'package:flutter_deer/util/change_notifier_manage.dart'; import 'package:flutter_deer/util/other_utils.dart'; +import 'package:flutter_deer/util/toast_utils.dart'; import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; import 'package:flutter_deer/widgets/my_scroll_view.dart'; -import 'package:flutter_deer/login/widgets/my_text_field.dart'; +import '../../l10n/deer_localizations.dart'; import '../login_router.dart'; /// design/1注册登录/index.html#artboard4 class SMSLoginPage extends StatefulWidget { - - const SMSLoginPage({Key key}) : super(key: key); + const SMSLoginPage({super.key}); @override _SMSLoginPageState createState() => _SMSLoginPageState(); @@ -33,16 +30,16 @@ class _SMSLoginPageState extends State with ChangeNotifierMixin> changeNotifier() { + Map?>? changeNotifier() { final List callbacks = [_verify]; - return >{ + return ?>{ _phoneController: callbacks, _vCodeController: callbacks, _nodeText1: null, _nodeText2: null, }; } - + void _verify() { final String name = _phoneController.text; final String vCode = _vCodeController.text; @@ -79,7 +76,7 @@ class _SMSLoginPageState extends State with ChangeNotifierMixin _buildBody() { return [ Text( - DeerLocalizations.of(context).verificationCodeLogin, + DeerLocalizations.of(context)!.verificationCodeLogin, style: TextStyles.textBold26, ), Gaps.vGap16, @@ -88,7 +85,7 @@ class _SMSLoginPageState extends State with ChangeNotifierMixin with ChangeNotifierMixin.value(true); }, ), Gaps.vGap8, Container( alignment: Alignment.centerLeft, - child: GestureDetector( - child: RichText( - text: TextSpan( - text: DeerLocalizations.of(context).registeredTips, - style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp14), - children: [ - TextSpan(text: DeerLocalizations.of(context).register, style: TextStyle(color: Theme.of(context).errorColor)), - TextSpan(text: Utils.getCurrLocale() == 'zh' ? '。' : '.'), - ], - ), + child: RichText( + text: TextSpan( + text: DeerLocalizations.of(context)!.registeredTips, + style: Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp14), + children: [ + TextSpan( + text: DeerLocalizations.of(context)!.register, + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + NavigatorUtils.push(context, LoginRouter.registerPage); + }, + ), + TextSpan(text: Utils.getCurrLocale() == 'zh' ? '。' : '.',), + ], ), - onTap: () => NavigatorUtils.push(context, LoginRouter.registerPage), - ) + ), ), Gaps.vGap24, MyButton( onPressed: _clickable ? _login : null, - text: DeerLocalizations.of(context).login, + text: DeerLocalizations.of(context)!.login, ), Container( height: 40.0, alignment: Alignment.centerRight, child: GestureDetector( child: Text( - DeerLocalizations.of(context).forgotPasswordLink, - style: Theme.of(context).textTheme.subtitle2, + DeerLocalizations.of(context)!.forgotPasswordLink, + style: Theme.of(context).textTheme.titleSmall, ), onTap: () => NavigatorUtils.push(context, LoginRouter.resetPasswordPage), ), diff --git a/lib/login/page/update_password_page.dart b/lib/login/page/update_password_page.dart index 4923139ad..188d6bf0c 100644 --- a/lib/login/page/update_password_page.dart +++ b/lib/login/page/update_password_page.dart @@ -1,20 +1,19 @@ - import 'package:flutter/material.dart'; -import 'package:flutter_deer/util/change_notifier_manage.dart'; +import 'package:flutter_deer/login/widgets/my_text_field.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; -import 'package:flutter_deer/util/toast_utils.dart'; +import 'package:flutter_deer/util/change_notifier_manage.dart'; import 'package:flutter_deer/util/other_utils.dart'; +import 'package:flutter_deer/util/toast_utils.dart'; import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; import 'package:flutter_deer/widgets/my_scroll_view.dart'; -import 'package:flutter_deer/login/widgets/my_text_field.dart'; /// design/1注册登录/index.html#artboard13 class UpdatePasswordPage extends StatefulWidget { - const UpdatePasswordPage({Key key}) : super(key: key); + const UpdatePasswordPage({super.key}); @override _UpdatePasswordPageState createState() => _UpdatePasswordPageState(); @@ -29,9 +28,9 @@ class _UpdatePasswordPageState extends State with ChangeNoti bool _clickable = false; @override - Map> changeNotifier() { + Map?>? changeNotifier() { final List callbacks = [_verify]; - return >{ + return ?>{ _oldPwdController: callbacks, _newPwdController: callbacks, _nodeText1: null, @@ -79,14 +78,13 @@ class _UpdatePasswordPageState extends State with ChangeNoti Gaps.vGap8, Text( '设置账号 15000000000', - style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp12), + style: Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp12), ), Gaps.vGap32, MyTextField( isInputPwd: true, focusNode: _nodeText1, controller: _oldPwdController, - maxLength: 16, keyboardType: TextInputType.visiblePassword, hintText: '请确认旧密码', ), @@ -95,7 +93,6 @@ class _UpdatePasswordPageState extends State with ChangeNoti isInputPwd: true, focusNode: _nodeText2, controller: _newPwdController, - maxLength: 16, keyboardType: TextInputType.visiblePassword, hintText: '请输入新密码', ), diff --git a/lib/login/widgets/my_text_field.dart b/lib/login/widgets/my_text_field.dart index 28d8e75ec..657006a7d 100644 --- a/lib/login/widgets/my_text_field.dart +++ b/lib/login/widgets/my_text_field.dart @@ -1,21 +1,21 @@ - import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_gen/gen_l10n/deer_localizations.dart'; import 'package:flutter_deer/res/resources.dart'; +import 'package:flutter_deer/util/device_utils.dart'; import 'package:flutter_deer/widgets/load_image.dart'; import 'package:flutter_deer/widgets/my_button.dart'; +import '../../l10n/deer_localizations.dart'; + /// 登录模块的输入框封装 class MyTextField extends StatefulWidget { - + const MyTextField({ - Key key, - @required this.controller, + super.key, + required this.controller, this.maxLength = 16, this.autoFocus = false, this.keyboardType = TextInputType.text, @@ -24,19 +24,19 @@ class MyTextField extends StatefulWidget { this.isInputPwd = false, this.getVCode, this.keyName - }): super(key: key); + }); final TextEditingController controller; final int maxLength; final bool autoFocus; final TextInputType keyboardType; final String hintText; - final FocusNode focusNode; + final FocusNode? focusNode; final bool isInputPwd; - final Future Function() getVCode; + final Future Function()? getVCode; /// 用于集成测试寻找widget - final String keyName; - + final String? keyName; + @override _MyTextFieldState createState() => _MyTextFieldState(); } @@ -48,8 +48,8 @@ class _MyTextFieldState extends State { /// 倒计时秒数 final int _second = 30; /// 当前秒数 - int _currentSecond; - StreamSubscription _subscription; + late int _currentSecond; + StreamSubscription? _subscription; @override void initState() { @@ -59,7 +59,7 @@ class _MyTextFieldState extends State { widget.controller.addListener(isEmpty); super.initState(); } - + void isEmpty() { final bool isNotEmpty = widget.controller.text.isNotEmpty; /// 状态不一样在刷新,避免重复不必要的setState @@ -69,17 +69,17 @@ class _MyTextFieldState extends State { }); } } - + @override void dispose() { _subscription?.cancel(); - widget.controller?.removeListener(isEmpty); + widget.controller.removeListener(isEmpty); super.dispose(); } - Future _getVCode() async { - final bool isSuccess = await widget.getVCode(); - if (isSuccess != null && isSuccess) { + Future _getVCode() async { + final bool isSuccess = await widget.getVCode!(); + if (isSuccess) { setState(() { _currentSecond = _second; _clickable = false; @@ -92,7 +92,7 @@ class _MyTextFieldState extends State { }); } } - + @override Widget build(BuildContext context) { final ThemeData themeData = Theme.of(context); @@ -107,7 +107,7 @@ class _MyTextFieldState extends State { textInputAction: TextInputAction.done, keyboardType: widget.keyboardType, // 数字、手机号限制格式为0到9, 密码限制不包含汉字 - inputFormatters: (widget.keyboardType == TextInputType.number || widget.keyboardType == TextInputType.phone) ? + inputFormatters: (widget.keyboardType == TextInputType.number || widget.keyboardType == TextInputType.phone) ? [FilteringTextInputFormatter.allow(RegExp('[0-9]'))] : [FilteringTextInputFormatter.deny(RegExp('[\u4e00-\u9fa5]'))], decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(vertical: 16.0), @@ -121,20 +121,23 @@ class _MyTextFieldState extends State { ), enabledBorder: UnderlineInputBorder( borderSide: BorderSide( - color: Theme.of(context).dividerTheme.color, + color: Theme.of(context).dividerTheme.color!, width: 0.8, ), ), ), ); - /// 个别机型(华为、vivo)密码安全键盘不弹出问题,临时修复方法:https://github.com/flutter/flutter/issues/68571 (issues/61446) - textField = Listener( - onPointerDown: (e) => FocusScope.of(context).requestFocus(widget.focusNode), - child: textField, - ); + /// 个别Android机型(华为、vivo)的密码安全键盘不弹出问题(已知小米正常),临时修复方法:https://github.com/flutter/flutter/issues/68571 (issues/61446) + /// 怀疑是安全键盘与三方输入法之间的切换冲突问题。 + if (Device.isAndroid) { + textField = Listener( + onPointerDown: (e) => FocusScope.of(context).requestFocus(widget.focusNode), + child: textField, + ); + } - Widget clearButton; + Widget? clearButton; if (_isShowDelete) { clearButton = Semantics( @@ -151,7 +154,7 @@ class _MyTextFieldState extends State { ); } - Widget pwdVisible; + late Widget pwdVisible; if (widget.isInputPwd) { pwdVisible = Semantics( label: '密码可见开关', @@ -172,13 +175,13 @@ class _MyTextFieldState extends State { ); } - Widget getVCodeButton; + late Widget getVCodeButton; if (widget.getVCode != null) { getVCodeButton = MyButton( key: const Key('getVerificationCode'), onPressed: _clickable ? _getVCode : null, fontSize: Dimens.font_sp12, - text: _clickable ? DeerLocalizations.of(context).getVerificationCode : '($_currentSecond s)', + text: _clickable ? DeerLocalizations.of(context)!.getVerificationCode : '($_currentSecond s)', textColor: themeData.primaryColor, disabledTextColor: isDark ? Colours.dark_text : Colors.white, backgroundColor: Colors.transparent, @@ -201,9 +204,12 @@ class _MyTextFieldState extends State { Row( mainAxisSize: MainAxisSize.min, children: [ - /// _isShowDelete参数动态变化,为了不破坏树结构,false时放一个空Widget。 + /// _isShowDelete参数动态变化,为了不破坏树结构使用Visibility,false时放一个空Widget。 /// 对于其他参数,为初始配置参数,基本可以确定树结构,就不做空Widget处理。 - if (_isShowDelete) clearButton else Gaps.empty, + Visibility( + visible: _isShowDelete, + child: clearButton ?? Gaps.empty, + ), if (widget.isInputPwd) Gaps.hGap15, if (widget.isInputPwd) pwdVisible, if (widget.getVCode != null) Gaps.hGap15, diff --git a/lib/main.dart b/lib/main.dart index 73dab9152..696116049 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,65 +1,104 @@ import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_deer/common/common.dart'; +import 'package:flutter_deer/demo/demo_page.dart'; +import 'package:flutter_deer/home/splash_page.dart'; import 'package:flutter_deer/net/dio_utils.dart'; import 'package:flutter_deer/net/intercept.dart'; -import 'package:flutter_deer/provider/locale_provider.dart'; -import 'package:flutter_deer/provider/theme_provider.dart'; +import 'package:flutter_deer/res/constant.dart'; import 'package:flutter_deer/routers/not_found_page.dart'; import 'package:flutter_deer/routers/routers.dart'; +import 'package:flutter_deer/setting/provider/locale_provider.dart'; +import 'package:flutter_deer/setting/provider/theme_provider.dart'; import 'package:flutter_deer/util/device_utils.dart'; import 'package:flutter_deer/util/handle_error_utils.dart'; import 'package:flutter_deer/util/log_utils.dart'; -import 'package:flutter_deer/util/theme_utils.dart'; -import 'package:flutter_gen/gen_l10n/deer_localizations.dart'; import 'package:oktoast/oktoast.dart'; -import 'package:flutter_deer/home/splash_page.dart'; import 'package:provider/provider.dart'; import 'package:quick_actions/quick_actions.dart'; import 'package:sp_util/sp_util.dart'; -import 'package:flutter_deer/demo/demo_page.dart'; +import 'package:url_strategy/url_strategy.dart'; +import 'package:window_manager/window_manager.dart'; + +import '../../l10n/deer_localizations.dart'; Future main() async { // debugProfileBuildsEnabled = true; // debugPaintLayerBordersEnabled = true; // debugProfilePaintsEnabled = true; // debugRepaintRainbowEnabled = true; - WidgetsFlutterBinding.ensureInitialized(); - /// sp初始化 - await SpUtil.getInstance(); + if (Constant.inProduction) { + /// Release环境时不打印debugPrint内容 + debugPrint = (String? message, {int? wrapWidth}) {}; + } /// 异常处理 - handleError(runApp(MyApp())); - /// 隐藏状态栏。为启动页、引导页设置。完成后修改回显示状态栏。 - SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]); + handleError(() async { + /// 确保初始化完成 + WidgetsFlutterBinding.ensureInitialized(); + + if (Device.isDesktop) { + await WindowManager.instance.ensureInitialized(); + windowManager.waitUntilReadyToShow().then((_) async { + /// 隐藏标题栏及操作按钮 + // await windowManager.setTitleBarStyle( + // TitleBarStyle.hidden, + // windowButtonVisibility: false, + // ); + /// 设置桌面端窗口大小 + await windowManager.setSize(const Size(400, 800)); + await windowManager.setMinimumSize(const Size(400, 800)); + /// 居中显示 + await windowManager.center(); + await windowManager.show(); + await windowManager.setPreventClose(false); + await windowManager.setSkipTaskbar(false); + }); + } + + /// 去除URL中的“#”(hash),仅针对Web。默认为setHashUrlStrategy + /// 注意本地部署和远程部署时`web/index.html`中的base标签,https://github.com/flutter/flutter/issues/69760 + setPathUrlStrategy(); + + /// sp初始化 + await SpUtil.getInstance(); + + /// 1.22 预览功能: 在输入频率与显示刷新率不匹配情况下提供平滑的滚动效果 + // GestureBinding.instance?.resamplingEnabled = true; + runApp(MyApp()); + }); + + /// 隐藏状态栏,导航栏。为启动页、引导页设置全屏显示。完成后还原。 + SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); // TODO(weilu): 启动体验不佳。状态栏、导航栏在冷启动开始的一瞬间为黑色,且无法通过隐藏、修改颜色等方式进行处理。。。 // 相关问题跟踪:https://github.com/flutter/flutter/issues/73351 } class MyApp extends StatelessWidget { - - MyApp({Key key, this.home, this.theme}): super(key: key) { + MyApp({super.key, this.home, this.theme}) { Log.init(); initDio(); Routes.initRoutes(); initQuickActions(); } - final Widget home; - final ThemeData theme; + final Widget? home; + final ThemeData? theme; static GlobalKey navigatorKey = GlobalKey(); - + void initDio() { final List interceptors = []; + /// 统一添加身份验证请求头 interceptors.add(AuthInterceptor()); + /// 刷新Token interceptors.add(TokenInterceptor()); + /// 打印Log(生产模式去除) if (!Constant.inProduction) { interceptors.add(LoggingInterceptor()); } + /// 适配数据(根据自己的数据结构,可自行选择添加) interceptors.add(AdapterInterceptor()); configDio( @@ -68,16 +107,15 @@ class MyApp extends StatelessWidget { ); } - void initQuickActions() { if (Device.isMobile) { - final QuickActions quickActions = QuickActions(); + const QuickActions quickActions = QuickActions(); if (Device.isIOS) { // Android每次是重新启动activity,所以放在了splash_page处理。 // 总体来说使用不方便,这种动态的方式在安卓中局限性高。这里仅做练习使用。 quickActions.initialize((String shortcutType) async { if (shortcutType == 'demo') { - navigatorKey.currentState.push(MaterialPageRoute( + navigatorKey.currentState?.push(MaterialPageRoute( builder: (BuildContext context) => const DemoPage(), )); } @@ -88,14 +126,14 @@ class MyApp extends StatelessWidget { const ShortcutItem( type: 'demo', localizedTitle: 'Demo', + icon: 'flutter_dash_black' ), ]); } } - + @override Widget build(BuildContext context) { - final Widget app = MultiProvider( providers: [ ChangeNotifierProvider(create: (_) => ThemeProvider()), @@ -110,11 +148,11 @@ class MyApp extends StatelessWidget { /// Toast 配置 return OKToast( - child: app, backgroundColor: Colors.black54, textPadding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 10.0), radius: 20.0, - position: ToastPosition.bottom + position: ToastPosition.bottom, + child: app ); } @@ -126,6 +164,7 @@ class MyApp extends StatelessWidget { // checkerboardRasterCacheImages: true, // showSemanticsDebugger: true, // 显示语义视图 // checkerboardOffscreenLayers: true, // 检查离屏渲染 + theme: theme ?? provider.getTheme(), darkTheme: provider.getTheme(isDarkMode: true), themeMode: provider.getThemeMode(), @@ -135,25 +174,21 @@ class MyApp extends StatelessWidget { supportedLocales: DeerLocalizations.supportedLocales, locale: localeProvider.locale, navigatorKey: navigatorKey, - builder: (BuildContext context, Widget child) { - /// 仅针对安卓 - if (Device.isAndroid) { - /// 切换深色模式会触发此方法,这里设置导航栏颜色 - ThemeUtils.setSystemNavigationBar(provider.getThemeMode()); - } + builder: (BuildContext context, Widget? child) { /// 保证文字大小不受手机系统设置影响 https://www.kikt.top/posts/flutter/layout/dynamic-text/ return MediaQuery( - data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), - child: child, + data: MediaQuery.of(context).copyWith(textScaler: TextScaler.noScaling), + child: child!, ); }, + /// 因为使用了fluro,这里设置主要针对Web onUnknownRoute: (_) { return MaterialPageRoute( builder: (BuildContext context) => const NotFoundPage(), ); }, + restorationScopeId: 'app', ); } - } diff --git a/lib/mvp/base_page.dart b/lib/mvp/base_page.dart index 06946c3b0..20b95f76c 100644 --- a/lib/mvp/base_page.dart +++ b/lib/mvp/base_page.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/mvp/base_presenter.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; @@ -9,7 +8,7 @@ import 'mvps.dart'; mixin BasePageMixin on State implements IMvpView { - P presenter; + P? presenter; P createPresenter(); @@ -51,7 +50,7 @@ mixin BasePageMixin on State< ); } catch(e) { /// 异常原因主要是页面没有build完成就调用Progress。 - print(e); + debugPrint(e.toString()); } } } @@ -101,4 +100,4 @@ mixin BasePageMixin on State< super.initState(); } -} \ No newline at end of file +} diff --git a/lib/mvp/base_page_presenter.dart b/lib/mvp/base_page_presenter.dart index cecc21ac8..cb323dbbf 100644 --- a/lib/mvp/base_page_presenter.dart +++ b/lib/mvp/base_page_presenter.dart @@ -2,7 +2,6 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter_deer/mvp/base_presenter.dart'; import 'package:flutter_deer/net/net.dart'; -import 'package:meta/meta.dart'; import 'mvps.dart'; @@ -12,7 +11,7 @@ class BasePagePresenter extends BasePresenter { _cancelToken = CancelToken(); } - CancelToken _cancelToken; + late CancelToken _cancelToken; @override void dispose() { @@ -23,16 +22,16 @@ class BasePagePresenter extends BasePresenter { } /// 返回Future 适用于刷新,加载更多 - Future requestNetwork(Method method, { - @required String url, + Future requestNetwork(Method method, { + required String url, bool isShow = true, bool isClose = true, - NetSuccessCallback onSuccess, - NetErrorCallback onError, + NetSuccessCallback? onSuccess, + NetErrorCallback? onError, dynamic params, - Map queryParameters, - CancelToken cancelToken, - Options options, + Map? queryParameters, + CancelToken? cancelToken, + Options? options, }) { if (isShow) { view.showProgress(); @@ -46,9 +45,7 @@ class BasePagePresenter extends BasePresenter { if (isClose) { view.closeProgress(); } - if (onSuccess != null) { - onSuccess(data); - } + onSuccess?.call(data); }, onError: (code, msg) { _onError(code, msg, onError); @@ -57,15 +54,15 @@ class BasePagePresenter extends BasePresenter { } void asyncRequestNetwork(Method method, { - @required String url, + required String url, bool isShow = true, bool isClose = true, - NetSuccessCallback onSuccess, - NetErrorCallback onError, + NetSuccessCallback? onSuccess, + NetErrorCallback? onError, dynamic params, - Map queryParameters, - CancelToken cancelToken, - Options options, + Map? queryParameters, + CancelToken? cancelToken, + Options? options, }) { if (isShow) { view.showProgress(); @@ -79,9 +76,7 @@ class BasePagePresenter extends BasePresenter { if (isClose) { view.closeProgress(); } - if (onSuccess != null) { - onSuccess(data); - } + onSuccess?.call(data); }, onError: (code, msg) { _onError(code, msg, onError); @@ -102,7 +97,7 @@ class BasePagePresenter extends BasePresenter { url: HttpApi.upload, params: formData, onSuccess: (data) { - imgPath = data; + imgPath = data ?? ''; } ); } catch(e) { @@ -111,14 +106,14 @@ class BasePagePresenter extends BasePresenter { return imgPath; } - void _onError(int code, String msg, NetErrorCallback onError) { + void _onError(int code, String msg, NetErrorCallback? onError) { /// 异常时直接关闭加载圈,不受isClose影响 view.closeProgress(); if (code != ExceptionHandle.cancel_error) { view.showToast(msg); } /// 页面如果dispose,则不回调onError - if (onError != null && view.getContext() != null) { + if (onError != null) { onError(code, msg); } } diff --git a/lib/mvp/base_presenter.dart b/lib/mvp/base_presenter.dart index 80bdb1543..57c09261b 100644 --- a/lib/mvp/base_presenter.dart +++ b/lib/mvp/base_presenter.dart @@ -1,9 +1,8 @@ - import 'mvps.dart'; class BasePresenter extends IPresenter { - V view; + late V view; @override void deactivate() {} diff --git a/lib/mvp/i_lifecycle.dart b/lib/mvp/i_lifecycle.dart index a37749ea0..64797cd4f 100644 --- a/lib/mvp/i_lifecycle.dart +++ b/lib/mvp/i_lifecycle.dart @@ -10,4 +10,4 @@ abstract class ILifecycle { void deactivate(); void dispose(); -} \ No newline at end of file +} diff --git a/lib/mvp/mvps.dart b/lib/mvp/mvps.dart index b7737c8c2..b1f9f7dc3 100644 --- a/lib/mvp/mvps.dart +++ b/lib/mvp/mvps.dart @@ -16,4 +16,4 @@ abstract class IMvpView { void showToast(String string); } -abstract class IPresenter extends ILifecycle {} \ No newline at end of file +abstract class IPresenter extends ILifecycle {} diff --git a/lib/mvp/power_presenter.dart b/lib/mvp/power_presenter.dart index 09813b4e1..7d1a48605 100644 --- a/lib/mvp/power_presenter.dart +++ b/lib/mvp/power_presenter.dart @@ -1,4 +1,3 @@ - import 'package:flutter_deer/mvp/base_page.dart'; import 'package:flutter_deer/mvp/base_page_presenter.dart'; import 'package:flutter_deer/mvp/base_presenter.dart'; @@ -10,37 +9,61 @@ class PowerPresenter extends BasePresenter { _state = state; } - BasePageMixin _state; + late BasePageMixin _state; List _presenters = []; void requestPresenter(List presenters) { _presenters = presenters; - _presenters.forEach((presenter) => presenter.view = _state); + _presenters.forEach(_requestPresenter); + } + + void _requestPresenter(BasePagePresenter presenter) { + presenter.view = _state; } @override void deactivate() { - _presenters.forEach((presenter) => presenter.deactivate()); + _presenters.forEach(_deactivate); + } + + void _deactivate(BasePagePresenter presenter) { + presenter.deactivate(); } @override void didChangeDependencies() { - _presenters.forEach((presenter) => presenter.didChangeDependencies()); + _presenters.forEach(_didChangeDependencies); + } + + void _didChangeDependencies(BasePagePresenter presenter) { + presenter.didChangeDependencies(); } @override void didUpdateWidgets(W oldWidget) { - _presenters.forEach((presenter) => presenter.didUpdateWidgets(oldWidget)); + + void didUpdateWidgets(BasePagePresenter presenter) { + presenter.didUpdateWidgets(oldWidget); + } + _presenters.forEach(didUpdateWidgets); } @override void dispose() { - _presenters.forEach((presenter) => presenter.dispose()); + _presenters.forEach(_dispose); + } + + void _dispose(BasePagePresenter presenter) { + presenter.dispose(); } @override void initState() { - _presenters.forEach((presenter) => presenter.initState()); + _presenters.forEach(_initState); } - -} \ No newline at end of file + + void _initState(BasePagePresenter presenter) { + presenter.initState(); + } + +} diff --git a/lib/net/base_entity.dart b/lib/net/base_entity.dart index 961248c36..0073e40c0 100644 --- a/lib/net/base_entity.dart +++ b/lib/net/base_entity.dart @@ -1,24 +1,26 @@ - -import 'package:flutter_deer/common/common.dart'; import 'package:flutter_deer/generated/json/base/json_convert_content.dart'; +import 'package:flutter_deer/res/constant.dart'; class BaseEntity { BaseEntity(this.code, this.message, this.data); BaseEntity.fromJson(Map json) { - code = json[Constant.code] as int; + code = json[Constant.code] as int?; message = json[Constant.message] as String; if (json.containsKey(Constant.data)) { - data = _generateOBJ(json[Constant.data]); + data = _generateOBJ(json[Constant.data] as Object?); } } - int code; - String message; - T data; + int? code; + late String message; + T? data; - T _generateOBJ(Object json) { + T? _generateOBJ(Object? json) { + if (json == null) { + return null; + } if (T.toString() == 'String') { return json.toString() as T; } else if (T.toString() == 'Map') { @@ -28,4 +30,4 @@ class BaseEntity { return JsonConvert.fromJsonAsT(json); } } -} \ No newline at end of file +} diff --git a/lib/net/dio_utils.dart b/lib/net/dio_utils.dart index 62658634a..0520c8fbe 100644 --- a/lib/net/dio_utils.dart +++ b/lib/net/dio_utils.dart @@ -1,27 +1,26 @@ - import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter_deer/common/common.dart'; +import 'package:flutter_deer/res/constant.dart'; import 'package:flutter_deer/util/log_utils.dart'; import 'base_entity.dart'; import 'error_handle.dart'; /// 默认dio配置 -int _connectTimeout = 15000; -int _receiveTimeout = 15000; -int _sendTimeout = 10000; -String _baseUrl; +Duration _connectTimeout = const Duration(seconds: 15); +Duration _receiveTimeout = const Duration(seconds: 15); +Duration _sendTimeout = const Duration(seconds: 10); +String _baseUrl = ''; List _interceptors = []; /// 初始化Dio配置 void configDio({ - int connectTimeout, - int receiveTimeout, - int sendTimeout, - String baseUrl, - List interceptors, + Duration? connectTimeout, + Duration? receiveTimeout, + Duration? sendTimeout, + String? baseUrl, + List? interceptors, }) { _connectTimeout = connectTimeout ?? _connectTimeout; _receiveTimeout = receiveTimeout ?? _receiveTimeout; @@ -30,9 +29,9 @@ void configDio({ _interceptors = interceptors ?? _interceptors; } -typedef NetSuccessCallback = Function(T data); -typedef NetSuccessListCallback = Function(List data); -typedef NetErrorCallback = Function(int code, String msg); +typedef NetSuccessCallback = void Function(T data); +typedef NetSuccessListCallback = void Function(List data); +typedef NetErrorCallback = void Function(int code, String msg); /// @weilu https://github.com/simplezhli class DioUtils { @@ -40,7 +39,7 @@ class DioUtils { factory DioUtils() => _singleton; DioUtils._() { - final BaseOptions _options = BaseOptions( + final BaseOptions options = BaseOptions( connectTimeout: _connectTimeout, receiveTimeout: _receiveTimeout, sendTimeout: _sendTimeout, @@ -53,38 +52,37 @@ class DioUtils { baseUrl: _baseUrl, // contentType: Headers.formUrlEncodedContentType, // 适用于post form表单提交 ); - _dio = Dio(_options); + _dio = Dio(options); /// Fiddler抓包代理配置 https://www.jianshu.com/p/d831b1f7c45b -// (_dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = -// (HttpClient client) { -// client.findProxy = (uri) { -// //proxy all request to localhost:8888 -// return 'PROXY 10.41.0.132:8888'; -// }; -// client.badCertificateCallback = -// (X509Certificate cert, String host, int port) => true; -// }; - + // _dio.httpClientAdapter = IOHttpClientAdapter()..onHttpClientCreate = (HttpClient client) { + // client.findProxy = (uri) { + // //proxy all request to localhost:8888 + // return 'PROXY 10.41.0.132:8888'; + // }; + // return client; + // }; + /// 添加拦截器 - _interceptors.forEach((interceptor) { + void addInterceptor(Interceptor interceptor) { _dio.interceptors.add(interceptor); - }); + } + _interceptors.forEach(addInterceptor); } static final DioUtils _singleton = DioUtils._(); static DioUtils get instance => DioUtils(); - static Dio _dio; + static late Dio _dio; Dio get dio => _dio; // 数据返回格式统一,统一处理异常 Future> _request(String method, String url, { - dynamic data, - Map queryParameters, - CancelToken cancelToken, - Options options, + Object? data, + Map? queryParameters, + CancelToken? cancelToken, + Options? options, }) async { final Response response = await _dio.request( url, @@ -100,27 +98,27 @@ class DioUtils { /// 主要目的减少不必要的性能开销 final bool isCompute = !Constant.isDriverTest && data.length > 10 * 1024; debugPrint('isCompute:$isCompute'); - final Map _map = isCompute ? await compute(parseData, data) : parseData(data); - return BaseEntity.fromJson(_map); + final Map map = isCompute ? await compute(parseData, data) : parseData(data); + return BaseEntity.fromJson(map); } catch(e) { debugPrint(e.toString()); return BaseEntity(ExceptionHandle.parse_error, '数据解析错误!', null); } } - Options _checkOptions(String method, Options options) { + Options _checkOptions(String method, Options? options) { options ??= Options(); options.method = method; return options; } - Future requestNetwork(Method method, String url, { - NetSuccessCallback onSuccess, - NetErrorCallback onError, - dynamic params, - Map queryParameters, - CancelToken cancelToken, - Options options, + Future requestNetwork(Method method, String url, { + NetSuccessCallback? onSuccess, + NetErrorCallback? onError, + Object? params, + Map? queryParameters, + CancelToken? cancelToken, + Options? options, }) { return _request(method.value, url, data: params, @@ -129,9 +127,7 @@ class DioUtils { cancelToken: cancelToken, ).then((BaseEntity result) { if (result.code == 0) { - if (onSuccess != null) { - onSuccess(result.data); - } + onSuccess?.call(result.data); } else { _onError(result.code, result.message, onError); } @@ -144,12 +140,12 @@ class DioUtils { /// 统一处理(onSuccess返回T对象,onSuccessList返回 List) void asyncRequestNetwork(Method method, String url, { - NetSuccessCallback onSuccess, - NetErrorCallback onError, - dynamic params, - Map queryParameters, - CancelToken cancelToken, - Options options, + NetSuccessCallback? onSuccess, + NetErrorCallback? onError, + Object? params, + Map? queryParameters, + CancelToken? cancelToken, + Options? options, }) { Stream.fromFuture(_request(method.value, url, data: params, @@ -173,20 +169,18 @@ class DioUtils { } void _cancelLogPrint(dynamic e, String url) { - if (e is DioError && CancelToken.isCancel(e)) { + if (e is DioException && CancelToken.isCancel(e)) { Log.e('取消请求接口: $url'); } } - void _onError(int code, String msg, NetErrorCallback onError) { + void _onError(int? code, String msg, NetErrorCallback? onError) { if (code == null) { code = ExceptionHandle.unknown_error; msg = '未知异常'; } Log.e('接口请求异常: code: $code, mag: $msg'); - if (onError != null) { - onError(code, msg); - } + onError?.call(code, msg); } } @@ -207,4 +201,4 @@ enum Method { /// https://zhuanlan.zhihu.com/p/98545689 extension MethodExtension on Method { String get value => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD'][index]; -} \ No newline at end of file +} diff --git a/lib/net/error_handle.dart b/lib/net/error_handle.dart index 728dbb817..dcd2cb9aa 100644 --- a/lib/net/error_handle.dart +++ b/lib/net/error_handle.dart @@ -1,10 +1,11 @@ import 'dart:io'; - import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; class ExceptionHandle { static const int success = 200; static const int success_not_content = 204; + static const int not_modified = 304; static const int unauthorized = 401; static const int forbidden = 403; static const int not_found = 404; @@ -32,12 +33,12 @@ class ExceptionHandle { }; static NetError handleException(dynamic error) { - print(error); - if (error is DioError) { + debugPrint(error.toString()); + if (error is DioException) { if (error.type.errorCode == 0) { return _handleException(error.error); } else { - return _errorMap[error.type.errorCode]; + return _errorMap[error.type.errorCode]!; } } else { return _handleException(error); @@ -55,7 +56,7 @@ class ExceptionHandle { if (error is FormatException) { errorCode = parse_error; } - return _errorMap[errorCode]; + return _errorMap[errorCode]!; } } @@ -67,13 +68,15 @@ class NetError{ String msg; } -extension DioErrorTypeExtension on DioErrorType { +extension DioErrorTypeExtension on DioExceptionType { int get errorCode => [ ExceptionHandle.connect_timeout_error, ExceptionHandle.send_timeout_error, ExceptionHandle.receive_timeout_error, 0, + 0, ExceptionHandle.cancel_error, 0, + ExceptionHandle.unknown_error, ][index]; -} \ No newline at end of file +} diff --git a/lib/net/http_api.dart b/lib/net/http_api.dart index 64332facc..9173c936b 100644 --- a/lib/net/http_api.dart +++ b/lib/net/http_api.dart @@ -4,4 +4,4 @@ class HttpApi{ static const String search = 'search/repositories'; static const String subscriptions = 'users/simplezhli/subscriptions'; static const String upload = 'uuc/upload-inco'; -} \ No newline at end of file +} diff --git a/lib/net/intercept.dart b/lib/net/intercept.dart index 409109ae6..3668c9be8 100644 --- a/lib/net/intercept.dart +++ b/lib/net/intercept.dart @@ -1,12 +1,12 @@ - import 'dart:convert'; import 'package:dio/dio.dart'; +import 'package:flutter_deer/res/constant.dart'; import 'package:flutter_deer/util/device_utils.dart'; -import 'package:sp_util/sp_util.dart'; -import 'package:flutter_deer/common/common.dart'; import 'package:flutter_deer/util/log_utils.dart'; +import 'package:flutter_deer/util/other_utils.dart'; +import 'package:sp_util/sp_util.dart'; import 'package:sprintf/sprintf.dart'; import 'dio_utils.dart'; @@ -15,7 +15,7 @@ import 'error_handle.dart'; class AuthInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { - final String accessToken = SpUtil.getString(Constant.accessToken); + final String accessToken = SpUtil.getString(Constant.accessToken).nullSafe; if (accessToken.isNotEmpty) { options.headers['Authorization'] = 'token $accessToken'; } @@ -27,20 +27,20 @@ class AuthInterceptor extends Interceptor { } } -class TokenInterceptor extends Interceptor { +class TokenInterceptor extends QueuedInterceptor { - Dio _tokenDio; + Dio? _tokenDio; - Future getToken() async { + Future getToken() async { final Map params = {}; - params['refresh_token'] = SpUtil.getString(Constant.refreshToken); + params['refresh_token'] = SpUtil.getString(Constant.refreshToken).nullSafe; try { _tokenDio ??= Dio(); - _tokenDio.options = DioUtils.instance.dio.options; - final Response response = await _tokenDio.post('lgn/refreshToken', data: params); + _tokenDio!.options = DioUtils.instance.dio.options; + final Response response = await _tokenDio!.post('lgn/refreshToken', data: params); if (response.statusCode == ExceptionHandle.success) { - return json.decode(response.data.toString())['access_token'] as String; + return (json.decode(response.data.toString()) as Map)['access_token'] as String; } } catch(e) { Log.e('刷新Token失败!'); @@ -49,16 +49,13 @@ class TokenInterceptor extends Interceptor { } @override - Future onResponse(Response response, ResponseInterceptorHandler handler) async { + Future onResponse(Response response, ResponseInterceptorHandler handler) async { //401代表token过期 - if (response != null && response.statusCode == ExceptionHandle.unauthorized) { + if (response.statusCode == ExceptionHandle.unauthorized) { Log.d('-----------自动刷新Token------------'); - final Dio dio = DioUtils.instance.dio; - dio.lock(); - final String accessToken = await getToken(); // 获取新的accessToken + final String? accessToken = await getToken(); // 获取新的accessToken Log.e('-----------NewToken: $accessToken ------------'); - SpUtil.putString(Constant.accessToken, accessToken); - dio.unlock(); + SpUtil.putString(Constant.accessToken, accessToken.nullSafe); if (accessToken != null) { // 重新请求失败接口 @@ -73,7 +70,7 @@ class TokenInterceptor extends Interceptor { try { Log.e('----------- 重新请求接口 ------------'); /// 避免重复执行拦截器,使用tokenDio - final Response response = await _tokenDio.request(request.path, + final Response response = await _tokenDio!.request(request.path, data: request.data, queryParameters: request.queryParameters, cancelToken: request.cancelToken, @@ -81,7 +78,7 @@ class TokenInterceptor extends Interceptor { onReceiveProgress: request.onReceiveProgress, ); return handler.next(response); - } on DioError catch (e) { + } on DioException catch (e) { return handler.reject(e); } } @@ -92,27 +89,27 @@ class TokenInterceptor extends Interceptor { class LoggingInterceptor extends Interceptor{ - DateTime _startTime; - DateTime _endTime; + late DateTime _startTime; + late DateTime _endTime; @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) { _startTime = DateTime.now(); Log.d('----------Start----------'); if (options.queryParameters.isEmpty) { - Log.d('RequestUrl: ' + options.baseUrl + options.path); + Log.d('RequestUrl: ${options.baseUrl}${options.path}'); } else { - Log.d('RequestUrl: ' + options.baseUrl + options.path + '?' + Transformer.urlEncodeMap(options.queryParameters)); + Log.d('RequestUrl: ${options.baseUrl}${options.path}?${Transformer.urlEncodeMap(options.queryParameters)}'); } - Log.d('RequestMethod: ' + options.method); - Log.d('RequestHeaders:' + options.headers.toString()); + Log.d('RequestMethod: ${options.method}'); + Log.d('RequestHeaders:${options.headers}'); Log.d('RequestContentType: ${options.contentType}'); - Log.d('RequestData: ${options.data.toString()}'); + Log.d('RequestData: ${options.data}'); super.onRequest(options, handler); } @override - void onResponse(Response response, ResponseInterceptorHandler handler) { + void onResponse(Response response, ResponseInterceptorHandler handler) { _endTime = DateTime.now(); final int duration = _endTime.difference(_startTime).inMilliseconds; if (response.statusCode == ExceptionHandle.success) { @@ -127,7 +124,7 @@ class LoggingInterceptor extends Interceptor{ } @override - void onError(DioError err, ErrorInterceptorHandler handler) { + void onError(DioException err, ErrorInterceptorHandler handler) { Log.d('----------Error-----------'); super.onError(err, handler); } @@ -136,30 +133,30 @@ class LoggingInterceptor extends Interceptor{ class AdapterInterceptor extends Interceptor{ static const String _kMsg = 'msg'; - static const String _kSlash = '\''; + static const String _kSlash = "'"; static const String _kMessage = 'message'; - static const String _kDefaultText = '"无返回信息"'; + static const String _kDefaultText = '无返回信息'; static const String _kNotFound = '未找到查询信息'; static const String _kFailureFormat = '{"code":%d,"message":"%s"}'; static const String _kSuccessFormat = '{"code":0,"data":%s,"message":""}'; @override - void onResponse(Response response, ResponseInterceptorHandler handler) { - final Response r = adapterData(response); + void onResponse(Response response, ResponseInterceptorHandler handler) { + final Response r = adapterData(response); super.onResponse(r, handler); } @override - void onError(DioError err, ErrorInterceptorHandler handler) { + void onError(DioException err, ErrorInterceptorHandler handler) { if (err.response != null) { - adapterData(err.response); + adapterData(err.response!); } super.onError(err, handler); } - Response adapterData(Response response) { + Response adapterData(Response response) { String result; String content = response.data?.toString() ?? ''; /// 成功时,直接格式化返回 @@ -212,4 +209,3 @@ class AdapterInterceptor extends Interceptor{ return response; } } - diff --git a/lib/order/iview/order_search_iview.dart b/lib/order/iview/order_search_iview.dart index 12ccddeda..9f134c828 100644 --- a/lib/order/iview/order_search_iview.dart +++ b/lib/order/iview/order_search_iview.dart @@ -1,10 +1,8 @@ - import 'package:flutter_deer/mvp/mvps.dart'; import 'package:flutter_deer/order/models/search_entity.dart'; -import 'package:flutter_deer/provider/base_list_provider.dart'; +import 'package:flutter_deer/order/provider/base_list_provider.dart'; abstract class OrderSearchIMvpView implements IMvpView { - BaseListProvider get provider; + BaseListProvider get provider; } - diff --git a/lib/order/models/search_entity.dart b/lib/order/models/search_entity.dart index 9ac725b99..85f2c0ba6 100644 --- a/lib/order/models/search_entity.dart +++ b/lib/order/models/search_entity.dart @@ -1,141 +1,220 @@ -import 'package:flutter_deer/generated/json/base/json_convert_content.dart'; -import 'package:flutter_deer/generated/json/base/json_filed.dart'; +import 'package:flutter_deer/generated/json/base/json_field.dart'; +import 'package:flutter_deer/generated/json/search_entity.g.dart'; + +@JsonSerializable() +class SearchEntity { + + SearchEntity(); + + factory SearchEntity.fromJson(Map json) => $SearchEntityFromJson(json); + + Map toJson() => $SearchEntityToJson(this); -class SearchEntity with JsonConvert { @JSONField(name: 'total_count') - int totalCount; + int? totalCount; @JSONField(name: 'incomplete_results') - bool incompleteResults; - List items; + bool? incompleteResults; + List? items; } -class SearchItem with JsonConvert { - int id; +@JsonSerializable() +class SearchItems { + + SearchItems(); + + factory SearchItems.fromJson(Map json) => $SearchItemsFromJson(json); + + Map toJson() => $SearchItemsToJson(this); + + int? id; @JSONField(name: 'node_id') - String nodeId; - String name; + String? nodeId; + String? name; @JSONField(name: 'full_name') - String fullName; - bool private; + String? fullName; + bool? private; + SearchItemsOwner? owner; @JSONField(name: 'html_url') - String htmlUrl; - String description; - bool fork; - String url; + String? htmlUrl; + String? description; + bool? fork; + String? url; @JSONField(name: 'forks_url') - String forksUrl; + String? forksUrl; @JSONField(name: 'keys_url') - String keysUrl; + String? keysUrl; @JSONField(name: 'collaborators_url') - String collaboratorsUrl; + String? collaboratorsUrl; @JSONField(name: 'teams_url') - String teamsUrl; + String? teamsUrl; @JSONField(name: 'hooks_url') - String hooksUrl; + String? hooksUrl; @JSONField(name: 'issue_events_url') - String issueEventsUrl; + String? issueEventsUrl; @JSONField(name: 'events_url') - String eventsUrl; + String? eventsUrl; @JSONField(name: 'assignees_url') - String assigneesUrl; + String? assigneesUrl; @JSONField(name: 'branches_url') - String branchesUrl; + String? branchesUrl; @JSONField(name: 'tags_url') - String tagsUrl; + String? tagsUrl; @JSONField(name: 'blobs_url') - String blobsUrl; + String? blobsUrl; @JSONField(name: 'git_tags_url') - String gitTagsUrl; + String? gitTagsUrl; @JSONField(name: 'git_refs_url') - String gitRefsUrl; + String? gitRefsUrl; @JSONField(name: 'trees_url') - String treesUrl; + String? treesUrl; @JSONField(name: 'statuses_url') - String statusesUrl; + String? statusesUrl; @JSONField(name: 'languages_url') - String languagesUrl; + String? languagesUrl; @JSONField(name: 'stargazers_url') - String stargazersUrl; + String? stargazersUrl; @JSONField(name: 'contributors_url') - String contributorsUrl; + String? contributorsUrl; @JSONField(name: 'subscribers_url') - String subscribersUrl; + String? subscribersUrl; @JSONField(name: 'subscription_url') - String subscriptionUrl; + String? subscriptionUrl; @JSONField(name: 'commits_url') - String commitsUrl; + String? commitsUrl; @JSONField(name: 'git_commits_url') - String gitCommitsUrl; + String? gitCommitsUrl; @JSONField(name: 'comments_url') - String commentsUrl; + String? commentsUrl; @JSONField(name: 'issue_comment_url') - String issueCommentUrl; + String? issueCommentUrl; @JSONField(name: 'contents_url') - String contentsUrl; + String? contentsUrl; @JSONField(name: 'compare_url') - String compareUrl; + String? compareUrl; @JSONField(name: 'merges_url') - String mergesUrl; + String? mergesUrl; @JSONField(name: 'archive_url') - String archiveUrl; + String? archiveUrl; @JSONField(name: 'downloads_url') - String downloadsUrl; + String? downloadsUrl; @JSONField(name: 'issues_url') - String issuesUrl; + String? issuesUrl; @JSONField(name: 'pulls_url') - String pullsUrl; + String? pullsUrl; @JSONField(name: 'milestones_url') - String milestonesUrl; + String? milestonesUrl; @JSONField(name: 'notifications_url') - String notificationsUrl; + String? notificationsUrl; @JSONField(name: 'labels_url') - String labelsUrl; + String? labelsUrl; @JSONField(name: 'releases_url') - String releasesUrl; + String? releasesUrl; @JSONField(name: 'deployments_url') - String deploymentsUrl; + String? deploymentsUrl; @JSONField(name: 'created_at') - String createdAt; + String? createdAt; @JSONField(name: 'updated_at') - String updatedAt; + String? updatedAt; @JSONField(name: 'pushed_at') - String pushedAt; + String? pushedAt; @JSONField(name: 'git_url') - String gitUrl; + String? gitUrl; @JSONField(name: 'ssh_url') - String sshUrl; + String? sshUrl; @JSONField(name: 'clone_url') - String cloneUrl; + String? cloneUrl; @JSONField(name: 'svn_url') - String svnUrl; - String homepage; - int size; + String? svnUrl; + String? homepage; + int? size; @JSONField(name: 'stargazers_count') - int stargazersCount; + int? stargazersCount; @JSONField(name: 'watchers_count') - int watchersCount; - String language; + int? watchersCount; + String? language; @JSONField(name: 'has_issues') - bool hasIssues; + bool? hasIssues; @JSONField(name: 'has_projects') - bool hasProjects; + bool? hasProjects; @JSONField(name: 'has_downloads') - bool hasDownloads; + bool? hasDownloads; @JSONField(name: 'has_wiki') - bool hasWiki; + bool? hasWiki; @JSONField(name: 'has_pages') - bool hasPages; + bool? hasPages; @JSONField(name: 'forks_count') - int forksCount; - bool archived; - bool disabled; + int? forksCount; + bool? archived; + bool? disabled; @JSONField(name: 'open_issues_count') - int openIssuesCount; - int forks; + int? openIssuesCount; + SearchItemsLicense? license; + int? forks; @JSONField(name: 'open_issues') - int openIssues; - int watchers; + int? openIssues; + int? watchers; @JSONField(name: 'default_branch') - String defaultBranch; - double score; + String? defaultBranch; + double? score; +} + +@JsonSerializable() +class SearchItemsOwner { + + SearchItemsOwner(); + + factory SearchItemsOwner.fromJson(Map json) => $SearchItemsOwnerFromJson(json); + + Map toJson() => $SearchItemsOwnerToJson(this); + + String? login; + int? id; + @JSONField(name: 'node_id') + String? nodeId; + @JSONField(name: 'avatar_url') + String? avatarUrl; + @JSONField(name: 'gravatar_id') + String? gravatarId; + String? url; + @JSONField(name: 'html_url') + String? htmlUrl; + @JSONField(name: 'followers_url') + String? followersUrl; + @JSONField(name: 'following_url') + String? followingUrl; + @JSONField(name: 'gists_url') + String? gistsUrl; + @JSONField(name: 'starred_url') + String? starredUrl; + @JSONField(name: 'subscriptions_url') + String? subscriptionsUrl; + @JSONField(name: 'organizations_url') + String? organizationsUrl; + @JSONField(name: 'repos_url') + String? reposUrl; + @JSONField(name: 'events_url') + String? eventsUrl; + @JSONField(name: 'received_events_url') + String? receivedEventsUrl; + String? type; + @JSONField(name: 'site_admin') + bool? siteAdmin; +} + +@JsonSerializable() +class SearchItemsLicense { + + SearchItemsLicense(); + + factory SearchItemsLicense.fromJson(Map json) => $SearchItemsLicenseFromJson(json); + + Map toJson() => $SearchItemsLicenseToJson(this); + + String? key; + String? name; + @JSONField(name: 'spdx_id') + String? spdxId; + String? url; + @JSONField(name: 'node_id') + String? nodeId; } diff --git a/lib/order/order_router.dart b/lib/order/order_router.dart index f162c5c75..c6a7f6135 100644 --- a/lib/order/order_router.dart +++ b/lib/order/order_router.dart @@ -1,4 +1,3 @@ - import 'package:fluro/fluro.dart'; import 'package:flutter_deer/routers/i_router.dart'; @@ -7,7 +6,6 @@ import 'page/order_page.dart'; import 'page/order_search_page.dart'; import 'page/order_track_page.dart'; - class OrderRouter implements IRouterProvider{ static String orderPage = '/order'; @@ -23,4 +21,4 @@ class OrderRouter implements IRouterProvider{ router.define(orderTrackPage, handler: Handler(handlerFunc: (_, __) => const OrderTrackPage())); } -} \ No newline at end of file +} diff --git a/lib/order/page/order_info_page.dart b/lib/order/page/order_info_page.dart index db2079ccf..8cedfd60b 100644 --- a/lib/order/page/order_info_page.dart +++ b/lib/order/page/order_info_page.dart @@ -1,12 +1,12 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; -import 'package:flutter_deer/util/theme_utils.dart'; +import 'package:flutter_deer/util/device_utils.dart'; import 'package:flutter_deer/util/other_utils.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; +import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/widgets/load_image.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; import 'package:flutter_deer/widgets/my_scroll_view.dart'; @@ -16,7 +16,7 @@ import '../order_router.dart'; /// design/3订单/index.html#artboard10 class OrderInfoPage extends StatefulWidget { - const OrderInfoPage({Key key}) : super(key: key); + const OrderInfoPage({super.key}); @override _OrderInfoPageState createState() => _OrderInfoPageState(); @@ -26,7 +26,7 @@ class _OrderInfoPageState extends State { @override Widget build(BuildContext context) { - final Color red = Theme.of(context).errorColor; + final Color red = Theme.of(context).colorScheme.error; final bool isDark = context.isDark; final Widget bottomMenu = Container( @@ -42,7 +42,6 @@ class _OrderInfoPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( - flex: 1, child: MyButton( backgroundColor: isDark ? Colours.dark_material_bg : const Color(0xFFE1EAFA), textColor: isDark ? Colours.dark_text : Colours.app_main, @@ -53,7 +52,6 @@ class _OrderInfoPageState extends State { ), Gaps.hGap16, Expanded( - flex: 1, child: MyButton( text: '接单', minHeight: 45, @@ -82,12 +80,12 @@ class _OrderInfoPageState extends State { child: LoadAssetImage('order/icon_avatar', width: 44.0, height: 44.0), ), Gaps.hGap8, - Expanded( + const Expanded( // 合并Text的语义 child: MergeSemantics( child: Column( crossAxisAlignment: CrossAxisAlignment.start, - children: const [ + children: [ Text('郭李'), Gaps.vGap8, Text('15000000000'), @@ -110,9 +108,9 @@ class _OrderInfoPageState extends State { ], ), Gaps.vGap10, - Row( + const Row( crossAxisAlignment: CrossAxisAlignment.start, - children: const [ + children: [ LoadAssetImage('order/icon_address', width: 16.0, height: 20.0), Gaps.hGap4, Expanded(child: Text('西安市雁塔区 鱼化寨街道唐兴路唐兴数码3楼318', maxLines: 2)), @@ -166,8 +164,8 @@ class _OrderInfoPageState extends State { body: MyScrollView( key: const Key('order_info'), padding: const EdgeInsets.symmetric(horizontal: 16.0), - children: children, bottomButton: bottomMenu, + children: children, ) ); } @@ -178,7 +176,7 @@ class _OrderInfoPageState extends State { padding: const EdgeInsets.symmetric(vertical: 4.0), child: Row( children: [ - Text(title, style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp14)), + Text(title, style: Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp14)), Gaps.hGap8, Text(content) ], @@ -192,8 +190,8 @@ class _OrderInfoPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ const Padding( - child: LoadAssetImage('order/icon_goods', width: 56.0, height: 56.0), padding: EdgeInsets.only(top: 5.0), + child: LoadAssetImage('order/icon_goods', width: 56.0, height: 56.0), ), Gaps.hGap8, Expanded( @@ -206,11 +204,11 @@ class _OrderInfoPageState extends State { overflow: TextOverflow.ellipsis, ), Gaps.vGap4, - Text(index.isEven ? '玫瑰香 520ml' : '125ml', style: Theme.of(context).textTheme.subtitle2), + Text(index.isEven ? '玫瑰香 520ml' : '125ml', style: Theme.of(context).textTheme.titleSmall), Gaps.vGap8, Row( children: [ - _buildGoodsTag(Theme.of(context).errorColor, '立减2.50元'), + _buildGoodsTag(Theme.of(context).colorScheme.error, '立减2.50元'), Gaps.hGap4, Offstage( offstage: index % 2 != 0, @@ -252,12 +250,12 @@ class _OrderInfoPageState extends State { alignment: Alignment.center, child: Text( text, - style: const TextStyle(color: Colors.white, fontSize: Dimens.font_sp10, height: 1.1,), + style: TextStyle(color: Colors.white, fontSize: Dimens.font_sp10, height: Device.isAndroid ? 1.1 : null,), ), ); } - Widget _buildGoodsInfoItem(String title, String content, {Color contentTextColor}) { + Widget _buildGoodsInfoItem(String title, String content, {Color? contentTextColor}) { return MergeSemantics( child: Container( padding: const EdgeInsets.symmetric(vertical: 8.0), @@ -266,7 +264,7 @@ class _OrderInfoPageState extends State { children: [ Text(title), Text(content, style: TextStyle( - color: contentTextColor ?? Theme.of(context).textTheme.bodyText2.color, + color: contentTextColor ?? Theme.of(context).textTheme.bodyMedium?.color, fontWeight: FontWeight.bold )) ], @@ -295,9 +293,9 @@ class _OrderInfoPageState extends State { }, style: ButtonStyle( // 按下高亮颜色 - overlayColor: MaterialStateProperty.all(Theme.of(context).errorColor.withOpacity(0.2)), + overlayColor: MaterialStateProperty.all(Theme.of(context).colorScheme.error.withOpacity(0.2)), ), - child: Text('拨打', style: TextStyle(color: Theme.of(context).errorColor),), + child: Text('拨打', style: TextStyle(color: Theme.of(context).colorScheme.error),), ), ], ); diff --git a/lib/order/page/order_list_page.dart b/lib/order/page/order_list_page.dart index 8f49fc008..5f77febb9 100644 --- a/lib/order/page/order_list_page.dart +++ b/lib/order/page/order_list_page.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/order/provider/order_page_provider.dart'; import 'package:flutter_deer/order/widgets/order_item.dart'; @@ -11,9 +10,9 @@ import 'package:provider/provider.dart'; class OrderListPage extends StatefulWidget { const OrderListPage({ - Key key, - @required this.index, - }): super(key: key); + super.key, + required this.index, + }); final int index; @@ -40,7 +39,7 @@ class _OrderListPageState extends State with AutomaticKeepAliveCl } @override - Map> changeNotifier() { + Map?>? changeNotifier() { return {_controller: null}; } @@ -69,7 +68,7 @@ class _OrderListPageState extends State with AutomaticKeepAliveCl ///SliverAppBar的expandedHeight高度,避免重叠 handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), ), - child, + child!, ], ); }, diff --git a/lib/order/page/order_page.dart b/lib/order/page/order_page.dart index 3692342f9..234adde8d 100644 --- a/lib/order/page/order_page.dart +++ b/lib/order/page/order_page.dart @@ -1,5 +1,4 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/order/page/order_list_page.dart'; import 'package:flutter_deer/order/provider/order_page_provider.dart'; @@ -19,7 +18,7 @@ import '../order_router.dart'; /// design/3订单/index.html class OrderPage extends StatefulWidget { - const OrderPage({Key key}) : super(key: key); + const OrderPage({super.key}); @override _OrderPageState createState() => _OrderPageState(); @@ -30,7 +29,7 @@ class _OrderPageState extends State with AutomaticKeepAliveClientMixi @override bool get wantKeepAlive => true; - TabController _tabController; + TabController? _tabController; OrderPageProvider provider = OrderPageProvider(); int _lastReportedPage = 0; @@ -55,10 +54,16 @@ class _OrderPageState extends State with AutomaticKeepAliveClientMixi @override void dispose() { - _tabController.dispose(); + _tabController?.dispose(); super.dispose(); } + /// https://github.com/simplezhli/flutter_deer/issues/194 + @override + // ignore: must_call_super + void didChangeDependencies() { + } + bool isDark = false; @override @@ -77,7 +82,7 @@ class _OrderPageState extends State with AutomaticKeepAliveClientMixi width: double.infinity, child: isDark ? null : const DecoratedBox( decoration: BoxDecoration( - gradient: LinearGradient(colors: [Color(0xFF5793FA), Color(0xFF4647FA)]), + gradient: LinearGradient(colors: [Colours.gradient_blue, Color(0xFF4647FA)]), ), ), ), @@ -91,7 +96,7 @@ class _OrderPageState extends State with AutomaticKeepAliveClientMixi /// PageView的onPageChanged是监听ScrollUpdateNotification,会造成滑动中卡顿。这里修改为监听滚动结束再更新、 if (notification.depth == 0 && notification is ScrollEndNotification) { final PageMetrics metrics = notification.metrics as PageMetrics; - final int currentPage = metrics.page.round(); + final int currentPage = (metrics.page ?? 0).round(); if (currentPage != _lastReportedPage) { _lastReportedPage = currentPage; _onPageChange(currentPage); @@ -118,8 +123,7 @@ class _OrderPageState extends State with AutomaticKeepAliveClientMixi SliverOverlapAbsorber( handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context), sliver: SliverAppBar( - leading: Gaps.empty, - brightness: Brightness.dark, + systemOverlayStyle: isDark ? ThemeUtils.light : ThemeUtils.dark, actions: [ IconButton( onPressed: () { @@ -136,8 +140,7 @@ class _OrderPageState extends State with AutomaticKeepAliveClientMixi backgroundColor: Colors.transparent, elevation: 0.0, centerTitle: true, - expandedHeight: 100.0, - floating: false, // 不随着滑动隐藏标题 + expandedHeight: 100.0, // 不随着滑动隐藏标题 pinned: true, // 固定在顶部 flexibleSpace: MyFlexibleSpaceBar( background: isDark ? Container(height: 113.0, color: Colours.dark_bg_color,) : LoadAssetImage('order/order_bg', @@ -170,7 +173,7 @@ class _OrderPageState extends State with AutomaticKeepAliveClientMixi height: 80.0, padding: const EdgeInsets.only(top: 8.0), child: TabBar( - labelPadding: const EdgeInsets.symmetric(horizontal: 0), + labelPadding: EdgeInsets.zero, controller: _tabController, labelColor: context.isDark ? Colours.dark_text : Colours.text, unselectedLabelColor: context.isDark ? Colours.dark_text_gray : Colours.text, @@ -202,11 +205,11 @@ class _OrderPageState extends State with AutomaticKeepAliveClientMixi ]; } - final PageController _pageController = PageController(initialPage: 0); + final PageController _pageController = PageController(); Future _onPageChange(int index) async { provider.setIndex(index); /// 这里没有指示器,所以缩短过渡动画时间,减少不必要的刷新 - _tabController.animateTo(index, duration: const Duration(milliseconds: 0)); + _tabController?.animateTo(index, duration: Duration.zero); } } @@ -242,7 +245,6 @@ class _TabView extends StatelessWidget { width: 46.0, padding: const EdgeInsets.symmetric(vertical: 8.0), child: Column( - crossAxisAlignment: CrossAxisAlignment.center, children: [ /// 使用context.select替代Consumer LoadAssetImage(context.select((value) => value.index) == index ? @@ -257,7 +259,7 @@ class _TabView extends StatelessWidget { right: 0.0, child: index < 3 ? DecoratedBox( decoration: BoxDecoration( - color: Theme.of(context).errorColor, + color: Theme.of(context).colorScheme.error, borderRadius: BorderRadius.circular(11.0), ), child: const Padding( @@ -294,4 +296,4 @@ class SliverAppBarDelegate extends SliverPersistentHeaderDelegate { bool shouldRebuild(SliverAppBarDelegate oldDelegate) { return true; } -} \ No newline at end of file +} diff --git a/lib/order/page/order_search_page.dart b/lib/order/page/order_search_page.dart index 775aa9e92..567abda1c 100644 --- a/lib/order/page/order_search_page.dart +++ b/lib/order/page/order_search_page.dart @@ -1,50 +1,49 @@ - - import 'package:flutter/material.dart'; import 'package:flutter_deer/mvp/base_page.dart'; import 'package:flutter_deer/mvp/power_presenter.dart'; -import 'package:flutter_deer/order/models/search_entity.dart'; import 'package:flutter_deer/order/iview/order_search_iview.dart'; +import 'package:flutter_deer/order/models/search_entity.dart'; import 'package:flutter_deer/order/presenter/order_search_presenter.dart'; -import 'package:flutter_deer/provider/base_list_provider.dart'; -import 'package:flutter_deer/shop/models/user_entity.dart'; +import 'package:flutter_deer/order/provider/base_list_provider.dart'; import 'package:flutter_deer/shop/iview/shop_iview.dart'; +import 'package:flutter_deer/shop/models/user_entity.dart'; import 'package:flutter_deer/shop/presenter/shop_presenter.dart'; +import 'package:flutter_deer/util/other_utils.dart'; import 'package:flutter_deer/widgets/my_refresh_list.dart'; -import 'package:flutter_deer/widgets/search_bar.dart'; +import 'package:flutter_deer/widgets/my_search_bar.dart'; import 'package:flutter_deer/widgets/state_layout.dart'; import 'package:provider/provider.dart'; /// design/3订单/index.html#artboard8 class OrderSearchPage extends StatefulWidget { - const OrderSearchPage({Key key}) : super(key: key); + const OrderSearchPage({super.key}); @override _OrderSearchPageState createState() => _OrderSearchPageState(); } -class _OrderSearchPageState extends State with BasePageMixin implements OrderSearchIMvpView, ShopIMvpView { +class _OrderSearchPageState extends State with BasePageMixin> implements OrderSearchIMvpView, ShopIMvpView { @override - BaseListProvider provider = BaseListProvider(); + BaseListProvider provider = BaseListProvider(); - String _keyword; + late String _keyword; int _page = 1; @override void initState() { /// 默认为加载中状态,本页面场景默认为空 - provider.setStateTypeNotNotify(StateType.empty); + provider.stateType = StateType.empty; super.initState(); } @override Widget build(BuildContext context) { - return ChangeNotifierProvider>( + return ChangeNotifierProvider>( create: (_) => provider, child: Scaffold( - appBar: SearchBar( + appBar: MySearchBar( hintText: '请输入手机号或姓名查询', onPressed: (text) { if (text.isEmpty) { @@ -57,7 +56,7 @@ class _OrderSearchPageState extends State with BasePageMixin>( + body: Consumer>( builder: (_, provider, __) { return DeerListView( key: const Key('order_search_list'), @@ -71,7 +70,7 @@ class _OrderSearchPageState extends State with BasePageMixin with BasePageMixin(this); + PowerPresenter createPresenter() { + final PowerPresenter powerPresenter = PowerPresenter(this); _orderSearchPresenter = OrderSearchPresenter(); _shopPagePresenter = ShopPagePresenter(); powerPresenter.requestPresenter([_orderSearchPresenter, _shopPagePresenter]); @@ -107,8 +106,8 @@ class _OrderSearchPageState extends State with BasePageMixin false; @override - void setUser(UserEntity user) { - showToast(user.name); + void setUser(UserEntity? user) { + showToast(user?.name ?? ''); } } diff --git a/lib/order/page/order_track_page.dart b/lib/order/page/order_track_page.dart index 37f655544..e662ca57e 100644 --- a/lib/order/page/order_track_page.dart +++ b/lib/order/page/order_track_page.dart @@ -1,5 +1,3 @@ - - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/widgets/my_app_bar.dart'; @@ -9,7 +7,7 @@ import 'package:flutter_deer/widgets/my_scroll_view.dart'; /// design/3订单/index.html#artboard10 class OrderTrackPage extends StatefulWidget { - const OrderTrackPage({Key key}) : super(key: key); + const OrderTrackPage({super.key}); @override _OrderTrackPageState createState() => _OrderTrackPageState(); @@ -41,7 +39,7 @@ class _OrderTrackPageState extends State { Stepper( physics: const BouncingScrollPhysics(), currentStep: 4 - 1, - controlsBuilder: (_, {onStepContinue, onStepCancel}) { + controlsBuilder: (_, __) { return Gaps.empty; //操作按钮置空 }, steps: List.generate(4, (i) => _buildStep(i)), @@ -62,15 +60,14 @@ class _OrderTrackPageState extends State { child: Text(_titleList[index], style: index == 0 ? TextStyle( fontSize: Dimens.font_sp14, color: primaryColor, - ) : Theme.of(context).textTheme.bodyText2), + ) : Theme.of(context).textTheme.bodyMedium), ), subtitle: Text(_timeList[index], style: index == 0 ? TextStyle( fontSize: Dimens.font_sp12, color: primaryColor, - ) : Theme.of(context).textTheme.subtitle2), + ) : Theme.of(context).textTheme.titleSmall), content: const Text(''), isActive: index == 0, - // TODO(weilu): 这里的状态图标无法修改,暂时使用原生的。应该可以复制Step代码修改一下。 state: index == 0 ? StepState.complete : StepState.indexed, ); } diff --git a/lib/order/presenter/order_search_presenter.dart b/lib/order/presenter/order_search_presenter.dart index 436438a90..b0826665a 100644 --- a/lib/order/presenter/order_search_presenter.dart +++ b/lib/order/presenter/order_search_presenter.dart @@ -1,14 +1,13 @@ - import 'package:flutter_deer/mvp/base_page_presenter.dart'; import 'package:flutter_deer/net/net.dart'; -import 'package:flutter_deer/order/models/search_entity.dart'; import 'package:flutter_deer/order/iview/order_search_iview.dart'; +import 'package:flutter_deer/order/models/search_entity.dart'; import 'package:flutter_deer/widgets/state_layout.dart'; class OrderSearchPresenter extends BasePagePresenter { - Future search(String text, int page, bool isShowDialog) { + Future search(String text, int page, bool isShowDialog) { final Map params = {}; params['q'] = text; @@ -19,33 +18,33 @@ class OrderSearchPresenter extends BasePagePresenter { queryParameters: params, isShow: isShowDialog, onSuccess: (data) { - if (data != null) { + if (data != null && data.items != null) { /// 一页30条数据,等于30条认为有下一页 /// 具体的处理逻辑根据具体的接口情况处理,这部分可以抽离出来 - view.provider.setHasMore(data.items.length == 30); + view.provider.hasMore = data.items!.length == 30; if (page == 1) { /// 刷新 view.provider.list.clear(); - if (data.items.isEmpty) { + if (data.items!.isEmpty) { view.provider.setStateType(StateType.order); } else { - view.provider.addAll(data.items); + view.provider.addAll(data.items!); } } else { - view.provider.addAll(data.items); + view.provider.addAll(data.items!); } } else { /// 加载失败 - view.provider.setHasMore(false); + view.provider.hasMore = false; view.provider.setStateType(StateType.network); } }, onError: (_, __) { /// 加载失败 - view.provider.setHasMore(false); + view.provider.hasMore = false; view.provider.setStateType(StateType.network); } ); } -} \ No newline at end of file +} diff --git a/lib/provider/base_list_provider.dart b/lib/order/provider/base_list_provider.dart similarity index 73% rename from lib/provider/base_list_provider.dart rename to lib/order/provider/base_list_provider.dart index 8e7c4d9a6..ead786683 100644 --- a/lib/provider/base_list_provider.dart +++ b/lib/order/provider/base_list_provider.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/widgets/state_layout.dart'; @@ -7,25 +6,15 @@ class BaseListProvider extends ChangeNotifier { final List _list = []; List get list => _list; - StateType _stateType = StateType.loading; - bool _hasMore = true; - - StateType get stateType => _stateType; - bool get hasMore => _hasMore; + bool hasMore = true; - void setStateTypeNotNotify(StateType stateType) { - _stateType = stateType; - } + StateType stateType = StateType.loading; void setStateType(StateType stateType) { - _stateType = stateType; + this.stateType = stateType; notifyListeners(); } - void setHasMore(bool hasMore) { - _hasMore = hasMore; - } - void add(T data) { _list.add(data); notifyListeners(); @@ -64,4 +53,4 @@ class BaseListProvider extends ChangeNotifier { void refresh() { notifyListeners(); } -} \ No newline at end of file +} diff --git a/lib/order/provider/order_page_provider.dart b/lib/order/provider/order_page_provider.dart index 7a0e69802..d5ec24e6e 100644 --- a/lib/order/provider/order_page_provider.dart +++ b/lib/order/provider/order_page_provider.dart @@ -15,4 +15,4 @@ class OrderPageProvider extends ChangeNotifier { notifyListeners(); } -} \ No newline at end of file +} diff --git a/lib/order/widgets/order_item.dart b/lib/order/widgets/order_item.dart index 08129494b..dc03995e3 100644 --- a/lib/order/widgets/order_item.dart +++ b/lib/order/widgets/order_item.dart @@ -1,12 +1,11 @@ - import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_deer/order/widgets/pay_type_dialog.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; +import 'package:flutter_deer/util/other_utils.dart'; import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/util/toast_utils.dart'; -import 'package:flutter_deer/util/other_utils.dart'; import 'package:flutter_deer/widgets/my_card.dart'; import '../order_router.dart'; @@ -17,10 +16,10 @@ const List orderRightButtonText = ['接单', '开始配送', '完成', ' class OrderItem extends StatelessWidget { const OrderItem({ - Key key, - @required this.tabIndex, - @required this.index, - }) : super(key: key); + super.key, + required this.tabIndex, + required this.index, + }); final int tabIndex; final int index; @@ -43,7 +42,7 @@ class OrderItem extends StatelessWidget { } Widget _buildContent(BuildContext context) { - final TextStyle textTextStyle = Theme.of(context).textTheme.bodyText2.copyWith(fontSize: Dimens.font_sp12); + final TextStyle? textTextStyle = Theme.of(context).textTheme.bodyMedium?.copyWith(fontSize: Dimens.font_sp12); final bool isDark = context.isDark; return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -57,7 +56,7 @@ class OrderItem extends StatelessWidget { '货到付款', style: TextStyle( fontSize: Dimens.font_sp12, - color: Theme.of(context).errorColor, + color: Theme.of(context).colorScheme.error, ), ), ], @@ -65,7 +64,7 @@ class OrderItem extends StatelessWidget { Gaps.vGap8, Text( '西安市雁塔区 鱼化寨街道唐兴路唐兴数码3楼318', - style: Theme.of(context).textTheme.subtitle2, + style: Theme.of(context).textTheme.titleSmall, ), Gaps.vGap8, Gaps.line, @@ -75,7 +74,7 @@ class OrderItem extends StatelessWidget { style: textTextStyle, children: [ const TextSpan(text: '清凉一度抽纸'), - TextSpan(text: ' x1', style: Theme.of(context).textTheme.subtitle2), + TextSpan(text: ' x1', style: Theme.of(context).textTheme.titleSmall), ], ), ), @@ -85,7 +84,7 @@ class OrderItem extends StatelessWidget { style: textTextStyle, children: [ const TextSpan(text: '清凉一度抽纸'), - TextSpan(text: ' x2', style: Theme.of(context).textTheme.subtitle2), + TextSpan(text: ' x2', style: Theme.of(context).textTheme.titleSmall), ], ), ), @@ -98,7 +97,7 @@ class OrderItem extends StatelessWidget { style: textTextStyle, children: [ TextSpan(text: Utils.formatPrice('20.00', format: MoneyFormat.NORMAL)), - TextSpan(text: ' 共3件商品', style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp10)), + TextSpan(text: ' 共3件商品', style: Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp10)), ], ), ), @@ -173,9 +172,9 @@ class OrderItem extends StatelessWidget { }, style: ButtonStyle( // 按下高亮颜色 - overlayColor: MaterialStateProperty.all(Theme.of(context).errorColor.withOpacity(0.2)), + overlayColor: MaterialStateProperty.all(Theme.of(context).colorScheme.error.withOpacity(0.2)), ), - child: Text('拨打', style: TextStyle(color: Theme.of(context).errorColor),), + child: Text('拨打', style: TextStyle(color: Theme.of(context).colorScheme.error),), ), ], ); @@ -203,21 +202,22 @@ class OrderItem extends StatelessWidget { class OrderItemButton extends StatelessWidget { const OrderItemButton({ - Key key, + super.key, this.bgColor, this.textColor, - this.text, + required this.text, this.onTap - }): super(key: key); + }); - final Color bgColor; - final Color textColor; - final GestureTapCallback onTap; + final Color? bgColor; + final Color? textColor; + final GestureTapCallback? onTap; final String text; @override Widget build(BuildContext context) { return GestureDetector( + onTap: onTap, child: Container( alignment: Alignment.center, padding: const EdgeInsets.symmetric(horizontal: 14.0), @@ -232,7 +232,6 @@ class OrderItemButton extends StatelessWidget { ), child: Text(text, style: TextStyle(fontSize: Dimens.font_sp14, color: textColor),), ), - onTap: onTap, ); } } diff --git a/lib/order/widgets/order_tag_item.dart b/lib/order/widgets/order_tag_item.dart index 4887a6ea7..a19ad6a7e 100644 --- a/lib/order/widgets/order_tag_item.dart +++ b/lib/order/widgets/order_tag_item.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/util/theme_utils.dart'; @@ -8,10 +7,10 @@ import 'package:flutter_deer/widgets/my_card.dart'; class OrderTagItem extends StatelessWidget { const OrderTagItem({ - Key key, - @required this.date, - @required this.orderTotal, - }) : super(key: key); + super.key, + required this.date, + required this.orderTotal, + }); final String date; final int orderTotal; @@ -40,4 +39,4 @@ class OrderTagItem extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/order/widgets/pay_type_dialog.dart b/lib/order/widgets/pay_type_dialog.dart index 9a718c2ef..ea6290f55 100644 --- a/lib/order/widgets/pay_type_dialog.dart +++ b/lib/order/widgets/pay_type_dialog.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; @@ -9,11 +8,11 @@ import 'package:flutter_deer/widgets/load_image.dart'; class PayTypeDialog extends StatefulWidget { const PayTypeDialog({ - Key key, + super.key, this.onPressed, - }) : super(key : key); + }); - final Function(int, String) onPressed; + final void Function(int, String)? onPressed; @override _PayTypeDialog createState() => _PayTypeDialog(); @@ -72,8 +71,8 @@ class _PayTypeDialog extends State { ), onPressed: () { NavigatorUtils.goBack(context); - widget.onPressed(_value, _list[_value]); + widget.onPressed?.call(_value, _list[_value]); }, ); } -} \ No newline at end of file +} diff --git a/lib/res/colors.dart b/lib/res/colors.dart index 2b6bc3364..5c026bf0d 100644 --- a/lib/res/colors.dart +++ b/lib/res/colors.dart @@ -39,5 +39,8 @@ class Colours { static const Color bg_gray_ = Color(0xFFFAFAFA); static const Color dark_bg_gray_ = Color(0xFF242526); -} + static const Color gradient_blue = Color(0xFF5793FA); + static const Color shadow_blue = Color(0x805793FA); + static const Color orange = Color(0xFFFF8547); +} diff --git a/lib/common/common.dart b/lib/res/constant.dart similarity index 100% rename from lib/common/common.dart rename to lib/res/constant.dart diff --git a/lib/res/gaps.dart b/lib/res/gaps.dart index c80b46898..9e7c0e39e 100644 --- a/lib/res/gaps.dart +++ b/lib/res/gaps.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_deer/res/resources.dart'; /// 间隔 @@ -43,4 +42,7 @@ class Gaps { ); static const Widget empty = SizedBox.shrink(); + + /// 补充一种空Widget实现 https://github.com/letsar/nil + /// https://github.com/flutter/flutter/issues/78159 } diff --git a/lib/res/resources.dart b/lib/res/resources.dart index 19c7dcd54..b41e6bf23 100644 --- a/lib/res/resources.dart +++ b/lib/res/resources.dart @@ -9,4 +9,4 @@ export 'styles.dart'; class Images { static const Widget arrowRight = LoadAssetImage('ic_arrow_right', height: 16.0, width: 16.0); -} \ No newline at end of file +} diff --git a/lib/res/styles.dart b/lib/res/styles.dart index 963520000..5a3a6dd2a 100644 --- a/lib/res/styles.dart +++ b/lib/res/styles.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'colors.dart'; import 'dimens.dart'; diff --git a/lib/routers/fluro_navigator.dart b/lib/routers/fluro_navigator.dart index 34b3f6b23..0d7be3e54 100644 --- a/lib/routers/fluro_navigator.dart +++ b/lib/routers/fluro_navigator.dart @@ -7,22 +7,36 @@ import 'routers.dart'; class NavigatorUtils { static void push(BuildContext context, String path, - {bool replace = false, bool clearStack = false}) { + {bool replace = false, bool clearStack = false, Object? arguments}) { unfocus(); - Routes.router.navigateTo(context, path, replace: replace, clearStack: clearStack, transition: TransitionType.native); + Routes.router.navigateTo(context, path, + replace: replace, + clearStack: clearStack, + transition: TransitionType.native, + routeSettings: RouteSettings( + arguments: arguments, + ), + ); } - static void pushResult(BuildContext context, String path, Function(Object) function, - {bool replace = false, bool clearStack = false}) { + static void pushResult(BuildContext context, String path, void Function(Object) function, + {bool replace = false, bool clearStack = false, Object? arguments}) { unfocus(); - Routes.router.navigateTo(context, path, replace: replace, clearStack: clearStack, transition: TransitionType.native).then((Object result) { + Routes.router.navigateTo(context, path, + replace: replace, + clearStack: clearStack, + transition: TransitionType.native, + routeSettings: RouteSettings( + arguments: arguments, + ), + ).then((Object? result) { // 页面返回result为null if (result == null) { return; } function(result); }).catchError((dynamic error) { - print('$error'); + debugPrint('$error'); }); } diff --git a/lib/routers/i_router.dart b/lib/routers/i_router.dart index 71eb09c7c..d6b12fef7 100644 --- a/lib/routers/i_router.dart +++ b/lib/routers/i_router.dart @@ -4,4 +4,4 @@ import 'package:fluro/fluro.dart'; abstract class IRouterProvider { void initRouter(FluroRouter router); -} \ No newline at end of file +} diff --git a/lib/routers/not_found_page.dart b/lib/routers/not_found_page.dart index ef58c9ee9..7485f14e7 100644 --- a/lib/routers/not_found_page.dart +++ b/lib/routers/not_found_page.dart @@ -4,7 +4,7 @@ import 'package:flutter_deer/widgets/state_layout.dart'; class NotFoundPage extends StatelessWidget { - const NotFoundPage({Key key}) : super(key: key); + const NotFoundPage({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/routers/routers.dart b/lib/routers/routers.dart index 56ea3a82a..5442651e6 100644 --- a/lib/routers/routers.dart +++ b/lib/routers/routers.dart @@ -1,21 +1,18 @@ - import 'package:fluro/fluro.dart'; import 'package:flutter/material.dart'; import 'package:flutter_deer/account/account_router.dart'; import 'package:flutter_deer/goods/goods_router.dart'; -import 'package:flutter_deer/routers/not_found_page.dart'; +import 'package:flutter_deer/home/home_page.dart'; +import 'package:flutter_deer/home/webview_page.dart'; import 'package:flutter_deer/login/login_router.dart'; import 'package:flutter_deer/order/order_router.dart'; import 'package:flutter_deer/routers/i_router.dart'; +import 'package:flutter_deer/routers/not_found_page.dart'; import 'package:flutter_deer/setting/setting_router.dart'; - -import 'package:flutter_deer/home/home_page.dart'; -import 'package:flutter_deer/home/webview_page.dart'; import 'package:flutter_deer/shop/shop_router.dart'; import 'package:flutter_deer/statistics/statistics_router.dart'; import 'package:flutter_deer/store/store_router.dart'; -// ignore: avoid_classes_with_only_static_members class Routes { static String home = '/home'; @@ -28,20 +25,20 @@ class Routes { static void initRoutes() { /// 指定路由跳转错误返回页 router.notFoundHandler = Handler( - handlerFunc: (BuildContext context, Map> params) { + handlerFunc: (BuildContext? context, Map> params) { debugPrint('未找到目标页'); return const NotFoundPage(); }); router.define(home, handler: Handler( - handlerFunc: (BuildContext context, Map> params) => const Home())); + handlerFunc: (BuildContext? context, Map> params) => const Home())); router.define(webViewPage, handler: Handler(handlerFunc: (_, params) { - final String title = params['title']?.first; - final String url = params['url']?.first; + final String title = params['title']?.first ?? ''; + final String url = params['url']?.first ?? ''; return WebViewPage(title: title, url: url); })); - + _listRouter.clear(); /// 各自路由由各自模块管理,统一在此添加初始化 _listRouter.add(ShopRouter()); @@ -54,8 +51,9 @@ class Routes { _listRouter.add(StatisticsRouter()); /// 初始化路由 - _listRouter.forEach((routerProvider) { + void initRouter(IRouterProvider routerProvider) { routerProvider.initRouter(router); - }); + } + _listRouter.forEach(initRouter); } } diff --git a/lib/routers/web_page_transitions.dart b/lib/routers/web_page_transitions.dart index 119fb58d4..4d2704e24 100644 --- a/lib/routers/web_page_transitions.dart +++ b/lib/routers/web_page_transitions.dart @@ -23,4 +23,4 @@ class NoTransitionsOnWeb extends PageTransitionsTheme { child, ); } -} \ No newline at end of file +} diff --git a/lib/setting/page/about_page.dart b/lib/setting/page/about_page.dart index 2f77e038f..9090490f4 100644 --- a/lib/setting/page/about_page.dart +++ b/lib/setting/page/about_page.dart @@ -1,4 +1,3 @@ - import 'dart:async'; import 'dart:math'; @@ -7,12 +6,12 @@ import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/device_utils.dart'; import 'package:flutter_deer/util/other_utils.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/click_item.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; class AboutPage extends StatefulWidget { - const AboutPage({Key key}) : super(key: key); + const AboutPage({super.key}); @override _AboutPageState createState() => _AboutPageState(); @@ -40,7 +39,7 @@ class _AboutPageState extends State { return Color.fromARGB(255, red, greed, blue); } - Timer _countdownTimer; + Timer? _countdownTimer; @override void initState() { diff --git a/lib/setting/page/account_manager_page.dart b/lib/setting/page/account_manager_page.dart index 3f2bd0aea..3900aa3d4 100644 --- a/lib/setting/page/account_manager_page.dart +++ b/lib/setting/page/account_manager_page.dart @@ -1,16 +1,15 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/login/login_router.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/click_item.dart'; import 'package:flutter_deer/widgets/load_image.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; /// design/8设置/index.html#artboard1 class AccountManagerPage extends StatefulWidget { - const AccountManagerPage({Key key}) : super(key: key); + const AccountManagerPage({super.key}); @override _AccountManagerPageState createState() => _AccountManagerPageState(); diff --git a/lib/setting/page/locale_page.dart b/lib/setting/page/locale_page.dart index 768a4cda4..f58229975 100644 --- a/lib/setting/page/locale_page.dart +++ b/lib/setting/page/locale_page.dart @@ -1,15 +1,14 @@ - import 'package:flutter/material.dart'; -import 'package:flutter_deer/common/common.dart'; -import 'package:flutter_deer/provider/locale_provider.dart'; +import 'package:flutter_deer/res/constant.dart'; +import 'package:flutter_deer/setting/provider/locale_provider.dart'; import 'package:flutter_deer/util/toast_utils.dart'; -import 'package:sp_util/sp_util.dart'; import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:provider/provider.dart'; +import 'package:sp_util/sp_util.dart'; class LocalePage extends StatefulWidget { - const LocalePage({Key key}) : super(key: key); + const LocalePage({super.key}); @override _LocalePageState createState() => _LocalePageState(); @@ -21,7 +20,7 @@ class _LocalePageState extends State { @override Widget build(BuildContext context) { - final String locale = SpUtil.getString(Constant.locale); + final String? locale = SpUtil.getString(Constant.locale); String localeMode; switch(locale) { case 'zh': @@ -47,6 +46,7 @@ class _LocalePageState extends State { final String locale = index == 0 ? '' : (index == 1 ? 'zh' : 'en'); context.read().setLocale(locale); Toast.show('当前功能仅登录模块有效'); + setState(() {}); }, child: Container( alignment: Alignment.centerLeft, diff --git a/lib/setting/page/setting_page.dart b/lib/setting/page/setting_page.dart index adc1a64ff..2ab7f295a 100644 --- a/lib/setting/page/setting_page.dart +++ b/lib/setting/page/setting_page.dart @@ -1,16 +1,18 @@ - -import 'package:flutter_deer/demo/demo_page.dart'; -import 'package:flutter_deer/util/device_utils.dart'; -import 'package:sp_util/sp_util.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_deer/common/common.dart'; +import 'package:flutter_deer/demo/demo_page.dart'; +import 'package:flutter_deer/res/constant.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; +import 'package:flutter_deer/setting/provider/locale_provider.dart'; +import 'package:flutter_deer/setting/provider/theme_provider.dart'; import 'package:flutter_deer/setting/widgets/exit_dialog.dart'; import 'package:flutter_deer/setting/widgets/update_dialog.dart'; import 'package:flutter_deer/util/app_navigator.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; +import 'package:flutter_deer/util/device_utils.dart'; import 'package:flutter_deer/widgets/click_item.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; +import 'package:provider/provider.dart'; +import 'package:sp_util/sp_util.dart'; import '../setting_router.dart'; @@ -18,7 +20,7 @@ import '../setting_router.dart'; /// design/8设置/index.html class SettingPage extends StatefulWidget { - const SettingPage({Key key}) : super(key: key); + const SettingPage({super.key}); @override _SettingPageState createState() => _SettingPageState(); @@ -31,55 +33,59 @@ class _SettingPageState extends State { appBar: const MyAppBar( centerTitle: '设置', ), - body: Column( - children: [ - Gaps.vGap5, - ClickItem( - title: '账号管理', - onTap: () => NavigatorUtils.push(context, SettingRouter.accountManagerPage), - ), - if (Device.isMobile) ClickItem( - title: '清除缓存', - content: '23.5MB', - onTap: () {}, - ), - ClickItem( - title: '夜间模式', - content: _getCurrentTheme(), - onTap: () => NavigatorUtils.push(context, SettingRouter.themePage), - ), - ClickItem( - title: '多语言', - content: _getCurrentLocale(), - onTap: () => NavigatorUtils.push(context, SettingRouter.localePage), - ), - if (Device.isMobile) ClickItem( - title: '检查更新', - onTap: _showUpdateDialog, - ), - ClickItem( - title: '关于我们', - onTap: () => NavigatorUtils.push(context, SettingRouter.aboutPage), - ), - ClickItem( - title: '退出当前账号', - onTap: _showExitDialog, - ), - if (Device.isMobile) ClickItem( - title: 'Deer Web版', - onTap: () => NavigatorUtils.goWebViewPage(context, 'Flutter Deer', 'https://simplezhli.github.io/flutter_deer/'), - ), - ClickItem( - title: '其他Demo', - onTap: () => AppNavigator.push(context, const DemoPage()), - ), - ], + body: Consumer2( + builder: (_, ThemeProvider provider, LocaleProvider localeProvider, __) { + return Column( + children: [ + Gaps.vGap5, + ClickItem( + title: '账号管理', + onTap: () => NavigatorUtils.push(context, SettingRouter.accountManagerPage), + ), + if (Device.isMobile) ClickItem( + title: '清除缓存', + content: '23.5MB', + onTap: () {}, + ), + ClickItem( + title: '夜间模式', + content: _getCurrentTheme(), + onTap: () => NavigatorUtils.push(context, SettingRouter.themePage), + ), + ClickItem( + title: '多语言', + content: _getCurrentLocale(), + onTap: () => NavigatorUtils.push(context, SettingRouter.localePage), + ), + if (Device.isMobile) ClickItem( + title: '检查更新', + onTap: _showUpdateDialog, + ), + ClickItem( + title: '关于我们', + onTap: () => NavigatorUtils.push(context, SettingRouter.aboutPage), + ), + ClickItem( + title: '退出当前账号', + onTap: _showExitDialog, + ), + if (Device.isMobile) ClickItem( + title: 'Deer Web版', + onTap: () => NavigatorUtils.goWebViewPage(context, 'Flutter Deer', 'https://simplezhli.github.io/flutter_deer/'), + ), + ClickItem( + title: '其他Demo', + onTap: () => AppNavigator.push(context, const DemoPage()), + ), + ], + ); + }, ), ); } String _getCurrentTheme() { - final String theme = SpUtil.getString(Constant.theme); + final String? theme = SpUtil.getString(Constant.theme); String themeMode; switch(theme) { case 'Dark': @@ -96,7 +102,7 @@ class _SettingPageState extends State { } String _getCurrentLocale() { - final String locale = SpUtil.getString(Constant.locale); + final String? locale = SpUtil.getString(Constant.locale); String localeMode; switch(locale) { case 'zh': diff --git a/lib/setting/page/theme_page.dart b/lib/setting/page/theme_page.dart index a8544f385..3466e0478 100644 --- a/lib/setting/page/theme_page.dart +++ b/lib/setting/page/theme_page.dart @@ -1,14 +1,13 @@ - import 'package:flutter/material.dart'; -import 'package:flutter_deer/common/common.dart'; -import 'package:sp_util/sp_util.dart'; -import 'package:flutter_deer/provider/theme_provider.dart'; +import 'package:flutter_deer/res/constant.dart'; +import 'package:flutter_deer/setting/provider/theme_provider.dart'; import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:provider/provider.dart'; +import 'package:sp_util/sp_util.dart'; class ThemePage extends StatefulWidget { - const ThemePage({Key key}) : super(key: key); + const ThemePage({super.key}); @override _ThemePageState createState() => _ThemePageState(); @@ -20,7 +19,7 @@ class _ThemePageState extends State { @override Widget build(BuildContext context) { - final String theme = SpUtil.getString(Constant.theme); + final String? theme = SpUtil.getString(Constant.theme); String themeMode; switch(theme) { case 'Dark': diff --git a/lib/provider/locale_provider.dart b/lib/setting/provider/locale_provider.dart similarity index 75% rename from lib/provider/locale_provider.dart rename to lib/setting/provider/locale_provider.dart index c7617430e..248d837ff 100644 --- a/lib/provider/locale_provider.dart +++ b/lib/setting/provider/locale_provider.dart @@ -1,14 +1,12 @@ -import 'dart:ui'; - -import 'package:sp_util/sp_util.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_deer/common/common.dart'; +import 'package:flutter_deer/res/constant.dart'; +import 'package:sp_util/sp_util.dart'; class LocaleProvider extends ChangeNotifier { - Locale get locale { - final String locale = SpUtil.getString(Constant.locale); + Locale? get locale { + final String locale = SpUtil.getString(Constant.locale) ?? ''; switch(locale) { case 'zh': return const Locale('zh', 'CN'); @@ -24,4 +22,4 @@ class LocaleProvider extends ChangeNotifier { notifyListeners(); } -} \ No newline at end of file +} diff --git a/lib/provider/theme_provider.dart b/lib/setting/provider/theme_provider.dart similarity index 69% rename from lib/provider/theme_provider.dart rename to lib/setting/provider/theme_provider.dart index 710a0fe1b..763e0e855 100644 --- a/lib/provider/theme_provider.dart +++ b/lib/setting/provider/theme_provider.dart @@ -1,20 +1,20 @@ -import 'dart:ui'; - -import 'package:flutter_deer/routers/web_page_transitions.dart'; -import 'package:sp_util/sp_util.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_deer/common/common.dart'; +import 'package:flutter_deer/res/constant.dart'; import 'package:flutter_deer/res/resources.dart'; +import 'package:flutter_deer/routers/web_page_transitions.dart'; +import 'package:sp_util/sp_util.dart'; + +import '../../util/theme_utils.dart'; extension ThemeModeExtension on ThemeMode { String get value => ['System', 'Light', 'Dark'][index]; } class ThemeProvider extends ChangeNotifier { - + void syncTheme() { - final String theme = SpUtil.getString(Constant.theme); + final String theme = SpUtil.getString(Constant.theme) ?? ''; if (theme.isNotEmpty && theme != ThemeMode.system.value) { notifyListeners(); } @@ -26,7 +26,7 @@ class ThemeProvider extends ChangeNotifier { } ThemeMode getThemeMode(){ - final String theme = SpUtil.getString(Constant.theme); + final String theme = SpUtil.getString(Constant.theme) ?? ''; switch(theme) { case 'Dark': return ThemeMode.dark; @@ -39,30 +39,34 @@ class ThemeProvider extends ChangeNotifier { ThemeData getTheme({bool isDarkMode = false}) { return ThemeData( - errorColor: isDarkMode ? Colours.dark_red : Colours.red, - brightness: isDarkMode ? Brightness.dark : Brightness.light, + useMaterial3: false, primaryColor: isDarkMode ? Colours.dark_app_main : Colours.app_main, - accentColor: isDarkMode ? Colours.dark_app_main : Colours.app_main, + colorScheme: ColorScheme.fromSwatch().copyWith( + brightness: isDarkMode ? Brightness.dark : Brightness.light, + secondary: isDarkMode ? Colours.dark_app_main : Colours.app_main, + error: isDarkMode ? Colours.dark_red : Colours.red, + ), // Tab指示器颜色 indicatorColor: isDarkMode ? Colours.dark_app_main : Colours.app_main, // 页面背景色 scaffoldBackgroundColor: isDarkMode ? Colours.dark_bg_color : Colors.white, // 主要用于Material背景色 canvasColor: isDarkMode ? Colours.dark_material_bg : Colors.white, - // 文字选择色(输入框复制粘贴菜单) + // 文字选择色(输入框选择文字等) // textSelectionColor: Colours.app_main.withAlpha(70), // textSelectionHandleColor: Colours.app_main, - // 稳定发行版:1.23 变更(https://flutter.dev/docs/release/breaking-changes/text-selection-theme) + // 稳定版:1.23 变更(https://flutter.dev/docs/release/breaking-changes/text-selection-theme) textSelectionTheme: TextSelectionThemeData( selectionColor: Colours.app_main.withAlpha(70), selectionHandleColor: Colours.app_main, + cursorColor: Colours.app_main, ), textTheme: TextTheme( // TextField输入文字颜色 - subtitle1: isDarkMode ? TextStyles.textDark : TextStyles.text, + titleMedium: isDarkMode ? TextStyles.textDark : TextStyles.text, // Text文字样式 - bodyText2: isDarkMode ? TextStyles.textDark : TextStyles.text, - subtitle2: isDarkMode ? TextStyles.textDarkGray12 : TextStyles.textGray12, + bodyMedium: isDarkMode ? TextStyles.textDark : TextStyles.text, + titleSmall: isDarkMode ? TextStyles.textDarkGray12 : TextStyles.textGray12, ), inputDecorationTheme: InputDecorationTheme( hintStyle: isDarkMode ? TextStyles.textHint14 : TextStyles.textDarkGray14, @@ -70,7 +74,7 @@ class ThemeProvider extends ChangeNotifier { appBarTheme: AppBarTheme( elevation: 0.0, color: isDarkMode ? Colours.dark_bg_color : Colors.white, - brightness: isDarkMode ? Brightness.dark : Brightness.light, + systemOverlayStyle: isDarkMode ? ThemeUtils.light : ThemeUtils.dark, ), dividerTheme: DividerThemeData( color: isDarkMode ? Colours.dark_line : Colours.line, @@ -81,7 +85,8 @@ class ThemeProvider extends ChangeNotifier { brightness: isDarkMode ? Brightness.dark : Brightness.light, ), pageTransitionsTheme: NoTransitionsOnWeb(), + visualDensity: VisualDensity.standard, // https://github.com/flutter/flutter/issues/77142 ); } -} \ No newline at end of file +} diff --git a/lib/setting/setting_router.dart b/lib/setting/setting_router.dart index 0f5b56bea..c376551c6 100644 --- a/lib/setting/setting_router.dart +++ b/lib/setting/setting_router.dart @@ -1,4 +1,3 @@ - import 'package:fluro/fluro.dart'; import 'package:flutter_deer/routers/i_router.dart'; import 'package:flutter_deer/setting/page/locale_page.dart'; @@ -25,4 +24,4 @@ class SettingRouter implements IRouterProvider{ router.define(accountManagerPage, handler: Handler(handlerFunc: (_, __) => const AccountManagerPage())); } -} \ No newline at end of file +} diff --git a/lib/setting/widgets/exit_dialog.dart b/lib/setting/widgets/exit_dialog.dart index 04132d602..bb00d0c3a 100644 --- a/lib/setting/widgets/exit_dialog.dart +++ b/lib/setting/widgets/exit_dialog.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/login/login_router.dart'; import 'package:flutter_deer/res/styles.dart'; @@ -8,8 +7,8 @@ import 'package:flutter_deer/widgets/base_dialog.dart'; class ExitDialog extends StatefulWidget { const ExitDialog({ - Key key, - }) : super(key : key); + super.key, + }); @override _ExitDialog createState() => _ExitDialog(); @@ -31,4 +30,4 @@ class _ExitDialog extends State { }, ); } -} \ No newline at end of file +} diff --git a/lib/setting/widgets/update_dialog.dart b/lib/setting/widgets/update_dialog.dart index 29ce04938..f33bc7264 100644 --- a/lib/setting/widgets/update_dialog.dart +++ b/lib/setting/widgets/update_dialog.dart @@ -1,13 +1,13 @@ - import 'dart:io'; import 'package:dio/dio.dart'; -import 'package:flustars/flustars.dart'; +import 'package:flustars_flutter3/flustars_flutter3.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/image_utils.dart'; +import 'package:flutter_deer/util/other_utils.dart'; import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/util/toast_utils.dart'; import 'package:flutter_deer/util/version_utils.dart'; @@ -16,7 +16,7 @@ import 'package:flutter_deer/widgets/my_button.dart'; class UpdateDialog extends StatefulWidget { - const UpdateDialog({Key key}) : super(key: key); + const UpdateDialog({super.key}); @override _UpdateDialogState createState() => _UpdateDialogState(); @@ -39,11 +39,8 @@ class _UpdateDialogState extends State { @override Widget build(BuildContext context) { final Color primaryColor = Theme.of(context).primaryColor; - return WillPopScope( - onWillPop: () async { - /// 使用false禁止返回键返回,达到强制升级目的 - return true; - }, + return PopScope( + canPop: false, /// 使用false禁止返回键返回,达到强制升级目的 child: Scaffold( resizeToAvoidBottomInset: false, backgroundColor: Colors.transparent, @@ -77,11 +74,14 @@ class _UpdateDialogState extends State { Gaps.vGap10, const Text('1.又双叒修复了一大堆bug。\n\n2.祭天了多名程序猿。'), Gaps.vGap15, - if (_isDownload) LinearProgressIndicator( - backgroundColor: Colours.line, - valueColor: AlwaysStoppedAnimation(primaryColor), - value: _value, - ) else _buildButton(context), + if (_isDownload) + LinearProgressIndicator( + backgroundColor: Colours.line, + valueColor: AlwaysStoppedAnimation(primaryColor), + value: _value, + ) + else + _buildButton(context), ], ), ), @@ -151,10 +151,10 @@ class _UpdateDialogState extends State { setInitDir(initStorageDir: true); await DirectoryUtil.getInstance(); DirectoryUtil.createStorageDirSync(category: 'Download'); - final String path = DirectoryUtil.getStoragePath(fileName: 'deer', category: 'Download', format: 'apk'); + final String path = DirectoryUtil.getStoragePath(fileName: 'deer', category: 'Download', format: 'apk').nullSafe; final File file = File(path); /// 链接可能会失效 - await Dio().download('https://54a0bf2343ff38bdc347780545bd8c9e.dd.cdntips.com/imtt.dd.qq.com/16891/apk/E664A57DD3EA0540676EFC830BFDDE97.apk', + await Dio().download('http://imtt.dd.qq.com/16891/apk/FF9625F40FD26F015F4CDED37B6B66AE.apk', file.path, cancelToken: _cancelToken, onReceiveProgress: (int count, int total) { @@ -172,7 +172,7 @@ class _UpdateDialogState extends State { ); } catch (e) { Toast.show('下载失败!'); - print(e); + debugPrint(e.toString()); setState(() { _isDownload = false; }); diff --git a/lib/shop/iview/shop_iview.dart b/lib/shop/iview/shop_iview.dart index f497b22df..591abc6a1 100644 --- a/lib/shop/iview/shop_iview.dart +++ b/lib/shop/iview/shop_iview.dart @@ -1,10 +1,9 @@ - import 'package:flutter_deer/mvp/mvps.dart'; import 'package:flutter_deer/shop/models/user_entity.dart'; abstract class ShopIMvpView implements IMvpView { - void setUser(UserEntity user); + void setUser(UserEntity? user); bool get isAccessibilityTest; } diff --git a/lib/shop/models/user_entity.dart b/lib/shop/models/user_entity.dart index 7c746043a..758c7374d 100644 --- a/lib/shop/models/user_entity.dart +++ b/lib/shop/models/user_entity.dart @@ -1,10 +1,18 @@ -import 'package:flutter_deer/generated/json/base/json_convert_content.dart'; -import 'package:flutter_deer/generated/json/base/json_filed.dart'; +import 'package:flutter_deer/generated/json/base/json_field.dart'; +import 'package:flutter_deer/generated/json/user_entity.g.dart'; + +@JsonSerializable() +class UserEntity { + + UserEntity(); + + factory UserEntity.fromJson(Map json) => $UserEntityFromJson(json); + + Map toJson() => $UserEntityToJson(this); -class UserEntity with JsonConvert { @JSONField(name: 'avatar_url') - String avatarUrl; - String name; - int id; - String blog; + String? avatarUrl; + String? name; + int? id; + String? blog; } diff --git a/lib/shop/page/freight_config_page.dart b/lib/shop/page/freight_config_page.dart index b8f102f85..488753924 100644 --- a/lib/shop/page/freight_config_page.dart +++ b/lib/shop/page/freight_config_page.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; @@ -7,8 +6,8 @@ import 'package:flutter_deer/shop/widgets/price_input_dialog.dart'; import 'package:flutter_deer/shop/widgets/range_price_input_dialog.dart'; import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/util/toast_utils.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/load_image.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; import 'package:flutter_deer/widgets/my_card.dart'; @@ -16,7 +15,7 @@ import 'package:flutter_deer/widgets/my_card.dart'; /// design/7店铺-店铺配置/index.html class FreightConfigPage extends StatefulWidget { - const FreightConfigPage({Key key}) : super(key: key); + const FreightConfigPage({super.key}); @override _FreightConfigPageState createState() => _FreightConfigPageState(); @@ -138,7 +137,7 @@ class _FreightConfigPageState extends State { _getPriceText(index).isEmpty ? '订单金额' : _getPriceText(index), key: Key('订单金额$index'), textAlign: TextAlign.end, - style: _getPriceText(index).isEmpty ? Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp14) : null, + style: _getPriceText(index).isEmpty ? Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp14) : null, ), ), )), @@ -146,9 +145,9 @@ class _FreightConfigPageState extends State { const Text('元'), ], ), - Gaps.vGap15, + const Spacer(), Gaps.line, - Gaps.vGap15, + const Spacer(), Row( children: [ Semantics( @@ -196,7 +195,7 @@ class _FreightConfigPageState extends State { child: Text( _list[index].price.isEmpty ? (_list[index].type == 1 ? '运费比率' : '运费金额'): _list[index].price, textAlign: TextAlign.end, - style: _list[index].price.isEmpty ? Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp14) : null, + style: _list[index].price.isEmpty ? Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp14) : null, ), ), )), @@ -285,7 +284,7 @@ class _FreightConfigPageState extends State { if (_list[index].min.isEmpty || _list[index].max.isEmpty) { return ''; } else { - return _list[index].min + '~' + _list[index].max; + return '${_list[index].min}~${_list[index].max}'; } } } diff --git a/lib/shop/page/input_text_page.dart b/lib/shop/page/input_text_page.dart index 18dab97b6..3d5de00a7 100644 --- a/lib/shop/page/input_text_page.dart +++ b/lib/shop/page/input_text_page.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; @@ -9,17 +8,17 @@ import 'package:flutter_deer/widgets/my_app_bar.dart'; class InputTextPage extends StatefulWidget { const InputTextPage({ - Key key, - @required this.title, + super.key, + required this.title, this.content, this.hintText, this.keyboardType = TextInputType.text, - }) : super(key : key); + }); final String title; - final String content; - final String hintText; - final TextInputType keyboardType; + final String? content; + final String? hintText; + final TextInputType? keyboardType; @override _InputTextPageState createState() => _InputTextPageState(); @@ -28,13 +27,13 @@ class InputTextPage extends StatefulWidget { class _InputTextPageState extends State { final TextEditingController _controller = TextEditingController(); - List _inputFormatters; - int _maxLength; + List? _inputFormatters; + late int _maxLength; @override void initState() { super.initState(); - _controller.text = widget.content; + _controller.text = widget.content ?? ''; _maxLength = widget.keyboardType == TextInputType.phone ? 11 : 30; _inputFormatters = widget.keyboardType == TextInputType.phone ? [FilteringTextInputFormatter.allow(RegExp('[0-9]'))] : null; } diff --git a/lib/shop/page/message_page.dart b/lib/shop/page/message_page.dart index 461eaed42..993850d32 100644 --- a/lib/shop/page/message_page.dart +++ b/lib/shop/page/message_page.dart @@ -1,5 +1,3 @@ - -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/widgets/my_app_bar.dart'; @@ -8,7 +6,7 @@ import 'package:flutter_deer/widgets/my_card.dart'; /// design/8设置/index.html#artboard2 class MessagePage extends StatefulWidget { - const MessagePage({Key key}) : super(key: key); + const MessagePage({super.key}); @override _MessagePageState createState() => _MessagePageState(); @@ -58,7 +56,7 @@ class _MessageItemState extends State<_MessageItem> { return Column( children: [ Gaps.vGap15, - Text('2021-5-31 17:19:36', style: Theme.of(context).textTheme.subtitle2), + Text('2021-5-31 17:19:36', style: Theme.of(context).textTheme.titleSmall), Gaps.vGap8, MyCard( child: Padding( diff --git a/lib/shop/page/select_address_page.dart b/lib/shop/page/select_address_page.dart index 58a7176a9..58614c692 100644 --- a/lib/shop/page/select_address_page.dart +++ b/lib/shop/page/select_address_page.dart @@ -1,14 +1,14 @@ - -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_2d_amap/flutter_2d_amap.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; +import 'package:flutter_deer/util/other_utils.dart'; +import 'package:flutter_deer/util/toast_utils.dart'; import 'package:flutter_deer/widgets/my_button.dart'; -import 'package:flutter_deer/widgets/search_bar.dart'; +import 'package:flutter_deer/widgets/my_search_bar.dart'; class AddressSelectPage extends StatefulWidget { - const AddressSelectPage({Key key}) : super(key: key); + const AddressSelectPage({super.key}); @override _AddressSelectPageState createState() => _AddressSelectPageState(); @@ -19,7 +19,7 @@ class _AddressSelectPageState extends State { List _list = []; int _index = 0; final ScrollController _controller = ScrollController(); - AMap2DController _aMap2DController; + AMap2DController? _aMap2DController; @override void dispose() { @@ -30,10 +30,11 @@ class _AddressSelectPageState extends State { @override void initState() { super.initState(); + Flutter2dAMap.updatePrivacy(true); /// 配置key Flutter2dAMap.setApiKey( iOSKey: '4327916279bf45a044bb53b947442387', - webKey: '4e479545913a3a180b3cffc267dad646', + webKey: 'c9446a164fd1245dd110b54114095303', ); } @@ -41,14 +42,12 @@ class _AddressSelectPageState extends State { Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, - appBar: SearchBar( + appBar: MySearchBar( hintText: '搜索地址', onPressed: (text) { _controller.animateTo(0.0, duration: const Duration(milliseconds: 10), curve: Curves.ease); _index = 0; - if (_aMap2DController != null) { - _aMap2DController.search(text); - } + _aMap2DController?.search(text); }, ), body: SafeArea( @@ -88,9 +87,7 @@ class _AddressSelectPageState extends State { date: _list[index], onTap: () { _index = index; - if (_aMap2DController != null) { - _aMap2DController.move(_list[index].latitude, _list[index].longitude); - } + _aMap2DController?.move(_list[index].latitude.nullSafe, _list[index].longitude.nullSafe); setState(() { }); }, @@ -100,6 +97,10 @@ class _AddressSelectPageState extends State { ), MyButton( onPressed: () { + if (_list.isEmpty) { + Toast.show('未选择地址!'); + return; + } NavigatorUtils.goBackWithParams(context, _list[_index]); }, text: '确认选择地址', @@ -114,15 +115,14 @@ class _AddressSelectPageState extends State { class _AddressItem extends StatelessWidget { const _AddressItem({ - Key key, - @required this.date, + required this.date, this.isSelected = false, this.onTap, - }) : super(key: key); + }); final PoiSearch date; final bool isSelected; - final GestureTapCallback onTap; + final GestureTapCallback? onTap; @override Widget build(BuildContext context) { @@ -136,10 +136,7 @@ class _AddressItem extends StatelessWidget { children: [ Expanded( child: Text( - date.provinceName + ' ' + - date.cityName + ' ' + - date.adName + ' ' + - date.title, + '${date.provinceName.nullSafe} ${date.cityName.nullSafe} ${date.adName.nullSafe} ${date.title.nullSafe}', ), ), Visibility( @@ -152,4 +149,3 @@ class _AddressItem extends StatelessWidget { ); } } - diff --git a/lib/shop/page/shop_page.dart b/lib/shop/page/shop_page.dart index c7aa8fac9..4e57eef75 100644 --- a/lib/shop/page/shop_page.dart +++ b/lib/shop/page/shop_page.dart @@ -1,12 +1,11 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/account/account_router.dart'; import 'package:flutter_deer/mvp/base_page.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/setting/setting_router.dart'; -import 'package:flutter_deer/shop/models/user_entity.dart'; import 'package:flutter_deer/shop/iview/shop_iview.dart'; +import 'package:flutter_deer/shop/models/user_entity.dart'; import 'package:flutter_deer/shop/presenter/shop_presenter.dart'; import 'package:flutter_deer/shop/provider/user_provider.dart'; import 'package:flutter_deer/shop/shop_router.dart'; @@ -19,9 +18,9 @@ import 'package:provider/provider.dart'; class ShopPage extends StatefulWidget { const ShopPage({ - Key key, + super.key, this.isAccessibilityTest = false, - }) : super(key : key); + }); final bool isAccessibilityTest; @@ -38,7 +37,7 @@ class _ShopPageState extends State with BasePageMixin with BasePageMixin with BasePageMixin with BasePageMixin with BasePageMixin with BasePageMixin[ + children: [ LoadAssetImage('shop/zybq', width: 40.0, height: 16.0,), Gaps.hGap8, Text('店铺账号:15000000000', style: TextStyles.textSize12) @@ -196,14 +195,13 @@ class _ShopPageState extends State with BasePageMixin data; final List image; final List darkImage; @@ -240,4 +238,3 @@ class _ShopFunctionModule extends StatelessWidget { ); } } - diff --git a/lib/shop/page/shop_setting_page.dart b/lib/shop/page/shop_setting_page.dart index 44a5b827c..0c6cde614 100644 --- a/lib/shop/page/shop_setting_page.dart +++ b/lib/shop/page/shop_setting_page.dart @@ -1,25 +1,21 @@ - - import 'package:flutter/material.dart'; import 'package:flutter_2d_amap/flutter_2d_amap.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; -import 'package:flutter_deer/shop/page/input_text_page.dart'; -import 'package:flutter_deer/shop/widgets/pay_type_dialog.dart'; import 'package:flutter_deer/shop/shop_router.dart'; +import 'package:flutter_deer/shop/widgets/pay_type_dialog.dart'; import 'package:flutter_deer/shop/widgets/price_input_dialog.dart'; import 'package:flutter_deer/shop/widgets/send_type_dialog.dart'; -import 'package:flutter_deer/util/app_navigator.dart'; import 'package:flutter_deer/util/other_utils.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/click_item.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; import 'package:flutter_deer/widgets/my_scroll_view.dart'; /// design/7店铺-店铺配置/index.html#artboard17 class ShopSettingPage extends StatefulWidget { - const ShopSettingPage({Key key}) : super(key: key); + const ShopSettingPage({super.key}); @override _ShopSettingPageState createState() => _ShopSettingPageState(); @@ -45,6 +41,13 @@ class _ShopSettingPageState extends State { appBar: const MyAppBar(), body: MyScrollView( padding: const EdgeInsets.symmetric(vertical: 16.0), + bottomButton: Padding( + padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 8.0), + child: MyButton( + text: '提交', + onPressed: () => NavigatorUtils.goBack(context), + ), + ), children: [ Gaps.vGap5, Row( @@ -175,23 +178,13 @@ class _ShopSettingPageState extends State { NavigatorUtils.pushResult(context, ShopRouter.addressSelectPage, (result) { setState(() { final PoiSearch model = result as PoiSearch; - _address = model.provinceName + ' ' + - model.cityName + ' ' + - model.adName + ' ' + - model.title; + _address = '${model.provinceName.nullSafe} ${model.cityName.nullSafe} ${model.adName.nullSafe} ${model.title.nullSafe}'; }); }); }, ), Gaps.vGap8, ], - bottomButton: Padding( - padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 8.0), - child: MyButton( - text: '提交', - onPressed: () => NavigatorUtils.goBack(context), - ), - ), ) ); } @@ -211,20 +204,21 @@ class _ShopSettingPageState extends State { } void _goInputTextPage(BuildContext context, String title, - String hintText, String content, Function(Object) function, {TextInputType keyboardType}) { - AppNavigator.pushResult( - context, - InputTextPage( - title: title, - hintText: hintText, - content: content, - keyboardType: keyboardType, - ), - function, + String hintText, String content, void Function(Object?) function, + {TextInputType? keyboardType}) { + + NavigatorUtils.pushResult(context, + ShopRouter.inputTextPage, function, + arguments: InputTextPageArgumentsData( + title: title, + hintText: hintText, + content: content, + keyboardType: keyboardType, + ) ); } - void _showInputDialog(String title, Function(String) onPressed) { + void _showInputDialog(String title, void Function(String) onPressed) { showDialog( context: context, barrierDismissible: false, @@ -255,7 +249,7 @@ class _ShopSettingPageState extends State { } void _showSendTypeDialog() { - showElasticDialog ( + showElasticDialog( context: context, barrierDismissible: false, builder: (BuildContext context) { @@ -270,3 +264,19 @@ class _ShopSettingPageState extends State { ); } } + + +class InputTextPageArgumentsData { + + InputTextPageArgumentsData({ + required this.title, + this.content, + this.hintText, + this.keyboardType, +}); + + late String title; + late String? content; + late String? hintText; + late TextInputType? keyboardType; +} diff --git a/lib/shop/presenter/shop_presenter.dart b/lib/shop/presenter/shop_presenter.dart index 174ffe989..3841e6973 100644 --- a/lib/shop/presenter/shop_presenter.dart +++ b/lib/shop/presenter/shop_presenter.dart @@ -1,11 +1,9 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/account/models/city_entity.dart'; import 'package:flutter_deer/mvp/base_page_presenter.dart'; import 'package:flutter_deer/net/net.dart'; -import 'package:flutter_deer/shop/models/user_entity.dart'; import 'package:flutter_deer/shop/iview/shop_iview.dart'; -import 'package:flutter_deer/util/log_utils.dart'; +import 'package:flutter_deer/shop/models/user_entity.dart'; class ShopPagePresenter extends BasePagePresenter { @@ -33,10 +31,8 @@ class ShopPagePresenter extends BasePagePresenter { asyncRequestNetwork>(Method.get, url: HttpApi.subscriptions, onSuccess: (data) { - data.forEach((element) { - Log.d(element.name); - }); + }, ); } -} \ No newline at end of file +} diff --git a/lib/shop/provider/user_provider.dart b/lib/shop/provider/user_provider.dart index 76b615569..29c786f1b 100644 --- a/lib/shop/provider/user_provider.dart +++ b/lib/shop/provider/user_provider.dart @@ -1,14 +1,13 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/shop/models/user_entity.dart'; class UserProvider extends ChangeNotifier { - UserEntity _user; - UserEntity get user => _user; + UserEntity? _user; + UserEntity? get user => _user; - void setUser(UserEntity user) { + void setUser(UserEntity? user) { _user = user; notifyListeners(); } -} \ No newline at end of file +} diff --git a/lib/shop/shop_router.dart b/lib/shop/shop_router.dart index da55a1c98..dc0b91e0d 100644 --- a/lib/shop/shop_router.dart +++ b/lib/shop/shop_router.dart @@ -1,8 +1,8 @@ - import 'package:fluro/fluro.dart'; import 'package:flutter_deer/routers/i_router.dart'; import 'page/freight_config_page.dart'; +import 'page/input_text_page.dart'; import 'page/message_page.dart'; import 'page/select_address_page.dart'; import 'page/shop_page.dart'; @@ -15,6 +15,7 @@ class ShopRouter implements IRouterProvider{ static String messagePage = '/shop/message'; static String freightConfigPage = '/shop/freightConfig'; static String addressSelectPage = '/shop/addressSelect'; + static String inputTextPage = '/shop/inputText'; @override void initRouter(FluroRouter router) { @@ -23,6 +24,15 @@ class ShopRouter implements IRouterProvider{ router.define(messagePage, handler: Handler(handlerFunc: (_, __) => const MessagePage())); router.define(freightConfigPage, handler: Handler(handlerFunc: (_, __) => const FreightConfigPage())); router.define(addressSelectPage, handler: Handler(handlerFunc: (_, __) => const AddressSelectPage())); + router.define(inputTextPage, handler: Handler(handlerFunc: (context, params) { + /// 类参数 + final args = context!.settings!.arguments! as InputTextPageArgumentsData; + return InputTextPage( + title: args.title, + hintText: args.hintText, + content: args.content, + keyboardType: args.keyboardType, + ); + })); } - -} \ No newline at end of file +} diff --git a/lib/shop/widgets/pay_type_dialog.dart b/lib/shop/widgets/pay_type_dialog.dart index 9d6f32cb4..1e3cb3191 100644 --- a/lib/shop/widgets/pay_type_dialog.dart +++ b/lib/shop/widgets/pay_type_dialog.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; @@ -11,13 +10,13 @@ import 'package:flutter_deer/widgets/load_image.dart'; class PayTypeDialog extends StatefulWidget { const PayTypeDialog({ - Key key, + super.key, this.value, - this.onPressed, - }) : super(key : key); + required this.onPressed, + }); - final List value; - final Function(List) onPressed; + final List? value; + final void Function(List) onPressed; @override _PayTypeDialog createState() => _PayTypeDialog(); @@ -26,7 +25,7 @@ class PayTypeDialog extends StatefulWidget { class _PayTypeDialog extends State { - List _selectValue; + late List _selectValue; final List _list = ['线上支付', '对公转账', '货到付款']; Widget _buildItem(int index) { @@ -81,4 +80,4 @@ class _PayTypeDialog extends State { }, ); } -} \ No newline at end of file +} diff --git a/lib/shop/widgets/price_input_dialog.dart b/lib/shop/widgets/price_input_dialog.dart index 0f9e3fa43..3afd98f36 100644 --- a/lib/shop/widgets/price_input_dialog.dart +++ b/lib/shop/widgets/price_input_dialog.dart @@ -1,6 +1,4 @@ - import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/input_formatter/number_text_input_formatter.dart'; import 'package:flutter_deer/util/theme_utils.dart'; @@ -12,15 +10,15 @@ import 'package:flutter_deer/widgets/base_dialog.dart'; class PriceInputDialog extends StatefulWidget { const PriceInputDialog({ - Key key, + super.key, this.title, this.inputMaxPrice = 100000, - this.onPressed, - }) : super(key : key); + required this.onPressed, + }); - final String title; + final String? title; final double inputMaxPrice; - final Function(String) onPressed; + final void Function(String) onPressed; @override _PriceInputDialog createState() => _PriceInputDialog(); @@ -53,7 +51,6 @@ class _PriceInputDialog extends State { key: const Key('price_input'), autofocus: true, controller: _controller, - maxLines: 1, //style: TextStyles.textDark14, keyboardType: const TextInputType.numberWithOptions(decimal: true), // 金额限制数字格式 @@ -77,4 +74,4 @@ class _PriceInputDialog extends State { }, ); } -} \ No newline at end of file +} diff --git a/lib/shop/widgets/range_price_input_dialog.dart b/lib/shop/widgets/range_price_input_dialog.dart index a657aa3fe..d6ea38795 100644 --- a/lib/shop/widgets/range_price_input_dialog.dart +++ b/lib/shop/widgets/range_price_input_dialog.dart @@ -1,6 +1,4 @@ - import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/util/input_formatter/number_text_input_formatter.dart'; import 'package:flutter_deer/util/theme_utils.dart'; @@ -11,13 +9,13 @@ import 'package:flutter_deer/widgets/base_dialog.dart'; class RangePriceInputDialog extends StatefulWidget { const RangePriceInputDialog({ - Key key, + super.key, this.title, - this.onPressed, - }) : super(key : key); + required this.onPressed, + }); - final String title; - final Function(String, String) onPressed; + final String? title; + final void Function(String, String) onPressed; @override _RangePriceInputDialog createState() => _RangePriceInputDialog(); @@ -54,10 +52,10 @@ class _RangePriceInputDialog extends State { ), Container( alignment: Alignment.center, - child: const Text('至'), padding: const EdgeInsets.symmetric(horizontal: 12.0), color: context.dialogBackgroundColor, - height: double.infinity + height: double.infinity, + child: const Text('至') ), Expanded( child: _buildTextField(_controller1), @@ -85,7 +83,6 @@ class _RangePriceInputDialog extends State { autofocus: true, //style: TextStyles.textDark14, controller: controller, - maxLines: 1, keyboardType: const TextInputType.numberWithOptions(decimal: true), // 金额限制数字格式 inputFormatters: [UsNumberTextInputFormatter()], @@ -97,4 +94,4 @@ class _RangePriceInputDialog extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/shop/widgets/send_type_dialog.dart b/lib/shop/widgets/send_type_dialog.dart index d572443e0..3be7badf7 100644 --- a/lib/shop/widgets/send_type_dialog.dart +++ b/lib/shop/widgets/send_type_dialog.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; @@ -10,15 +9,14 @@ import 'package:flutter_deer/widgets/load_image.dart'; class SendTypeDialog extends StatefulWidget { const SendTypeDialog({ - Key key, - this.onPressed, - }) : super(key : key); + super.key, + required this.onPressed, + }); - final Function(int, String) onPressed; + final void Function(int, String) onPressed; @override _SendTypeDialog createState() => _SendTypeDialog(); - } class _SendTypeDialog extends State { @@ -77,4 +75,4 @@ class _SendTypeDialog extends State { }, ); } -} \ No newline at end of file +} diff --git a/lib/statistics/page/goods_statistics_page.dart b/lib/statistics/page/goods_statistics_page.dart index 069da587d..a9add263a 100644 --- a/lib/statistics/page/goods_statistics_page.dart +++ b/lib/statistics/page/goods_statistics_page.dart @@ -1,4 +1,3 @@ - import 'dart:math'; import 'package:flutter/material.dart'; @@ -7,8 +6,8 @@ import 'package:flutter_deer/statistics/widgets/selected_date.dart'; import 'package:flutter_deer/util/date_utils.dart' as date; import 'package:flutter_deer/util/image_utils.dart'; import 'package:flutter_deer/util/theme_utils.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/load_image.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_card.dart'; import 'package:flutter_deer/widgets/pie_chart/pie_chart.dart'; import 'package:flutter_deer/widgets/pie_chart/pie_data.dart'; @@ -16,7 +15,7 @@ import 'package:flutter_deer/widgets/pie_chart/pie_data.dart'; /// design/5统计/index.html#artboard11 class GoodsStatisticsPage extends StatefulWidget { - const GoodsStatisticsPage({Key key}) : super(key: key); + const GoodsStatisticsPage({super.key}); @override _GoodsStatisticsPageState createState() => _GoodsStatisticsPageState(); @@ -24,7 +23,7 @@ class GoodsStatisticsPage extends StatefulWidget { class _GoodsStatisticsPageState extends State { - DateTime _initialDay; + late DateTime _initialDay; int _selectedIndex = 2; /// false 待配货 true 已配货 bool _type = false; @@ -43,11 +42,11 @@ class _GoodsStatisticsPageState extends State { Gaps.hGap12, Gaps.vLine, Gaps.hGap12, - _buildSelectedText('${_initialDay.month.toString()}月', 1), + _buildSelectedText('${_initialDay.month}月', 1), Gaps.hGap12, Gaps.vLine, Gaps.hGap12, - _buildSelectedText(_type ? '${date.DateUtils.previousWeekToString(_initialDay)} -${date.DateUtils.apiDayFormat2(_initialDay)}' : '${_initialDay.day.toString()}日', 2), + _buildSelectedText(_type ? '${date.DateUtils.previousWeekToString(_initialDay)} -${date.DateUtils.apiDayFormat2(_initialDay)}' : '${_initialDay.day}日', 2), ], ); @@ -176,7 +175,7 @@ class _GoodsStatisticsPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text('那鲁火多饮料', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(fontWeight: FontWeight.bold, fontSize: Dimens.font_sp12)), - Text('250ml', style: Theme.of(context).textTheme.subtitle2), + Text('250ml', style: Theme.of(context).textTheme.titleSmall), ], ), ), @@ -187,8 +186,8 @@ class _GoodsStatisticsPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text('100件', style: Theme.of(context).textTheme.subtitle2), - Text('未支付', style: Theme.of(context).textTheme.subtitle2), + Text('100件', style: Theme.of(context).textTheme.titleSmall), + Text('未支付', style: Theme.of(context).textTheme.titleSmall), ], ), ), @@ -197,8 +196,8 @@ class _GoodsStatisticsPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: _type ? MainAxisAlignment.center : MainAxisAlignment.spaceBetween, children: [ - Text('400件', style: Theme.of(context).textTheme.subtitle2), - Visibility(visible: !_type, child: Text('已支付', style: Theme.of(context).textTheme.subtitle2)), + Text('400件', style: Theme.of(context).textTheme.titleSmall), + Visibility(visible: !_type, child: Text('已支付', style: Theme.of(context).textTheme.titleSmall)), ], ), ], diff --git a/lib/statistics/page/order_statistics_page.dart b/lib/statistics/page/order_statistics_page.dart index 1fcbed101..b11b2ac35 100644 --- a/lib/statistics/page/order_statistics_page.dart +++ b/lib/statistics/page/order_statistics_page.dart @@ -1,23 +1,22 @@ - import 'dart:math'; import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/statistics/widgets/selected_date.dart'; +import 'package:flutter_deer/util/date_utils.dart' as date; import 'package:flutter_deer/util/image_utils.dart'; import 'package:flutter_deer/util/theme_utils.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; +import 'package:flutter_deer/widgets/bezier_chart/bezier_chart.dart'; import 'package:flutter_deer/widgets/load_image.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_card.dart'; -import 'package:flutter_deer/widgets/bezier_chart/bezier_chart.dart'; -import 'package:flutter_deer/util/date_utils.dart' as date; /// design/5统计/index.html#artboard1 /// design/5统计/index.html#artboard6 class OrderStatisticsPage extends StatefulWidget { - const OrderStatisticsPage(this.index, {Key key}) : super(key: key); + const OrderStatisticsPage(this.index, {super.key}); final int index; @@ -28,18 +27,18 @@ class OrderStatisticsPage extends StatefulWidget { class _OrderStatisticsPageState extends State with TickerProviderStateMixin { int _selectedIndex = 2; - DateTime _initialDay; - Iterable _weeksDays; - List _currentMonthsDays; + late DateTime _initialDay; + late Iterable _weeksDays; + late List _currentMonthsDays; // 周视图中选择的日期 - int _selectedWeekDay; + late int _selectedWeekDay; // 月视图中选择的日期 - DateTime _selectedDay; + late DateTime _selectedDay; // 年视图中选择的月份 - int _selectedMonth; + late int _selectedMonth; final List _monthList = []; bool _isExpanded = true; - Color _unSelectedTextColor; + late Color _unSelectedTextColor; static const List _weeks = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']; @@ -79,7 +78,7 @@ class _OrderStatisticsPageState extends State with TickerPr Gaps.hGap12, Gaps.vLine, Gaps.hGap12, - _buildButton('${_initialDay.month.toString()}月', const Key('month'), 1), + _buildButton('${_initialDay.month}月', const Key('month'), 1), Gaps.hGap12, Gaps.vLine, Gaps.hGap12, @@ -104,10 +103,9 @@ class _OrderStatisticsPageState extends State with TickerPr // duration: const Duration(milliseconds: 300), // ), AnimatedSize( - child: _buildCalendar(), - vsync: this, curve: Curves.decelerate, duration: const Duration(milliseconds: 300), + child: _buildCalendar(), ), if (_selectedIndex == 1) InkWell( onTap: () { @@ -135,13 +133,13 @@ class _OrderStatisticsPageState extends State with TickerPr children: [ Text(widget.index == 1 ? '订单走势' : '交易额走势', style: TextStyles.textBold18), Gaps.vGap16, - _buildChart(Colours.app_main, const Color(0x805793FA), widget.index == 1 ? '全部订单' : '交易额(元)', '3000'), + _buildChart(Colours.app_main, Colours.shadow_blue, widget.index == 1 ? '全部订单' : '交易额(元)', '3000'), if (widget.index == 1) Column( children: [ Gaps.vGap16, _buildChart(const Color(0xFFFFAA33), const Color(0x80FFAA33), '完成订单', '2000'), Gaps.vGap16, - _buildChart(Theme.of(context).errorColor, const Color(0x80FF4759), '取消订单', '1000'), + _buildChart(Theme.of(context).colorScheme.error, const Color(0x80FF4759), '取消订单', '1000'), Gaps.vGap16, ], ) @@ -174,11 +172,14 @@ class _OrderStatisticsPageState extends State with TickerPr final Column body = Column( children: [ + Gaps.vGap16, Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + Gaps.hGap16, Text(title, style: const TextStyle(color: Colors.white)), + const Spacer(), Text(count, style: const TextStyle(color: Colors.white)), + Gaps.hGap16, ], ), Gaps.vGap4, @@ -196,10 +197,8 @@ class _OrderStatisticsPageState extends State with TickerPr ), ], config: BezierChartConfig( - footerHeight: 0, + footerHeight: 16, showVerticalIndicator: false, - verticalIndicatorFixedPosition: false, - snap: true, backgroundColor: color, ), ), @@ -213,7 +212,7 @@ class _OrderStatisticsPageState extends State with TickerPr color: color, shadowColor: shadowColor, child: Container( - padding: const EdgeInsets.all(16.0), + //padding: const EdgeInsets.symmetric(horizontal: 16.0), decoration: BoxDecoration( image: DecorationImage( image: ImageUtils.getAssetImage('statistic/chart_fg'), @@ -226,12 +225,12 @@ class _OrderStatisticsPageState extends State with TickerPr ); } - List data = []; - List data1 = []; - List data2 = []; + List> data = []; + List> data1 = []; + List> data2 = []; // 数据变化图标会刷新,否则不会 - List _getRandomData() { + List> _getRandomData() { if (data.isEmpty) { for (int i = 0; i < 7; i++) { data.add(DataPoint(value: Random.secure().nextInt(3000).toDouble(), xAxis: (i * 5).toDouble())); @@ -254,7 +253,7 @@ class _OrderStatisticsPageState extends State with TickerPr } Widget _buildCalendar() { - List children; + List children = []; if (_selectedIndex == 0) { children = _builderYearCalendar(); } else if (_selectedIndex == 1) { @@ -273,11 +272,12 @@ class _OrderStatisticsPageState extends State with TickerPr List _buildWeeks() { final List widgets = []; - _weeks.forEach((str) { + void addWidget(String str) { widgets.add(Center( - child: Text(str, style: Theme.of(context).textTheme.subtitle2), + child: Text(str, style: Theme.of(context).textTheme.titleSmall), )); - }); + } + _weeks.forEach(addWidget); return widgets; } @@ -290,7 +290,8 @@ class _OrderStatisticsPageState extends State with TickerPr list = date.DateUtils.daysInWeek(_selectedDay); } dayWidgets.addAll(_buildWeeks()); - list.forEach((day) { + + void addButton(DateTime day) { dayWidgets.add( Center( child: SelectedDateButton( @@ -309,13 +310,15 @@ class _OrderStatisticsPageState extends State with TickerPr ), ), ); - }); + } + + list.forEach(addButton); return dayWidgets; } List _builderYearCalendar() { final List monthWidgets = []; - _monthList.forEach((month) { + void addButton(int month) { monthWidgets.add( Center( child: SelectedDateButton( @@ -331,13 +334,15 @@ class _OrderStatisticsPageState extends State with TickerPr ), ), ); - }); + } + _monthList.forEach(addButton); return monthWidgets; } List _builderWeekCalendar() { final List dayWidgets = []; - _weeksDays.forEach((day) { + + void addButton(DateTime day) { dayWidgets.add( Center( child: SelectedDateButton( @@ -352,8 +357,10 @@ class _OrderStatisticsPageState extends State with TickerPr }, ), ), - ); - }); + ); + } + _weeksDays.forEach(addButton); return dayWidgets; } + } diff --git a/lib/statistics/page/statistics_page.dart b/lib/statistics/page/statistics_page.dart index 96f1fc870..d14e60e4f 100644 --- a/lib/statistics/page/statistics_page.dart +++ b/lib/statistics/page/statistics_page.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/order/page/order_page.dart'; import 'package:flutter_deer/res/resources.dart'; @@ -15,7 +14,7 @@ import 'package:flutter_deer/widgets/my_flexible_space_bar.dart'; /// design/5统计/index.html class StatisticsPage extends StatefulWidget { - const StatisticsPage({Key key}) : super(key: key); + const StatisticsPage({super.key}); @override _StatisticsPageState createState() => _StatisticsPageState(); @@ -40,8 +39,7 @@ class _StatisticsPageState extends State { isDark = context.isDark; return [ SliverAppBar( - brightness: Brightness.dark, - leading: Gaps.empty, + systemOverlayStyle: isDark ? ThemeUtils.light : ThemeUtils.dark, backgroundColor: Colors.transparent, elevation: 0.0, centerTitle: true, @@ -74,9 +72,9 @@ class _StatisticsPageState extends State { margin: const EdgeInsets.symmetric(horizontal: 16.0), alignment: Alignment.center, height: 120.0, - child: MyCard( + child: const MyCard( child: Row( - children: const [ + children: [ _StatisticsTab('新订单(单)', 'xdd', '80'), _StatisticsTab('待配送(单)', 'dps', '80'), _StatisticsTab('今日交易额(元)', 'jrjye', '8000.00'), @@ -88,12 +86,12 @@ class _StatisticsPageState extends State { , 120.0, ), ), - SliverToBoxAdapter( + const SliverToBoxAdapter( child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), + padding: EdgeInsets.symmetric(horizontal: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, - children: const [ + children: [ Gaps.vGap32, Text('数据走势', style: TextStyles.textBold18), Gaps.vGap16, @@ -113,7 +111,7 @@ class _StatisticsPageState extends State { class _StatisticsItem extends StatelessWidget { - const _StatisticsItem(this.title, this.img, this.index, {Key key}): super(key: key); + const _StatisticsItem(this.title, this.img, this.index); final String title; final String img; @@ -173,7 +171,7 @@ class _StatisticsTab extends StatelessWidget { children: [ LoadAssetImage('statistic/$img', width: 40.0, height: 40.0), Gaps.vGap4, - Text(title, style: Theme.of(context).textTheme.subtitle2), + Text(title, style: Theme.of(context).textTheme.titleSmall), Gaps.vGap8, Text(content, style: const TextStyle(fontSize: Dimens.font_sp18)), ], @@ -181,4 +179,4 @@ class _StatisticsTab extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/statistics/statistics_router.dart b/lib/statistics/statistics_router.dart index 3b2266172..3be21b257 100644 --- a/lib/statistics/statistics_router.dart +++ b/lib/statistics/statistics_router.dart @@ -1,4 +1,3 @@ - import 'package:fluro/fluro.dart'; import 'package:flutter_deer/routers/i_router.dart'; @@ -14,10 +13,10 @@ class StatisticsRouter implements IRouterProvider{ @override void initRouter(FluroRouter router) { router.define(orderStatisticsPage, handler: Handler(handlerFunc: (_, params) { - final int index = int.parse(params['index']?.first); + final int index = int.parse(params['index']?.first ?? '0'); return OrderStatisticsPage(index); })); router.define(goodsStatisticsPage, handler: Handler(handlerFunc: (_, __) => const GoodsStatisticsPage())); } -} \ No newline at end of file +} diff --git a/lib/statistics/widgets/selected_date.dart b/lib/statistics/widgets/selected_date.dart index d9ea4d018..92d89c1cd 100644 --- a/lib/statistics/widgets/selected_date.dart +++ b/lib/statistics/widgets/selected_date.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/util/theme_utils.dart'; @@ -6,23 +5,22 @@ import 'package:flutter_deer/util/theme_utils.dart'; class SelectedDateButton extends StatelessWidget { const SelectedDateButton(this.text,{ - Key key, + super.key, this.fontSize = 14.0, this.selected = false, - @required this.unSelectedTextColor, + required this.unSelectedTextColor, this.enable = true, this.onTap, this.semanticsLabel - }): assert(unSelectedTextColor != null, 'The [unSelectedTextColor] argument must not be null.'), - super(key: key); + }); final String text; final double fontSize; final bool selected; final Color unSelectedTextColor; - final GestureTapCallback onTap; + final GestureTapCallback? onTap; final bool enable; - final String semanticsLabel; + final String? semanticsLabel; @override Widget build(BuildContext context) { @@ -33,7 +31,8 @@ class SelectedDateButton extends StatelessWidget { borderRadius: BorderRadius.circular(16.0), onTap: onTap, child: Container( - constraints: const BoxConstraints( + constraints: BoxConstraints( + maxWidth: fontSize > 14 ? double.infinity : 32.0, // 日历按钮32 * 32 minWidth: 32.0, maxHeight: 32.0, minHeight: 32.0, @@ -41,24 +40,17 @@ class SelectedDateButton extends StatelessWidget { padding: EdgeInsets.symmetric(horizontal: fontSize > 14 ? 10.0 : 0.0), decoration: selected ? BoxDecoration( borderRadius: BorderRadius.circular(20.0), -// shape: BoxShape.circle, boxShadow: context.isDark ? null : const [ - BoxShadow(color: Color(0x805793FA), offset: Offset(0.0, 2.0), blurRadius: 8.0, spreadRadius: 0.0), + BoxShadow(color: Colours.shadow_blue, offset: Offset(0.0, 2.0), blurRadius: 8.0), ], gradient: const LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, - colors: [Color(0xFF5758FA), Color(0xFF5793FA)], + colors: [Color(0xFF5758FA), Colours.gradient_blue], ), ) : null, - child: Column( - // 此处为了宽度适应,高度撑满。 - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - child - ], - ), + alignment: Alignment.center, + child: child, ), ); } @@ -88,4 +80,3 @@ class SelectedDateButton extends StatelessWidget { return enable ? (selected ? Colors.white : unSelectedTextColor) : Colours.text_gray_c; } } - diff --git a/lib/store/page/store_audit_page.dart b/lib/store/page/store_audit_page.dart index 5dd5d5e72..121c65490 100644 --- a/lib/store/page/store_audit_page.dart +++ b/lib/store/page/store_audit_page.dart @@ -1,26 +1,26 @@ - import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_2d_amap/flutter_2d_amap.dart'; +import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/shop/shop_router.dart'; import 'package:flutter_deer/store/store_router.dart'; +import 'package:flutter_deer/util/other_utils.dart'; import 'package:flutter_deer/util/theme_utils.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; import 'package:flutter_deer/widgets/my_scroll_view.dart'; import 'package:flutter_deer/widgets/selected_image.dart'; import 'package:flutter_deer/widgets/selected_item.dart'; import 'package:flutter_deer/widgets/text_field_item.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:flutter_deer/res/resources.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; /// design/2店铺审核/index.html class StoreAuditPage extends StatefulWidget { - const StoreAuditPage({Key key}) : super(key: key); + const StoreAuditPage({super.key}); @override _StoreAuditPageState createState() => _StoreAuditPageState(); @@ -40,7 +40,6 @@ class _StoreAuditPageState extends State { return KeyboardActionsConfig( keyboardActionsPlatform: KeyboardActionsPlatform.IOS, keyboardBarColor: ThemeUtils.getKeyboardActionsColor(context), - nextFocus: true, actions: [ KeyboardActionsItem( focusNode: _nodeText1, @@ -56,9 +55,9 @@ class _StoreAuditPageState extends State { (node) { return GestureDetector( onTap: () => node.unfocus(), - child: const Padding( - padding: EdgeInsets.only(right: 16.0), - child: Text('关闭'), + child: Padding( + padding: const EdgeInsets.only(right: 16.0), + child: Text(Utils.getCurrLocale() == 'zh' ? '关闭' : 'Close'), ), ); }, @@ -78,17 +77,17 @@ class _StoreAuditPageState extends State { padding: const EdgeInsets.symmetric(vertical: 16.0), keyboardConfig: _buildConfig(context), tapOutsideToDismiss: true, - children: _buildBody(), bottomButton: Padding( padding: const EdgeInsets.only(left: 16.0, right: 16.0, bottom: 8.0), child: MyButton( onPressed: () { - debugPrint('文件路径:${_imageGlobalKey.currentState.pickedFile?.path}'); + debugPrint('文件路径:${_imageGlobalKey.currentState?.pickedFile?.path}'); NavigatorUtils.push(context, StoreRouter.auditResultPage); }, text: '提交', ), ), + children: _buildBody(), ), /// 同时存在底部按钮与keyboardConfig配置时,为保证Android与iOS平台软键盘弹出高度正常,添加下面的代码。 resizeToAvoidBottomInset: defaultTargetPlatform != TargetPlatform.iOS, @@ -112,7 +111,7 @@ class _StoreAuditPageState extends State { Center( child: Text( '店主手持身份证或营业执照', - style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp14), + style: Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp14), ), ), Gaps.vGap16, @@ -133,10 +132,7 @@ class _StoreAuditPageState extends State { NavigatorUtils.pushResult(context, ShopRouter.addressSelectPage, (result) { setState(() { final PoiSearch model = result as PoiSearch; - _address = model.provinceName + ' ' + - model.cityName + ' ' + - model.adName + ' ' + - model.title; + _address = '${model.provinceName.nullSafe} ${model.cityName.nullSafe} ${model.adName.nullSafe} ${model.title.nullSafe}'; }); }); } @@ -172,7 +168,6 @@ class _StoreAuditPageState extends State { return DraggableScrollableSheet( key: const Key('goods_sort'), initialChildSize: 0.7, - maxChildSize: 1, minChildSize: 0.65, expand: false, builder: (_, scrollController) { diff --git a/lib/store/page/store_audit_result_page.dart b/lib/store/page/store_audit_result_page.dart index da1774784..05672e8f0 100644 --- a/lib/store/page/store_audit_result_page.dart +++ b/lib/store/page/store_audit_result_page.dart @@ -1,16 +1,15 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; import 'package:flutter_deer/routers/routers.dart'; -import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/load_image.dart'; +import 'package:flutter_deer/widgets/my_app_bar.dart'; import 'package:flutter_deer/widgets/my_button.dart'; /// design/2店铺审核/index.html#artboard2 class StoreAuditResultPage extends StatefulWidget { - const StoreAuditResultPage({Key key}) : super(key: key); + const StoreAuditResultPage({super.key}); @override _StoreAuditResultPageState createState() => _StoreAuditResultPageState(); @@ -26,7 +25,6 @@ class _StoreAuditResultPageState extends State { body: Padding( padding: const EdgeInsets.all(16.0), child: Column( - crossAxisAlignment: CrossAxisAlignment.center, children: [ Gaps.vGap50, const LoadAssetImage('store/icon_success', @@ -41,12 +39,12 @@ class _StoreAuditResultPageState extends State { Gaps.vGap8, Text( '2021-02-21 15:20:10', - style: Theme.of(context).textTheme.subtitle2, + style: Theme.of(context).textTheme.titleSmall, ), Gaps.vGap8, Text( '预计完成时间:02月28日', - style: Theme.of(context).textTheme.subtitle2, + style: Theme.of(context).textTheme.titleSmall, ), Gaps.vGap24, MyButton( diff --git a/lib/store/store_router.dart b/lib/store/store_router.dart index 84b3f3959..fb4c69e61 100644 --- a/lib/store/store_router.dart +++ b/lib/store/store_router.dart @@ -1,4 +1,3 @@ - import 'package:fluro/fluro.dart'; import 'package:flutter_deer/routers/i_router.dart'; @@ -17,4 +16,4 @@ class StoreRouter implements IRouterProvider{ router.define(auditResultPage, handler: Handler(handlerFunc: (_, __) => const StoreAuditResultPage())); } -} \ No newline at end of file +} diff --git a/lib/util/app_navigator.dart b/lib/util/app_navigator.dart index 9b46668f0..01316858d 100644 --- a/lib/util/app_navigator.dart +++ b/lib/util/app_navigator.dart @@ -28,11 +28,11 @@ class AppNavigator { context, MaterialPageRoute( builder: (BuildContext context) => scene, - ), (route) => route == null + ), (route) => false ); } - static void pushResult(BuildContext context, Widget scene, Function(Object) function) { + static void pushResult(BuildContext context, Widget scene, void Function(Object?) function) { Navigator.push( context, MaterialPageRoute( @@ -45,7 +45,7 @@ class AppNavigator { } function(result); }).catchError((dynamic error) { - print('$error'); + debugPrint('$error'); }); } diff --git a/lib/util/change_notifier_manage.dart b/lib/util/change_notifier_manage.dart index 3255de26b..90d4aa306 100644 --- a/lib/util/change_notifier_manage.dart +++ b/lib/util/change_notifier_manage.dart @@ -1,5 +1,3 @@ - -import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; /// @weilu https://github.com/simplezhli/flutter_deer @@ -33,7 +31,7 @@ import 'package:flutter/widgets.dart'; /// final FocusNode _nodeText = FocusNode(); /// /// @override -/// Map> changeNotifier() { +/// Map?>? changeNotifier() { /// return { /// _controller: [callback], /// _nodeText: null, @@ -43,9 +41,9 @@ import 'package:flutter/widgets.dart'; /// ``` mixin ChangeNotifierMixin on State { - Map> _map; + Map?>? _map; - Map> changeNotifier(); + Map?>? changeNotifier(); @override void initState() { @@ -53,9 +51,12 @@ mixin ChangeNotifierMixin on State { /// 遍历数据,如果callbacks不为空则添加监听 _map?.forEach((changeNotifier, callbacks) { if (callbacks != null && callbacks.isNotEmpty) { - callbacks.forEach((callback) { + + void addListener(VoidCallback callback) { changeNotifier?.addListener(callback); - }); + } + + callbacks.forEach(addListener); } }); super.initState(); @@ -65,12 +66,14 @@ mixin ChangeNotifierMixin on State { void dispose() { _map?.forEach((changeNotifier, callbacks) { if (callbacks != null && callbacks.isNotEmpty) { - callbacks.forEach((callback) { + void removeListener(VoidCallback callback) { changeNotifier?.removeListener(callback); - }); + } + + callbacks.forEach(removeListener); } changeNotifier?.dispose(); }); super.dispose(); } -} \ No newline at end of file +} diff --git a/lib/util/date_utils.dart b/lib/util/date_utils.dart index 3c9486107..5519a22ce 100644 --- a/lib/util/date_utils.dart +++ b/lib/util/date_utils.dart @@ -1,4 +1,3 @@ -library utils; import 'package:intl/intl.dart'; @@ -98,8 +97,8 @@ class DateUtils { /// The last day of a given month static DateTime lastDayOfMonth(DateTime month) { final beginningNextMonth = (month.month < 12) - ? DateTime(month.year, month.month + 1, 1) - : DateTime(month.year + 1, 1, 1); + ? DateTime(month.year, month.month + 1) + : DateTime(month.year + 1); return beginningNextMonth.subtract(const Duration(days: 1)); } diff --git a/lib/util/device_utils.dart b/lib/util/device_utils.dart index 9898f30a6..4f546415b 100644 --- a/lib/util/device_utils.dart +++ b/lib/util/device_utils.dart @@ -1,7 +1,7 @@ - import 'dart:io'; -import 'package:device_info/device_info.dart'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter_deer/res/constant.dart'; /// https://medium.com/gskinner-team/flutter-simplify-platform-screen-size-detection-4cb6fc4f7ed1 class Device { @@ -16,7 +16,7 @@ class Device { static bool get isFuchsia => !isWeb && Platform.isFuchsia; static bool get isIOS => !isWeb && Platform.isIOS; - static AndroidDeviceInfo _androidInfo; + static late AndroidDeviceInfo _androidInfo; static Future initDeviceInfo() async { if (isAndroid) { @@ -27,10 +27,13 @@ class Device { /// 使用前记得初始化 static int getAndroidSdkInt() { - if (isAndroid && _androidInfo != null) { + if (Constant.isDriverTest) { + return -1; + } + if (isAndroid) { return _androidInfo.version.sdkInt; } else { return -1; } } -} \ No newline at end of file +} diff --git a/lib/util/handle_error_utils.dart b/lib/util/handle_error_utils.dart index 5fa15afa4..0b60052da 100644 --- a/lib/util/handle_error_utils.dart +++ b/lib/util/handle_error_utils.dart @@ -1,10 +1,10 @@ import 'dart:async'; import 'package:flutter/widgets.dart'; -import 'package:flutter_deer/common/common.dart'; +import 'package:flutter_deer/res/constant.dart'; /// 捕获全局异常,进行统一处理。 -void handleError(void body) { +void handleError(void Function() body) { /// 重写Flutter异常回调 FlutterError.onError FlutterError.onError = (FlutterErrorDetails details) { if (!Constant.inProduction) { @@ -12,12 +12,12 @@ void handleError(void body) { FlutterError.dumpErrorToConsole(details); } else { // release时,将异常交由zone统一处理。 - Zone.current.handleUncaughtError(details.exception, details.stack); + Zone.current.handleUncaughtError(details.exception, details.stack!); } }; /// 使用runZonedGuarded捕获Flutter未捕获的异常 - runZonedGuarded(() => body, (Object error, StackTrace stackTrace) async { + runZonedGuarded(body, (Object error, StackTrace stackTrace) async { await _reportError(error, stackTrace); }); @@ -35,4 +35,4 @@ Future _reportError(Object error, StackTrace stackTrace) async { /// 将异常信息收集并上传到服务器。可以直接使用类似`flutter_bugly`插件处理异常上报。 } -} \ No newline at end of file +} diff --git a/lib/util/image_utils.dart b/lib/util/image_utils.dart index d677e8646..2a90206dd 100644 --- a/lib/util/image_utils.dart +++ b/lib/util/image_utils.dart @@ -1,8 +1,8 @@ +import 'package:cached_network_image/cached_network_image.dart'; import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; -import 'package:cached_network_image/cached_network_image.dart'; class ImageUtils { @@ -14,11 +14,11 @@ class ImageUtils { return 'assets/images/$name.${format.value}'; } - static ImageProvider getImageProvider(String imageUrl, {String holderImg = 'none'}) { + static ImageProvider getImageProvider(String? imageUrl, {String holderImg = 'none'}) { if (TextUtil.isEmpty(imageUrl)) { return AssetImage(getImgPath(holderImg)); } - return CachedNetworkImageProvider(imageUrl); + return CachedNetworkImageProvider(imageUrl!); } } @@ -31,4 +31,4 @@ enum ImageFormat { extension ImageFormatExtension on ImageFormat { String get value => ['png', 'jpg', 'gif', 'webp'][index]; -} \ No newline at end of file +} diff --git a/lib/util/input_formatter/fix_ios_input_formatter.dart b/lib/util/input_formatter/fix_ios_input_formatter.dart index ee16e18a3..e387a9bd0 100644 --- a/lib/util/input_formatter/fix_ios_input_formatter.dart +++ b/lib/util/input_formatter/fix_ios_input_formatter.dart @@ -1,4 +1,3 @@ - import 'dart:io'; import 'package:flutter/services.dart'; @@ -20,9 +19,9 @@ class FixIOSTextInputFormatter extends TextInputFormatter { if (Platform.isIOS) { // ios Composing变化也执行format,因为在拼音阶段没有执行LengthLimitingTextInputFormatter,从拼音到汉字需要重新执行 - if (newValue != null && newValue.composing != null && newValue.composing.isValid) { + if (newValue.composing.isValid) { // ios拼音阶段不执行长度限制的format - return null; + return TextEditingValue.empty; } } return TextEditingValue( @@ -31,4 +30,4 @@ class FixIOSTextInputFormatter extends TextInputFormatter { ); } -} \ No newline at end of file +} diff --git a/lib/util/input_formatter/number_text_input_formatter.dart b/lib/util/input_formatter/number_text_input_formatter.dart index 43466b6f0..1e1dff330 100644 --- a/lib/util/input_formatter/number_text_input_formatter.dart +++ b/lib/util/input_formatter/number_text_input_formatter.dart @@ -1,4 +1,3 @@ - import 'package:flutter/services.dart'; /// 数字、小数格式化(默认两位小数) @@ -41,8 +40,8 @@ class UsNumberTextInputFormatter extends TextInputFormatter { value = '0.'; selectionIndex++; } else if (value != '' && value != _kDefaultDouble.toString() && - _strToFloat(value, _kDefaultDouble) == _kDefaultDouble || - _getValueDigit(value) > digit || _strToFloat(value, _kDefaultDouble) > max) { + _strToFloat(value) == _kDefaultDouble || + _getValueDigit(value) > digit || _strToFloat(value) > max) { value = oldValue.text; selectionIndex = oldValue.selection.end; } @@ -51,4 +50,4 @@ class UsNumberTextInputFormatter extends TextInputFormatter { selection: TextSelection.collapsed(offset: selectionIndex), ); } -} \ No newline at end of file +} diff --git a/lib/util/log_utils.dart b/lib/util/log_utils.dart index 47b2b9036..d2f3c56ca 100644 --- a/lib/util/log_utils.dart +++ b/lib/util/log_utils.dart @@ -1,15 +1,15 @@ import 'dart:convert' as convert; import 'package:common_utils/common_utils.dart'; -import 'package:flutter_deer/common/common.dart'; +import 'package:flutter_deer/res/constant.dart'; /// 输出Log工具类 class Log { static const String tag = 'DEER-LOG'; - + static void init() { - LogUtil.init(isDebug: !Constant.inProduction); + LogUtil.init(isDebug: !Constant.inProduction, maxLen: 512); } static void d(String msg, {String tag = tag}) { @@ -32,8 +32,9 @@ class Log { _printMap(data); } else if (data is List) { _printList(data); - } else + } else { LogUtil.v(msg, tag: tag); + } } catch(e) { LogUtil.e(msg, tag: tag); } @@ -41,7 +42,7 @@ class Log { } // https://github.com/Milad-Akarie/pretty_dio_logger - static void _printMap(Map data, {String tag = tag, int tabs = 1, bool isListItem = false, bool isLast = false}) { + static void _printMap(Map data, {String tag = tag, int tabs = 1, bool isListItem = false, bool isLast = false}) { final bool isRoot = tabs == 1; final String initialIndent = _indent(tabs); tabs++; @@ -57,15 +58,15 @@ class Log { value = '"$value"'; } if (value is Map) { - if (value.isEmpty) + if (value.isEmpty) { LogUtil.v('${_indent(tabs)} $key: $value${!isLast ? ',' : ''}', tag: tag); - else { + } else { LogUtil.v('${_indent(tabs)} $key: {', tag: tag); _printMap(value, tabs: tabs); } } else if (value is List) { - if (value.isEmpty) { - LogUtil.v('${_indent(tabs)} $key: ${value.toString()}', tag: tag); + if (value.isEmpty || value.length > 50) { + LogUtil.v('${_indent(tabs)} $key: $value', tag: tag); } else { LogUtil.v('${_indent(tabs)} $key: [', tag: tag); _printList(value, tabs: tabs); @@ -80,11 +81,11 @@ class Log { LogUtil.v('$initialIndent}${isListItem && !isLast ? ',' : ''}', tag: tag); } - static void _printList(List list, {String tag = tag, int tabs = 1}) { + static void _printList(List list, {String tag = tag, int tabs = 1}) { list.asMap().forEach((i, dynamic e) { final bool isLast = i == list.length - 1; if (e is Map) { - if (e.isEmpty) { + if (_canFlattenMap(e, list)) { LogUtil.v('${_indent(tabs)} $e${!isLast ? ',' : ''}', tag: tag); } else { _printMap(e, tabs: tabs + 1, isListItem: true, isLast: isLast); @@ -95,5 +96,11 @@ class Log { }); } + /// 避免一秒内输出过多行数的日志被限制显示 + /// Single process limit 250/s drop 66 lines. + static bool _canFlattenMap(Map map, List list) { + return list.length * map.length > 100; + } + static String _indent([int tabCount = 1]) => ' ' * tabCount; -} \ No newline at end of file +} diff --git a/lib/util/other_utils.dart b/lib/util/other_utils.dart index f4ee93d2b..7e0bc2ab2 100644 --- a/lib/util/other_utils.dart +++ b/lib/util/other_utils.dart @@ -1,13 +1,12 @@ - import 'dart:ui'; import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_deer/common/common.dart'; +import 'package:flutter_deer/res/constant.dart'; import 'package:flutter_deer/util/theme_utils.dart'; import 'package:flutter_deer/util/toast_utils.dart'; -import 'package:keyboard_actions/keyboard_actions_item.dart'; import 'package:keyboard_actions/keyboard_actions_config.dart'; +import 'package:keyboard_actions/keyboard_actions_item.dart'; import 'package:sp_util/sp_util.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -15,8 +14,9 @@ class Utils { /// 打开链接 static Future launchWebURL(String url) async { - if (await canLaunch(url)) { - await launch(url); + final Uri uri = Uri.parse(url); + if (await canLaunchUrl(uri)) { + await launchUrl(uri); } else { Toast.show('打开链接失败!'); } @@ -24,22 +24,21 @@ class Utils { /// 调起拨号页 static Future launchTelURL(String phone) async { - final String url = 'tel:'+ phone; - if (await canLaunch(url)) { - await launch(url); + final Uri uri = Uri.parse('tel:$phone'); + if (await canLaunchUrl(uri)) { + await launchUrl(uri); } else { Toast.show('拨号失败!'); } } static String formatPrice(String price, {MoneyFormat format = MoneyFormat.END_INTEGER}){ - return MoneyUtil.changeYWithUnit(NumUtil.getDoubleByValueStr(price), MoneyUnit.YUAN, format: format); + return MoneyUtil.changeYWithUnit(NumUtil.getDoubleByValueStr(price) ?? 0, MoneyUnit.YUAN, format: format); } static KeyboardActionsConfig getKeyboardActionsConfig(BuildContext context, List list) { return KeyboardActionsConfig( keyboardBarColor: ThemeUtils.getKeyboardActionsColor(context), - nextFocus: true, actions: List.generate(list.length, (i) => KeyboardActionsItem( focusNode: list[i], toolbarButtons: [ @@ -57,21 +56,20 @@ class Utils { ); } - static String getCurrLocale() { - final String locale = SpUtil.getString(Constant.locale); + static String? getCurrLocale() { + final String locale = SpUtil.getString(Constant.locale)!; if (locale == '') { - return window.locale.languageCode; + return PlatformDispatcher.instance.locale.languageCode; } return locale; } } - -Future showElasticDialog({ - @required BuildContext context, +Future showElasticDialog({ + required BuildContext context, bool barrierDismissible = true, - WidgetBuilder builder, + required WidgetBuilder builder, }) { return showGeneralDialog( @@ -108,4 +106,9 @@ Widget _buildDialogTransitions(BuildContext context, Animation animation child: child, ), ); -} \ No newline at end of file +} + +/// String 空安全处理 +extension StringExtension on String? { + String get nullSafe => this ?? ''; +} diff --git a/lib/util/screen_utils.dart b/lib/util/screen_utils.dart index 79609cf58..9c355ff87 100644 --- a/lib/util/screen_utils.dart +++ b/lib/util/screen_utils.dart @@ -43,4 +43,4 @@ extension MediaQueryExtension on BuildContext { Size get size => Screen.size(this); double get height => Screen.size(this).height; double get width => Screen.size(this).width; -} \ No newline at end of file +} diff --git a/lib/util/theme_utils.dart b/lib/util/theme_utils.dart index e9687bdb0..b4028204b 100644 --- a/lib/util/theme_utils.dart +++ b/lib/util/theme_utils.dart @@ -1,10 +1,6 @@ -import 'dart:async'; -import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:rxdart/rxdart.dart'; import 'package:flutter_deer/res/resources.dart'; -import 'package:flutter_deer/util/device_utils.dart'; class ThemeUtils { @@ -12,11 +8,11 @@ class ThemeUtils { return Theme.of(context).brightness == Brightness.dark; } - static Color getDarkColor(BuildContext context, Color darkColor) { + static Color? getDarkColor(BuildContext context, Color darkColor) { return isDark(context) ? darkColor : null; } - static Color getIconColor(BuildContext context) { + static Color? getIconColor(BuildContext context) { return isDark(context) ? Colours.dark_text : null; } @@ -28,45 +24,29 @@ class ThemeUtils { return isDark(context) ? Colours.dark_bg_gray_ : Colours.bg_gray; } - static Color getKeyboardActionsColor(BuildContext context) { + static Color? getKeyboardActionsColor(BuildContext context) { return isDark(context) ? Colours.dark_bg_color : Colors.grey[200]; } - static StreamSubscription _subscription; - - /// 设置NavigationBar样式,使得导航栏颜色与深色模式的设置相符。 - static void setSystemNavigationBar(ThemeMode mode) { - /// 主题切换动画(AnimatedTheme)时间为200毫秒,延时设置导航栏颜色,这样过渡相对自然。 - _subscription?.cancel(); - _subscription = Stream.value(1).delay(const Duration(milliseconds: 200)).listen((_) { - bool _isDark = false; - if (mode == ThemeMode.dark || (mode == ThemeMode.system && window.platformBrightness == Brightness.dark)) { - _isDark = true; - } - setSystemBarStyle(isDark: _isDark); - }); - } - - /// 设置StatusBar、NavigationBar样式。(仅针对安卓) - /// 本项目在android MainActivity中已设置,不需要覆盖设置。 - static void setSystemBarStyle({bool isDark}) { - if (Device.isAndroid) { - - final bool _isDark = isDark ?? window.platformBrightness == Brightness.dark; - debugPrint('isDark: $_isDark'); - final SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle( - /// 透明状态栏 - statusBarColor: Colors.transparent, - systemNavigationBarColor: _isDark ? Colours.dark_bg_color : Colors.white, - systemNavigationBarIconBrightness: _isDark ? Brightness.light : Brightness.dark, - ); - SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle); - } - } + static const SystemUiOverlayStyle light = SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + systemNavigationBarColor: Colours.dark_bg_color, + systemNavigationBarIconBrightness: Brightness.light, + statusBarIconBrightness: Brightness.light, + statusBarBrightness: Brightness.dark, + ); + + static const SystemUiOverlayStyle dark = SystemUiOverlayStyle( + statusBarColor: Colors.transparent, + systemNavigationBarColor: Colors.white, + systemNavigationBarIconBrightness: Brightness.dark, + statusBarIconBrightness: Brightness.dark, + statusBarBrightness: Brightness.light, + ); } extension ThemeExtension on BuildContext { bool get isDark => ThemeUtils.isDark(this); Color get backgroundColor => Theme.of(this).scaffoldBackgroundColor; Color get dialogBackgroundColor => Theme.of(this).canvasColor; -} \ No newline at end of file +} diff --git a/lib/util/toast_utils.dart b/lib/util/toast_utils.dart index 2599470ed..e9272b9c4 100644 --- a/lib/util/toast_utils.dart +++ b/lib/util/toast_utils.dart @@ -3,7 +3,7 @@ import 'package:oktoast/oktoast.dart'; /// Toast工具类 class Toast { - static void show(String msg, {int duration = 2000}) { + static void show(String? msg, {int duration = 2000}) { if (msg == null) { return; } diff --git a/lib/util/version_utils.dart b/lib/util/version_utils.dart index 15600fb01..806cb22c3 100644 --- a/lib/util/version_utils.dart +++ b/lib/util/version_utils.dart @@ -13,4 +13,4 @@ class VersionUtils { static void jumpAppStore() { _kChannel.invokeMethod('jumpAppStore'); } -} \ No newline at end of file +} diff --git a/lib/widgets/base_dialog.dart b/lib/widgets/base_dialog.dart index 321ba0cc3..ff7317ea9 100644 --- a/lib/widgets/base_dialog.dart +++ b/lib/widgets/base_dialog.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/routers/fluro_navigator.dart'; @@ -9,15 +8,15 @@ import 'package:flutter_deer/widgets/my_button.dart'; class BaseDialog extends StatelessWidget { const BaseDialog({ - Key key, + super.key, this.title, this.onPressed, this.hiddenTitle = false, - @required this.child - }) : super(key : key); + required this.child + }); - final String title; - final VoidCallback onPressed; + final String? title; + final VoidCallback? onPressed; final Widget child; final bool hiddenTitle; @@ -29,7 +28,7 @@ class BaseDialog extends StatelessWidget { child: Padding( padding: const EdgeInsets.only(bottom: 8.0), child: Text( - hiddenTitle ? '' : title, + hiddenTitle ? '' : title ?? '', style: TextStyles.textBold18, ), ), @@ -107,15 +106,14 @@ class BaseDialog extends StatelessWidget { class _DialogButton extends StatelessWidget { const _DialogButton({ - Key key, - this.text, + required this.text, this.textColor, this.onPressed, - }): super(key: key); + }); final String text; - final Color textColor; - final VoidCallback onPressed; + final Color? textColor; + final VoidCallback? onPressed; @override Widget build(BuildContext context) { @@ -128,4 +126,4 @@ class _DialogButton extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/widgets/bezier_chart/bezier_chart_config.dart b/lib/widgets/bezier_chart/bezier_chart_config.dart index b0beea0e9..1f872c45a 100644 --- a/lib/widgets/bezier_chart/bezier_chart_config.dart +++ b/lib/widgets/bezier_chart/bezier_chart_config.dart @@ -46,7 +46,7 @@ class BezierChartConfig { final TextStyle bubbleIndicatorValueStyle; ///NumberFormat for the value displayed inside the bubble indicator - final NumberFormat bubbleIndicatorValueFormat; + final NumberFormat? bubbleIndicatorValueFormat; ///TextStyle for the label displayed inside the bubble indicator final TextStyle bubbleIndicatorLabelStyle; @@ -55,7 +55,7 @@ class BezierChartConfig { final Color backgroundColor; ///Gradient of the background of the chart - final LinearGradient backgroundGradient; + final LinearGradient? backgroundGradient; ///`true` if you want to display the value of the Y axis, [false] by default final bool displayYAxis; @@ -63,16 +63,16 @@ class BezierChartConfig { ///If [displayYAxis] is true, then you can set a positive value to display the steps of Y axis values ///e.g 1: stepsYAxis : 5 , if your maxValue is 100, then the Y values should be: [0,5,10,15 .... 100] ///e.g 2: stepsYAxis : 10 , if your maxValue is 100, then the Y values should be: [10,20,30,40 .... 100] - final int stepsYAxis; + final int? stepsYAxis; ///`true` if you want to start the values of Y axis from the minimum value of your Y values. final bool startYAxisFromNonZeroValue; ///TextStyle of the text of the Y Axis values - final TextStyle yAxisTextStyle; + final TextStyle? yAxisTextStyle; ///TextStyle of the text of the X Axis values - final TextStyle xAxisTextStyle; + final TextStyle? xAxisTextStyle; ///Height of the footer final double footerHeight; @@ -88,7 +88,7 @@ class BezierChartConfig { final bool pinchZoom; ///If the `contentWidth` is upper than the current width then the content will be scrollable (only valid for `bezierChartScale` = `CUSTOM`) - final double contentWidth; + final double? contentWidth; ///`true` if you want to display a vertical line on each X data point, it only works when there is one `BezierLine`. final bool displayLinesXAxis; @@ -144,7 +144,7 @@ class BezierChartConfig { this.bubbleIndicatorValueFormat, this.physics = const AlwaysScrollableScrollPhysics(), this.updatePositionOnTap = false, - bool verticalLineFullHeight, + bool? verticalLineFullHeight, }) : this.verticalLineFullHeight = verticalLineFullHeight ?? verticalIndicatorFixedPosition; } \ No newline at end of file diff --git a/lib/widgets/bezier_chart/bezier_chart_widget.dart b/lib/widgets/bezier_chart/bezier_chart_widget.dart index a4dc304f2..e76ffb59d 100644 --- a/lib/widgets/bezier_chart/bezier_chart_widget.dart +++ b/lib/widgets/bezier_chart/bezier_chart_widget.dart @@ -30,58 +30,58 @@ class BezierChart extends StatefulWidget { ///[Optional] This callback only works if the `BezierChartScale` is `BezierChartScale.CUSTOM` otherwise it will be ignored ///This is used to display a custom footer value based on the current 'x' value - final FooterValueBuilder footerValueBuilder; + final FooterValueBuilder? footerValueBuilder; ///[Optional] This callback only works if the `BezierChartScale` is `BezierChartScale.CUSTOM` otherwise it will be ignored ///This is used to display a custom bubble label value based on the current 'x' value - final FooterValueBuilder bubbleLabelValueBuilder; + final FooterValueBuilder? bubbleLabelValueBuilder; ///[Optional] This callback only works if the `BezierChartScale` is Date type otherwise it will be ignored ///This is used to display a custom footer value based on the current 'x' value - final FooterDateTimeBuilder footerDateTimeBuilder; + final FooterDateTimeBuilder? footerDateTimeBuilder; ///[Optional] This callback only works if the `BezierChartScale` is Date type otherwise it will be ignored ///This is used to display a custom bubble label value based on the current 'x' value - final FooterDateTimeBuilder bubbleLabelDateTimeBuilder; + final FooterDateTimeBuilder? bubbleLabelDateTimeBuilder; ///[Optional] This callback notify when the display indicator is visible or not - final ValueChanged onIndicatorVisible; + final ValueChanged? onIndicatorVisible; ///[Optional] This callback will display the current `double` value selected by the indicator ///Only works when the `BezierChartScale` is not `BezierChartScale.CUSTOM` - final ValueChanged onValueSelected; + final ValueChanged? onValueSelected; ///[Optional] This callback will display the current `DateTime` selected by the indicator ///Only works when the `BezierChartScale` is date type - final ValueChanged onDateTimeSelected; + final ValueChanged? onDateTimeSelected; ///This value is required only if the `BezierChartScale` is not `BezierChartScale.CUSTOM` - final DateTime fromDate; + final DateTime? fromDate; ///This value is required only if the `BezierChartScale` is not `BezierChartScale.CUSTOM` - final DateTime toDate; + final DateTime? toDate; ///This value represents the date selected to display the info in the Chart ///For `BezierChartScale.HOURLY` it will use year, month, day and hour ///For `BezierChartScale.WEEKLY` it will use year, month and day ///For `BezierChartScale.MONTHLY` it will use year, month ///For `BezierChartScale.YEARLY` it will use year - final DateTime selectedDate; + final DateTime? selectedDate; ///This value represents the value selected to display the info in the Chart ///It's only for `BezierChartScale.CUSTOM` - final double selectedValue; + final double? selectedValue; ///Beziers used in the Axis Y final List series; ///Notify if the `BezierChartScale` changed, it only works with date scales. - final ValueChanged onScaleChanged; + final ValueChanged? onScaleChanged; BezierChart({ - Key key, - this.config, - this.xAxisCustomValues, + Key? key, + required this.config, + required this.xAxisCustomValues, this.footerValueBuilder, this.bubbleLabelValueBuilder, this.footerDateTimeBuilder, @@ -94,13 +94,11 @@ class BezierChart extends StatefulWidget { this.onValueSelected, this.selectedValue, this.bezierChartAggregation = BezierChartAggregation.SUM, - @required this.bezierChartScale, - @required this.series, + required this.bezierChartScale, + required this.series, this.onScaleChanged, }) : assert( - (bezierChartScale == BezierChartScale.CUSTOM && - xAxisCustomValues != null && - series != null) || + (bezierChartScale == BezierChartScale.CUSTOM) || bezierChartScale != BezierChartScale.CUSTOM, 'The xAxisCustomValues and series must not be null', ), @@ -134,7 +132,7 @@ class BezierChart extends StatefulWidget { ), assert( (((bezierChartScale != BezierChartScale.CUSTOM) && - toDate.isAfter(fromDate)) || + toDate!.isAfter(fromDate!)) || (bezierChartScale == BezierChartScale.CUSTOM && fromDate == null && toDate == null)), @@ -149,12 +147,12 @@ class BezierChart extends StatefulWidget { @visibleForTesting class BezierChartState extends State with SingleTickerProviderStateMixin { - AnimationController _animationController; - ScrollController _scrollController; + late AnimationController _animationController; + late ScrollController _scrollController; GlobalKey _keyScroll = GlobalKey(); ///Track the current position when dragging the indicator - Offset _verticalIndicatorPosition; + Offset? _verticalIndicatorPosition; bool _displayIndicator = false; ///padding for leading and trailing of the chart @@ -174,10 +172,10 @@ class BezierChartState extends State double _currentScale = 1.0; ///This value allow us to get the last scale used when start the pinch/zoom again - double _previousScale; + late double _previousScale; ///The current chart scale - BezierChartScale _currentBezierChartScale; + late BezierChartScale _currentBezierChartScale; double _lastValueSnapped = double.infinity; bool get isPinchZoomActive => (_touchFingers > 1 && widget.config.pinchZoom); @@ -189,13 +187,13 @@ class BezierChartState extends State bool _isScrollable = false; ///Calculate all of the values related to the Y axis - List _yValues; + late List _yValues; ///Values from valueBuilder - List _tempYValues; + late List _tempYValues; - DateTime _dateTimeSelected; - double _valueSelected; + DateTime? _dateTimeSelected; + double? _valueSelected; GlobalKey _keyLastYAxisItem = GlobalKey(); double _yAxisWidth = 0.0; @@ -209,9 +207,8 @@ class BezierChartState extends State ///Update and refresh the position based on the current screen void _updatePosition(Offset globalPosition) { - RenderBox renderBox = context.findRenderObject(); + final RenderBox renderBox = context.findRenderObject() as RenderBox; final position = renderBox.globalToLocal(globalPosition); - if (position == null) return; return setState( () { final fixedPosition = Offset( @@ -231,7 +228,7 @@ class BezierChartState extends State from: 0.0, ); if (widget.onIndicatorVisible != null) { - widget.onIndicatorVisible(true); + widget.onIndicatorVisible!(true); } } _onDataPointSnap(double.maxFinite); @@ -242,7 +239,7 @@ class BezierChartState extends State _onHideIndicator() { if (_displayIndicator) { if (widget.onIndicatorVisible != null) { - widget.onIndicatorVisible(false); + widget.onIndicatorVisible!(false); } _animationController.reverse(from: 1.0).whenCompleteOrCancel( () { @@ -273,7 +270,7 @@ class BezierChartState extends State _checkMissingValues(DateTime newDate) { for (BezierLine line in widget.series) { if (line.onMissingValue != null) { - final newValue = line.onMissingValue(newDate); + final newValue = line.onMissingValue!(newDate); if (!_tempYValues.contains(newValue)) _tempYValues.add(newValue); //if there is no missingvalue specified we should use 0 as minimum value to avoid overlap } else if (widget.config.startYAxisFromNonZeroValue && @@ -293,9 +290,9 @@ class BezierChartState extends State .map((val) => DataPoint(value: val, xAxis: val)) .toList(); } else if (scale == BezierChartScale.HOURLY) { - final hours = widget.toDate.difference(widget.fromDate).inHours; + final hours = widget.toDate!.difference(widget.fromDate!).inHours; for (int i = 0; i < hours; i++) { - final tempDate = widget.fromDate.add( + final tempDate = widget.fromDate!.add( Duration( hours: (i + 1), ), @@ -308,11 +305,11 @@ class BezierChartState extends State _checkMissingValues(newDate); } } else if (scale == BezierChartScale.WEEKLY) { - final days = _convertToDateOnly(widget.toDate) - .difference(_convertToDateOnly(widget.fromDate)) + final days = _convertToDateOnly(widget.toDate!) + .difference(_convertToDateOnly(widget.fromDate!)) .inDays; for (int i = 0; i <= days; i++) { - final newDate = widget.fromDate.add( + final newDate = widget.fromDate!.add( Duration( days: (i), ), @@ -324,12 +321,12 @@ class BezierChartState extends State } } else if (scale == BezierChartScale.MONTHLY) { DateTime startDate = DateTime( - widget.fromDate.year, - widget.fromDate.month, + widget.fromDate!.year, + widget.fromDate!.month, ); DateTime endDate = DateTime( - widget.toDate.year, - widget.toDate.month, + widget.toDate!.year, + widget.toDate!.month, ); for (int i = 0; (startDate.isBefore(endDate) || areEqualDates(startDate, endDate)); @@ -342,10 +339,10 @@ class BezierChartState extends State } } else if (scale == BezierChartScale.YEARLY) { DateTime startDate = DateTime( - widget.fromDate.year, + widget.fromDate!.year, ); DateTime endDate = DateTime( - widget.toDate.year, + widget.toDate!.year, ); for (int i = 0; (startDate.isBefore(endDate) || areEqualDates(startDate, endDate)); @@ -404,7 +401,7 @@ class BezierChartState extends State ///When the widget finish rendering for the first time _onLayoutDone(_) { - _yAxisWidth = _keyLastYAxisItem.currentContext?.size?.width; + _yAxisWidth = _keyLastYAxisItem.currentContext?.size?.width ?? 0; //Move to selected position if ((widget.selectedDate != null && _currentBezierChartScale != BezierChartScale.CUSTOM) || @@ -413,20 +410,20 @@ class BezierChartState extends State int index = -1; if (_currentBezierChartScale == BezierChartScale.WEEKLY) { index = _xAxisDataPoints.indexWhere( - (dp) => areEqualDates((dp.xAxis as DateTime), widget.selectedDate)); + (dp) => areEqualDates((dp.xAxis as DateTime), widget.selectedDate!)); } else if (_currentBezierChartScale == BezierChartScale.HOURLY) { index = _xAxisDataPoints.indexWhere((dp) => - (dp.xAxis as DateTime).year == widget.selectedDate.year && - (dp.xAxis as DateTime).month == widget.selectedDate.month && - (dp.xAxis as DateTime).day == widget.selectedDate.day && - (dp.xAxis as DateTime).hour == widget.selectedDate.hour); + (dp.xAxis as DateTime).year == widget.selectedDate!.year && + (dp.xAxis as DateTime).month == widget.selectedDate!.month && + (dp.xAxis as DateTime).day == widget.selectedDate!.day && + (dp.xAxis as DateTime).hour == widget.selectedDate!.hour); } else if (_currentBezierChartScale == BezierChartScale.MONTHLY) { index = _xAxisDataPoints.indexWhere((dp) => - (dp.xAxis as DateTime).year == widget.selectedDate.year && - (dp.xAxis as DateTime).month == widget.selectedDate.month); + (dp.xAxis as DateTime).year == widget.selectedDate!.year && + (dp.xAxis as DateTime).month == widget.selectedDate!.month); } else if (_currentBezierChartScale == BezierChartScale.YEARLY) { index = _xAxisDataPoints.indexWhere( - (dp) => (dp.xAxis as DateTime).year == widget.selectedDate.year); + (dp) => (dp.xAxis as DateTime).year == widget.selectedDate!.year); } else if (_currentBezierChartScale == BezierChartScale.CUSTOM) { index = _xAxisDataPoints .indexWhere((dp) => (dp.xAxis as double) == widget.selectedValue); @@ -455,7 +452,7 @@ class BezierChartState extends State } else { final jumpToX = (index * horizontalSpacing) - horizontalPadding / 2 - - _keyScroll.currentContext.size.width / 2; + _keyScroll.currentContext!.size!.width / 2; _scrollController.jumpTo(jumpToX); fixedPosition = Offset( @@ -482,7 +479,7 @@ class BezierChartState extends State _checkIfNeedScroll() { if (_contentWidth > - _keyScroll.currentContext.size.width - horizontalPadding * 2) { + _keyScroll.currentContext!.size!.width - horizontalPadding * 2) { _isScrollable = true; } } @@ -499,7 +496,7 @@ class BezierChartState extends State _currentBezierChartScale == BezierChartScale.HOURLY) { for (BezierLine line in widget.series) { Map> tmpMap = Map(); - for (DataPoint dataPoint in line.data) { + for (DataPoint dataPoint in line.data) { String key; if (_currentBezierChartScale == BezierChartScale.MONTHLY) { key = @@ -516,9 +513,9 @@ class BezierChartState extends State //support aggregations for y axis if (!tmpMap.containsKey(key)) { - tmpMap[key] = new List(); + tmpMap[key] = []; } - tmpMap[key].add(dataPoint.value); + tmpMap[key]?.add(dataPoint.value); } Map valueMap = Map(); @@ -552,7 +549,7 @@ class BezierChartState extends State valueMap.keys.forEach( (key) { final value = valueMap[key]; - if (!_yValues.contains(value)) _yValues.add(value); + if (!_yValues.contains(value)) _yValues.add(value!); ///Sum all the values corresponding to each month and create a new data serie if (_currentBezierChartScale == BezierChartScale.HOURLY) { @@ -564,7 +561,7 @@ class BezierChartState extends State final date = DateTime(year, month, day, hour, 0); newDataPoints.add( DataPoint( - value: value, + value: value!, xAxis: date, ), ); @@ -578,7 +575,7 @@ class BezierChartState extends State final date = DateTime(year, month); newDataPoints.add( DataPoint( - value: value, + value: value!, xAxis: date, ), ); @@ -590,7 +587,7 @@ class BezierChartState extends State final date = DateTime(year, month, day, 0); newDataPoints.add( DataPoint( - value: value, + value: value!, xAxis: date, ), ); @@ -600,7 +597,7 @@ class BezierChartState extends State final date = DateTime(year); newDataPoints.add( DataPoint( - value: value, + value: value!, xAxis: date, ), ); @@ -710,7 +707,7 @@ class BezierChartState extends State void _notifyScaleChanged(BezierChartScale lastScale) { if (widget.onScaleChanged != null && lastScale != _currentBezierChartScale) { - widget.onScaleChanged(_currentBezierChartScale); + widget.onScaleChanged!(_currentBezierChartScale); } } @@ -893,11 +890,11 @@ class BezierChartState extends State if (widget.onValueSelected != null) { if (_valueSelected == null) { _valueSelected = val; - widget.onValueSelected(_valueSelected); + widget.onValueSelected!(_valueSelected!); } else { if (_valueSelected != val) { _valueSelected = val; - widget.onValueSelected(_valueSelected); + widget.onValueSelected!(_valueSelected!); } } } @@ -906,11 +903,11 @@ class BezierChartState extends State if (widget.onDateTimeSelected != null) { if (_dateTimeSelected == null) { _dateTimeSelected = val; - widget.onDateTimeSelected(_dateTimeSelected); + widget.onDateTimeSelected!(_dateTimeSelected!); } else { if (_dateTimeSelected != val) { _dateTimeSelected = val; - widget.onDateTimeSelected(_dateTimeSelected); + widget.onDateTimeSelected!(_dateTimeSelected!); } } } @@ -921,7 +918,7 @@ class BezierChartState extends State ), ); if (widget.config.displayYAxis) { - if (_yValues != null && _yValues.isNotEmpty) { + if (_yValues.isNotEmpty) { //add a background container for the Y Axis items.add(Positioned( left: 0, @@ -946,10 +943,10 @@ class BezierChartState extends State ? _yValues.first : 0.0); final steps = widget.config.stepsYAxis != null && - widget.config.stepsYAxis > 0 + widget.config.stepsYAxis! > 0 ? widget.config.stepsYAxis : null; - _addYItem(double value, {Key key}) { + _addYItem(double value, {Key? key}) { items.add( Positioned( bottom: _getRealValue( @@ -1011,50 +1008,50 @@ _getRealValue(double value, double maxConstraint, double maxValue) => //BezierChart class _BezierChartPainter extends CustomPainter { final BezierChartConfig config; - final Offset verticalIndicatorPosition; + final Offset? verticalIndicatorPosition; final List series; final List xAxisDataPoints; double _maxValueY = 0.0; double _maxValueX = 0.0; List<_CustomValue> _currentCustomValues = []; - DataPoint _currentXDataPoint; + DataPoint? _currentXDataPoint; final double radiusDotIndicatorMain = 7; final double radiusDotIndicatorItems = 3.5; final bool showIndicator; final Animation animation; - final ValueChanged onDataPointSnap; + final ValueChanged? onDataPointSnap; final BezierChartScale bezierChartScale; final double maxWidth; final double scrollOffset; bool footerDrawed = false; - final FooterValueBuilder footerValueBuilder; - final FooterValueBuilder bubbleLabelValueBuilder; - final FooterDateTimeBuilder footerDateTimeBuilder; - final FooterDateTimeBuilder bubbleLabelDateTimeBuilder; + final FooterValueBuilder? footerValueBuilder; + final FooterValueBuilder? bubbleLabelValueBuilder; + final FooterDateTimeBuilder? footerDateTimeBuilder; + final FooterDateTimeBuilder? bubbleLabelDateTimeBuilder; final double maxYValue; final double minYValue; - final ValueChanged onValueSelected; - final ValueChanged onDateTimeSelected; + final ValueChanged? onValueSelected; + final ValueChanged? onDateTimeSelected; final bool shouldRepaintChart; _BezierChartPainter({ - this.shouldRepaintChart, - this.config, + required this.shouldRepaintChart, + required this.config, this.verticalIndicatorPosition, - this.series, - this.showIndicator, - this.xAxisDataPoints, - this.animation, - this.bezierChartScale, + required this.series, + required this.showIndicator, + required this.xAxisDataPoints, + required this.animation, + required this.bezierChartScale, this.onDataPointSnap, - this.maxWidth, + required this.maxWidth, this.footerValueBuilder, this.bubbleLabelValueBuilder, - this.scrollOffset, + required this.scrollOffset, this.footerDateTimeBuilder, this.bubbleLabelDateTimeBuilder, - this.maxYValue, - this.minYValue, + required this.maxYValue, + required this.minYValue, this.onDateTimeSelected, this.onValueSelected, }) : super(repaint: animation) { @@ -1103,16 +1100,16 @@ class _BezierChartPainter extends CustomPainter { double verticalX = 0.0; //fixing verticalIndicator outbounds if (verticalIndicatorPosition != null) { - verticalX = verticalIndicatorPosition.dx; - if (verticalIndicatorPosition.dx < 0) { + verticalX = verticalIndicatorPosition!.dx; + if (verticalIndicatorPosition!.dx < 0) { verticalX = 0.0; - } else if (verticalIndicatorPosition.dx > size.width) { + } else if (verticalIndicatorPosition!.dx > size.width) { verticalX = size.width; } } //variables for the last item on the list (this is required to display the indicator) - Offset p0, p1, p2, p3; + late Offset p0, p1, p2, p3; void _drawBezierLinePath(BezierLine line) { Path path = Path(); List dataPoints = []; @@ -1140,7 +1137,7 @@ class _BezierChartPainter extends CustomPainter { ..strokeWidth = 1.0 ..style = PaintingStyle.stroke; - _AxisValue lastPoint; + _AxisValue? lastPoint; //display each data point for (int i = 0; i < xAxisDataPoints.length; i++) { @@ -1164,7 +1161,7 @@ class _BezierChartPainter extends CustomPainter { value = line.data[i].value; } else { //search from axis - for (DataPoint dp in line.data) { + for (DataPoint dp in line.data) { final dateTime = (xAxisDataPoints[i].xAxis as DateTime); if (bezierChartScale == BezierChartScale.HOURLY) { @@ -1185,7 +1182,7 @@ class _BezierChartPainter extends CustomPainter { if (value == 0) { if (line.onMissingValue != null) { isMissingValue = true; - value = line.onMissingValue(xAxisDataPoints[i].xAxis as DateTime); + value = line.onMissingValue!(xAxisDataPoints[i].xAxis as DateTime); } } } @@ -1256,15 +1253,15 @@ class _BezierChartPainter extends CustomPainter { bool isDouble = (xAxisDataPoints[i].xAxis is double); if (isDouble) { if (onValueSelected != null) { - onValueSelected(xAxisDataPoints[i].xAxis); + onValueSelected!(xAxisDataPoints[i].xAxis); } } else { if (onDateTimeSelected != null) { - onDateTimeSelected(xAxisDataPoints[i].xAxis); + onDateTimeSelected!(xAxisDataPoints[i].xAxis); } } - onDataPointSnap(xAxisDataPoints[i].value); + onDataPointSnap!(xAxisDataPoints[i].value); _currentCustomValues.add( _CustomValue( value: '${formatAsIntOrDouble(axisY)}', @@ -1327,7 +1324,7 @@ class _BezierChartPainter extends CustomPainter { if (config.snap) { if (_currentXDataPoint != null) { verticalX = _getRealValue( - _currentXDataPoint.value, + _currentXDataPoint!.value, size.width, _maxValueX, ); @@ -1336,187 +1333,185 @@ class _BezierChartPainter extends CustomPainter { } } - if (p0 != null) { - final yValue = _getYValues( - p0, - p1, - p2, - p3, - (verticalX - p0.dx) / (p3.dx - p0.dx), - ); - - double infoWidth = 0; //base value, modified based on the label text - double infoHeight = 30; - - //bubble indicator padding - final horizontalPadding = 28.0; - //todo 42 - double offsetInfo = 37 + ((_currentCustomValues.length - 1.0) * 10.0); - final centerForCircle = Offset(verticalX, height - yValue); - final center = config.verticalIndicatorFixedPosition - ? Offset(verticalX, offsetInfo) - : centerForCircle; - - if (config.showVerticalIndicator) { - canvas.drawLine( - Offset(verticalX, height), - Offset(verticalX, config.verticalLineFullHeight ? 0.0 : center.dy), - paintVerticalIndicator, - ); - } + final yValue = _getYValues( + p0, + p1, + p2, + p3, + (verticalX - p0.dx) / (p3.dx - p0.dx), + ); - //draw point - canvas.drawCircle( - centerForCircle, - radiusDotIndicatorMain, - Paint() - ..color = series.reversed.toList().last.dataPointFillColor - ..strokeWidth = 4.0, + double infoWidth = 0; //base value, modified based on the label text + double infoHeight = 30; + + //bubble indicator padding + // TODO 28 + final horizontalPadding = 18.0; + // TODO 42 + double offsetInfo = 37 + ((_currentCustomValues.length - 1.0) * 10.0); + final centerForCircle = Offset(verticalX, height - yValue); + final center = config.verticalIndicatorFixedPosition + ? Offset(verticalX, offsetInfo) + : centerForCircle; + + if (config.showVerticalIndicator) { + canvas.drawLine( + Offset(verticalX, height), + Offset(verticalX, config.verticalLineFullHeight ? 0.0 : center.dy), + paintVerticalIndicator, ); + } - //calculate the total lenght of the lines - List textValues = []; - List centerCircles = []; - // TODO(weilu): 修改处 infoHeight / (8.75) - double space = - 10 - ((infoHeight / (4)) * _currentCustomValues.length); - infoHeight = - infoHeight + (_currentCustomValues.length - 1) * (infoHeight / 3); - - for (_CustomValue customValue - in _currentCustomValues.reversed.toList()) { - textValues.add( - TextSpan( - text: '${customValue.value} ', - style: config.bubbleIndicatorValueStyle.copyWith(fontSize: 11), - children: [ - TextSpan( - text: '${customValue.label}\n', - style: config.bubbleIndicatorLabelStyle.copyWith(fontSize: 9), - ), - ], - ), - ); - centerCircles.add( - // Offset(center.dx - infoWidth / 2 + radiusDotIndicatorItems * 1.5, - Offset( - center.dx, - center.dy - - offsetInfo - - radiusDotIndicatorItems + - space + - (_currentCustomValues.length == 1 ? 1 : 0)), - ); - space += 12.5; - } + //draw point + canvas.drawCircle( + centerForCircle, + radiusDotIndicatorMain, + Paint() + ..color = series.reversed.toList().last.dataPointFillColor + ..strokeWidth = 4.0, + ); - //Calculate Text size - TextPainter textPainter = TextPainter( - textAlign: TextAlign.center, - text: TextSpan( - text: _getInfoTitleText(), - // TODO(weilu): 修改处 9.5 - style: config.bubbleIndicatorTitleStyle.copyWith(fontSize: 5.0), - children: textValues, + //calculate the total lenght of the lines + List textValues = []; + List centerCircles = []; + // TODO(weilu): 修改处 infoHeight / (8.75) + double space = + 10 - ((infoHeight / (4)) * _currentCustomValues.length); + infoHeight = + infoHeight + (_currentCustomValues.length - 1) * (infoHeight / 3); + + for (_CustomValue customValue in _currentCustomValues.reversed.toList()) { + textValues.add( + TextSpan( + text: '${customValue.value} ', + style: config.bubbleIndicatorValueStyle.copyWith(fontSize: 11), + children: [ + TextSpan( + text: '${customValue.label}\n', + style: config.bubbleIndicatorLabelStyle.copyWith(fontSize: 9), + ), + ], ), - textDirection: TextDirection.ltr, ); - textPainter.layout(); - - infoWidth = - textPainter.width + radiusDotIndicatorItems * 2 + horizontalPadding; - - ///Draw Bubble Indicator Info - /// Draw shadow bubble info - if (animation.isCompleted) { - Path path = Path(); - path.moveTo(center.dx - infoWidth / 2 + 4, - center.dy - offsetInfo + infoHeight / 1.8); - path.lineTo(center.dx + infoWidth / 2 + 4, - center.dy - offsetInfo + infoHeight / 1.8); - path.lineTo(center.dx + infoWidth / 2 + 4, - center.dy - offsetInfo - infoHeight / 3); - //path.close(); - // canvas.drawShadow(path, Colors.black, 20.0, false); - canvas.drawPath(path, paintControlPoints..color = Colors.black12); - } + centerCircles.add( + // Offset(center.dx - infoWidth / 2 + radiusDotIndicatorItems * 1.5, + Offset( + center.dx, + center.dy - + offsetInfo - + radiusDotIndicatorItems + + space + + (_currentCustomValues.length == 1 ? 1 : 0)), + ); + space += 12.5; + } - final paintInfo = Paint() - ..color = config.bubbleIndicatorColor - ..style = PaintingStyle.fill; - - //Draw Bubble info - canvas.drawRRect( - RRect.fromRectAndRadius( - _fromCenter( - center: Offset( - center.dx, - (center.dy - offsetInfo * animation.value), - ), - width: infoWidth, - height: infoHeight, + //Calculate Text size + TextPainter textPainter = TextPainter( + textAlign: TextAlign.center, + text: TextSpan( + text: _getInfoTitleText(), + // TODO(weilu): 修改处 9.5 + style: config.bubbleIndicatorTitleStyle.copyWith(fontSize: 5.0), + children: textValues, + ), + textDirection: TextDirection.ltr, + ); + textPainter.layout(); + + infoWidth = + textPainter.width + radiusDotIndicatorItems * 2 + horizontalPadding; + + ///Draw Bubble Indicator Info + /// Draw shadow bubble info + if (animation.isCompleted) { + Path path = Path(); + path.moveTo(center.dx - infoWidth / 2 + 4, + center.dy - offsetInfo + infoHeight / 1.8); + path.lineTo(center.dx + infoWidth / 2 + 4, + center.dy - offsetInfo + infoHeight / 1.8); + path.lineTo(center.dx + infoWidth / 2 + 4, + center.dy - offsetInfo - infoHeight / 3); + //path.close(); + // canvas.drawShadow(path, Colors.black, 20.0, false); + canvas.drawPath(path, paintControlPoints..color = Colors.black12); + } + + final paintInfo = Paint() + ..color = config.bubbleIndicatorColor + ..style = PaintingStyle.fill; + + //Draw Bubble info + canvas.drawRRect( + RRect.fromRectAndRadius( + _fromCenter( + center: Offset( + center.dx, + (center.dy - offsetInfo * animation.value), ), - Radius.circular(5), + width: infoWidth, + height: infoHeight, ), - paintInfo, - ); + Radius.circular(5), + ), + paintInfo, + ); - //Draw triangle Bubble - final double triangleSize = 6; - - Path pathArrow = Path(); - - pathArrow.moveTo(center.dx - triangleSize, - center.dy - offsetInfo * animation.value + infoHeight / 2.1); - pathArrow.lineTo( - center.dx, - center.dy - - offsetInfo * animation.value + - infoHeight / 2.1 + - triangleSize * 1.5); - pathArrow.lineTo(center.dx + triangleSize, - center.dy - offsetInfo * animation.value + infoHeight / 2.1); - pathArrow.close(); - canvas.drawPath( - pathArrow, - paintInfo, + //Draw triangle Bubble + final double triangleSize = 6; + + Path pathArrow = Path(); + + pathArrow.moveTo(center.dx - triangleSize, + center.dy - offsetInfo * animation.value + infoHeight / 2.1); + pathArrow.lineTo( + center.dx, + center.dy - + offsetInfo * animation.value + + infoHeight / 2.1 + + triangleSize * 1.5); + pathArrow.lineTo(center.dx + triangleSize, + center.dy - offsetInfo * animation.value + infoHeight / 2.1); + pathArrow.close(); + canvas.drawPath( + pathArrow, + paintInfo, + ); + //End triangle + + if (animation.isCompleted) { + //Paint Text , title and description + textPainter.paint( + canvas, + Offset( + center.dx - textPainter.width / 2 + 6, // TODO 0 + center.dy - offsetInfo - infoHeight / 2.5, + ), ); - //End triangle - - if (animation.isCompleted) { - //Paint Text , title and description - textPainter.paint( - canvas, - Offset( - center.dx - textPainter.width / 2, - center.dy - offsetInfo - infoHeight / 2.5, - ), - ); - //draw circle indicators and text - for (int z = 0; z < _currentCustomValues.length; z++) { - _CustomValue customValue = _currentCustomValues[z]; - Offset centerIndicator = centerCircles.reversed.toList()[z]; - Offset fixedCenter = Offset( - centerIndicator.dx - - infoWidth / 2 + - radiusDotIndicatorItems + - 4, - centerIndicator.dy); - canvas.drawCircle( - fixedCenter, - radiusDotIndicatorItems, - Paint() - ..color = customValue.color - ..style = PaintingStyle.fill); - canvas.drawCircle( - fixedCenter, - radiusDotIndicatorItems, - Paint() - ..color = Colors.black - ..strokeWidth = 0.5 - ..style = PaintingStyle.stroke); - } + //draw circle indicators and text + for (int z = 0; z < _currentCustomValues.length; z++) { + _CustomValue customValue = _currentCustomValues[z]; + Offset centerIndicator = centerCircles.reversed.toList()[z]; + Offset fixedCenter = Offset( + centerIndicator.dx - + infoWidth / 2 + + radiusDotIndicatorItems + + 6, // TODO 4 + centerIndicator.dy); + canvas.drawCircle( + fixedCenter, + radiusDotIndicatorItems, + Paint() + ..color = customValue.color + ..style = PaintingStyle.fill); + canvas.drawCircle( + fixedCenter, + radiusDotIndicatorItems, + Paint() + ..color = Colors.black + ..strokeWidth = 0.5 + ..style = PaintingStyle.stroke); } } } @@ -1525,50 +1520,50 @@ class _BezierChartPainter extends CustomPainter { String _getInfoTitleText() { final scale = bezierChartScale; if (bubbleLabelValueBuilder != null && scale == BezierChartScale.CUSTOM) { - return bubbleLabelValueBuilder(_currentXDataPoint.value); + return bubbleLabelValueBuilder!(_currentXDataPoint!.value); } if (bubbleLabelDateTimeBuilder != null && scale != BezierChartScale.CUSTOM) { - return bubbleLabelDateTimeBuilder( - _currentXDataPoint.xAxis as DateTime, scale); + return bubbleLabelDateTimeBuilder!( + _currentXDataPoint!.xAxis as DateTime, scale); } if (scale == BezierChartScale.CUSTOM) { - return "${formatAsIntOrDouble(_currentXDataPoint.value)}\n"; + return "${formatAsIntOrDouble(_currentXDataPoint!.value)}\n"; } else if (scale == BezierChartScale.HOURLY) { final dateFormat = intl.DateFormat('dd/MM HH:mm'); - final date = _currentXDataPoint.xAxis as DateTime; + final date = _currentXDataPoint!.xAxis as DateTime; final now = DateTime.now(); if (areEqualDatesIncludingHour(date, now)) { return 'Now\n'; } else { - return '${dateFormat.format(_currentXDataPoint.xAxis)}\n'; + return '${dateFormat.format(_currentXDataPoint!.xAxis)}\n'; } } else if (scale == BezierChartScale.WEEKLY) { final dateFormat = intl.DateFormat('EEE d'); - final date = _currentXDataPoint.xAxis as DateTime; + final date = _currentXDataPoint!.xAxis as DateTime; final now = DateTime.now(); if (areEqualDates(date, now)) { return 'Current\n'; } else { - return '${dateFormat.format(_currentXDataPoint.xAxis)}\n'; + return '${dateFormat.format(_currentXDataPoint!.xAxis)}\n'; } } else if (scale == BezierChartScale.MONTHLY) { final dateFormat = intl.DateFormat('MMM y'); - final date = _currentXDataPoint.xAxis as DateTime; + final date = _currentXDataPoint!.xAxis as DateTime; final now = DateTime.now(); if (date.year == now.year && now.month == date.month) { return 'Current Month\n'; } else { - return '${dateFormat.format(_currentXDataPoint.xAxis)}\n'; + return '${dateFormat.format(_currentXDataPoint!.xAxis)}\n'; } } else if (scale == BezierChartScale.YEARLY) { final dateFormat = intl.DateFormat('y'); - final date = _currentXDataPoint.xAxis as DateTime; + final date = _currentXDataPoint!.xAxis as DateTime; final now = DateTime.now(); if (date.year == now.year) { return 'Current Year\n'; } else { - return '${dateFormat.format(_currentXDataPoint.xAxis)}\n'; + return '${dateFormat.format(_currentXDataPoint!.xAxis)}\n'; } } return ''; @@ -1577,10 +1572,10 @@ class _BezierChartPainter extends CustomPainter { String _getFooterText(DataPoint dataPoint) { final scale = bezierChartScale; if (footerValueBuilder != null && scale == BezierChartScale.CUSTOM) { - return footerValueBuilder(dataPoint.value); + return footerValueBuilder!(dataPoint.value); } if (footerDateTimeBuilder != null && scale != BezierChartScale.CUSTOM) { - return footerDateTimeBuilder(dataPoint.xAxis as DateTime, scale); + return footerDateTimeBuilder!(dataPoint.xAxis as DateTime, scale); } if (scale == BezierChartScale.CUSTOM) { return '${formatAsIntOrDouble(dataPoint.value)}\n'; @@ -1628,7 +1623,7 @@ class _BezierChartPainter extends CustomPainter { return y; } - Rect _fromCenter({Offset center, double width, double height}) => + Rect _fromCenter({required Offset center, required double width, required double height}) => Rect.fromLTRB( center.dx - width / 2, center.dy - height / 2, @@ -1643,15 +1638,15 @@ class _BezierChartPainter extends CustomPainter { // oldDelegate.scrollOffset != scrollOffset || // oldDelegate.showIndicator != showIndicator; // TODO(weilu): 修改处 - oldDelegate.series != series; + oldDelegate.series != series || oldDelegate.verticalIndicatorPosition != verticalIndicatorPosition; } class _AxisValue { final double x; final double y; const _AxisValue({ - this.x, - this.y, + required this.x, + required this.y, }); } @@ -1664,7 +1659,7 @@ bool _compareLengths(int currentValue, List val2) { return true; } -bool _isSorted(List list, [int Function(double, double) compare]) { +bool _isSorted(List list, [int Function(double, double)? compare]) { if (list.length < 2) return true; compare ??= (double a, double b) => a.compareTo(b); double prev = list.first; @@ -1710,9 +1705,9 @@ class _CustomValue { final Color color; _CustomValue({ - @required this.value, - @required this.label, - @required this.color, + required this.value, + required this.label, + required this.color, }); } diff --git a/lib/widgets/bezier_chart/bezier_line.dart b/lib/widgets/bezier_chart/bezier_line.dart index 715e0a285..87da27e32 100644 --- a/lib/widgets/bezier_chart/bezier_line.dart +++ b/lib/widgets/bezier_chart/bezier_line.dart @@ -21,7 +21,7 @@ class BezierLine { ///This builder is only valid for `bezierChartScale` of date types ///It uses the double value returned by the function based on the current `DateTime` received as parameter - final MissingValueBuilder onMissingValue; + final MissingValueBuilder? onMissingValue; ///Label used in the bubble info indicator final String label; @@ -31,13 +31,13 @@ class BezierLine { this.lineStrokeWidth = 2.0, this.label = '', this.onMissingValue, - Color dataPointFillColor, - Color dataPointStrokeColor, - this.data, - }) : this.dataPointFillColor = dataPointFillColor ?? lineColor, - this.dataPointStrokeColor = dataPointStrokeColor ?? lineColor; + Color? dataPointFillColor, + Color? dataPointStrokeColor, + required this.data, + }) : dataPointFillColor = dataPointFillColor ?? lineColor, + dataPointStrokeColor = dataPointStrokeColor ?? lineColor; - factory BezierLine.copy({BezierLine bezierLine}) { + factory BezierLine.copy({required BezierLine bezierLine}) { return BezierLine( lineColor: bezierLine.lineColor, lineStrokeWidth: bezierLine.lineStrokeWidth, @@ -63,11 +63,11 @@ class BezierLine { @override int get hashCode => data - .map((val) => val.value?.toString() ?? '') + .map((val) => val.value.toString()) .reduce((val1, val2) => '$val1$val2') .hashCode; -} +} ///This class represent each value `Y` per `X` axis class DataPoint { ///The value `Y` @@ -77,8 +77,8 @@ class DataPoint { final T xAxis; const DataPoint({ - this.value, - this.xAxis, + required this.value, + required this.xAxis, }); @override diff --git a/lib/widgets/bezier_chart/my_single_child_scroll_view.dart b/lib/widgets/bezier_chart/my_single_child_scroll_view.dart index 0ddf68fbe..2a41cc6ae 100644 --- a/lib/widgets/bezier_chart/my_single_child_scroll_view.dart +++ b/lib/widgets/bezier_chart/my_single_child_scroll_view.dart @@ -8,18 +8,16 @@ import 'package:flutter/gestures.dart' show DragStartBehavior; class MySingleChildScrollView extends StatelessWidget { /// Creates a box in which a single widget can be scrolled. const MySingleChildScrollView({ - Key key, + Key? key, this.scrollDirection = Axis.vertical, this.reverse = false, this.padding, - bool primary, + bool? primary, this.physics, this.controller, - this.child, + required this.child, this.dragStartBehavior = DragStartBehavior.start, - }) : assert(scrollDirection != null), - assert(dragStartBehavior != null), - assert( + }) : assert( !(controller != null && primary == true), 'Primary ScrollViews obtain their ScrollController via inheritance from a PrimaryScrollController widget. ' 'You cannot both set primary to true and pass an explicit controller.'), @@ -47,7 +45,7 @@ class MySingleChildScrollView extends StatelessWidget { final bool reverse; /// The amount of space by which to inset the child. - final EdgeInsetsGeometry padding; + final EdgeInsetsGeometry? padding; /// An object that can be used to control the position to which this scroll /// view is scrolled. @@ -61,7 +59,7 @@ class MySingleChildScrollView extends StatelessWidget { /// [ScrollController.keepScrollOffset]). It can be used to read the current /// scroll position (see [ScrollController.offset]), or change it (see /// [ScrollController.animateTo]). - final ScrollController controller; + final ScrollController? controller; /// Whether this is the primary scroll view associated with the parent /// [PrimaryScrollController]. @@ -79,7 +77,7 @@ class MySingleChildScrollView extends StatelessWidget { /// user stops dragging the scroll view. /// /// Defaults to matching platform conventions. - final ScrollPhysics physics; + final ScrollPhysics? physics; /// The widget that scrolls. /// @@ -98,8 +96,8 @@ class MySingleChildScrollView extends StatelessWidget { Widget build(BuildContext context) { final AxisDirection axisDirection = _getDirection(context); Widget contents = child; - if (padding != null) contents = Padding(padding: padding, child: contents); - final ScrollController scrollController = + if (padding != null) contents = Padding(padding: padding!, child: contents); + final ScrollController? scrollController = primary ? PrimaryScrollController.of(context) : controller; final Scrollable scrollable = Scrollable( dragStartBehavior: dragStartBehavior, @@ -122,12 +120,11 @@ class MySingleChildScrollView extends StatelessWidget { class _SingleChildViewport extends SingleChildRenderObjectWidget { const _SingleChildViewport({ - Key key, + Key? key, this.axisDirection = AxisDirection.down, - this.offset, - Widget child, - }) : assert(axisDirection != null), - super(key: key, child: child); + required this.offset, + Widget? child, + }) : super(key: key, child: child); final AxisDirection axisDirection; final ViewportOffset offset; @@ -155,13 +152,10 @@ class _RenderSingleChildViewport extends RenderBox implements RenderAbstractViewport { _RenderSingleChildViewport({ AxisDirection axisDirection = AxisDirection.down, - @required ViewportOffset offset, + required ViewportOffset offset, double cacheExtent = RenderAbstractViewport.defaultCacheExtent, - RenderBox child, - }) : assert(axisDirection != null), - assert(offset != null), - assert(cacheExtent != null), - _axisDirection = axisDirection, + RenderBox? child, + }) : _axisDirection = axisDirection, _offset = offset, _cacheExtent = cacheExtent { this.child = child; @@ -170,7 +164,6 @@ class _RenderSingleChildViewport extends RenderBox AxisDirection get axisDirection => _axisDirection; AxisDirection _axisDirection; set axisDirection(AxisDirection value) { - assert(value != null); if (value == _axisDirection) return; _axisDirection = value; markNeedsLayout(); @@ -181,7 +174,6 @@ class _RenderSingleChildViewport extends RenderBox ViewportOffset get offset => _offset; ViewportOffset _offset; set offset(ViewportOffset value) { - assert(value != null); if (value == _offset) return; if (attached) _offset.removeListener(_hasScrolled); _offset = value; @@ -193,7 +185,6 @@ class _RenderSingleChildViewport extends RenderBox double get cacheExtent => _cacheExtent; double _cacheExtent; set cacheExtent(double value) { - assert(value != null); if (value == _cacheExtent) return; _cacheExtent = value; markNeedsLayout(); @@ -234,7 +225,6 @@ class _RenderSingleChildViewport extends RenderBox case Axis.vertical: return size.height; } - return null; } double get _minScrollExtent { @@ -244,14 +234,14 @@ class _RenderSingleChildViewport extends RenderBox double get _maxScrollExtent { assert(hasSize); - if (child == null) return 0.0; + if (child == null) + return 0.0; switch (axis) { case Axis.horizontal: - return math.max(0.0, child.size.width - size.width); + return math.max(0.0, child!.size.width - size.width); case Axis.vertical: - return math.max(0.0, child.size.height - size.height); + return math.max(0.0, child!.size.height - size.height); } - return null; } BoxConstraints _getInnerConstraints(BoxConstraints constraints) { @@ -261,30 +251,33 @@ class _RenderSingleChildViewport extends RenderBox case Axis.vertical: return constraints.widthConstraints(); } - return null; } @override double computeMinIntrinsicWidth(double height) { - if (child != null) return child.getMinIntrinsicWidth(height); + if (child != null) + return child!.getMinIntrinsicWidth(height); return 0.0; } @override double computeMaxIntrinsicWidth(double height) { - if (child != null) return child.getMaxIntrinsicWidth(height); + if (child != null) + return child!.getMaxIntrinsicWidth(height); return 0.0; } @override double computeMinIntrinsicHeight(double width) { - if (child != null) return child.getMinIntrinsicHeight(width); + if (child != null) + return child!.getMinIntrinsicHeight(width); return 0.0; } @override double computeMaxIntrinsicHeight(double width) { - if (child != null) return child.getMaxIntrinsicHeight(width); + if (child != null) + return child!.getMaxIntrinsicHeight(width); return 0.0; } @@ -298,8 +291,8 @@ class _RenderSingleChildViewport extends RenderBox if (child == null) { size = constraints.smallest; } else { - child.layout(_getInnerConstraints(constraints), parentUsesSize: true); - size = constraints.constrain(child.size); + child!.layout(_getInnerConstraints(constraints), parentUsesSize: true); + size = constraints.constrain(child!.size); } offset.applyViewportDimension(_viewportExtent); @@ -309,24 +302,22 @@ class _RenderSingleChildViewport extends RenderBox Offset get _paintOffset => _paintOffsetForPosition(offset.pixels); Offset _paintOffsetForPosition(double position) { - assert(axisDirection != null); switch (axisDirection) { case AxisDirection.up: - return Offset(0.0, position - child.size.height + size.height); + return Offset(0.0, position - child!.size.height + size.height); case AxisDirection.down: return Offset(0.0, -position); case AxisDirection.left: - return Offset(position - child.size.width + size.width, 0.0); + return Offset(position - child!.size.width + size.width, 0.0); case AxisDirection.right: return Offset(-position, 0.0); } - return null; } bool _shouldClipAtPaintOffset(Offset paintOffset) { assert(child != null); return paintOffset < Offset.zero || - !(Offset.zero & size).contains((paintOffset & child.size).bottomRight); + !(Offset.zero & size).contains((paintOffset & child!.size).bottomRight); } @override @@ -335,7 +326,7 @@ class _RenderSingleChildViewport extends RenderBox final Offset paintOffset = _paintOffset; void paintContents(PaintingContext context, Offset offset) { - context.paintChild(child, offset + paintOffset); + context.paintChild(child!, offset + paintOffset); } if (_shouldClipAtPaintOffset(paintOffset)) { @@ -355,38 +346,38 @@ class _RenderSingleChildViewport extends RenderBox } @override - Rect describeApproximatePaintClip(RenderObject child) { - if (child != null && _shouldClipAtPaintOffset(_paintOffset)) + Rect? describeApproximatePaintClip(RenderObject child) { + if (_shouldClipAtPaintOffset(_paintOffset)) return Offset.zero & size; return null; } @override - bool hitTestChildren(HitTestResult result, {Offset position}) { + bool hitTestChildren(BoxHitTestResult result, {required Offset position}) { if (child != null) { final Offset transformed = position + -_paintOffset; - return child.hitTest(result, position: transformed); + return child!.hitTest(result, position: transformed); } return false; } @override RevealedOffset getOffsetToReveal(RenderObject target, double alignment, - {Rect rect}) { + {Rect? rect, Axis? axis,}) { + axis = this.axis; rect ??= target.paintBounds; if (target is! RenderBox) return RevealedOffset(offset: offset.pixels, rect: rect); - final RenderBox targetBox = target as RenderBox; + final RenderBox targetBox = target; final Matrix4 transform = targetBox.getTransformTo(this); final Rect bounds = MatrixUtils.transformRect(transform, rect); - final Size contentSize = child.size; + final Size contentSize = child!.size; double leadingScrollOffset; double targetMainAxisExtent; double mainAxisExtent; - assert(axisDirection != null); switch (axisDirection) { case AxisDirection.up: mainAxisExtent = size.height; @@ -418,8 +409,8 @@ class _RenderSingleChildViewport extends RenderBox @override void showOnScreen({ - RenderObject descendant, - Rect rect, + RenderObject? descendant, + Rect? rect, Duration duration = Duration.zero, Curve curve = Curves.ease, }) { @@ -432,7 +423,7 @@ class _RenderSingleChildViewport extends RenderBox ); } - final Rect newRect = RenderViewportBase.showInViewport( + final Rect? newRect = RenderViewportBase.showInViewport( descendant: descendant, viewport: this, offset: offset, @@ -449,7 +440,6 @@ class _RenderSingleChildViewport extends RenderBox @override Rect describeSemanticsClip(RenderObject child) { - assert(axis != null); switch (axis) { case Axis.vertical: return Rect.fromLTRB( @@ -466,6 +456,5 @@ class _RenderSingleChildViewport extends RenderBox semanticBounds.bottom, ); } - return null; } } diff --git a/lib/widgets/click_item.dart b/lib/widgets/click_item.dart index 89969ee2f..eece1f2ca 100644 --- a/lib/widgets/click_item.dart +++ b/lib/widgets/click_item.dart @@ -1,19 +1,18 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; class ClickItem extends StatelessWidget { const ClickItem({ - Key key, + super.key, this.onTap, - @required this.title, + required this.title, this.content = '', this.textAlign = TextAlign.start, this.maxLines = 1 - }): super(key: key); + }); - final GestureTapCallback onTap; + final GestureTapCallback? onTap; final String title; final String content; final TextAlign textAlign; @@ -35,7 +34,7 @@ class ClickItem extends StatelessWidget { maxLines: maxLines, textAlign: maxLines == 1 ? TextAlign.right : textAlign, overflow: TextOverflow.ellipsis, - style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp14), + style: Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp14), ), ), Gaps.hGap8, @@ -55,7 +54,6 @@ class ClickItem extends StatelessWidget { margin: const EdgeInsets.only(left: 15.0), padding: const EdgeInsets.fromLTRB(0, 15.0, 15.0, 15.0), constraints: const BoxConstraints( - maxHeight: double.infinity, minHeight: 50.0, ), width: double.infinity, diff --git a/lib/widgets/double_tap_back_exit_app.dart b/lib/widgets/double_tap_back_exit_app.dart index 35c7fc223..5adfaf860 100644 --- a/lib/widgets/double_tap_back_exit_app.dart +++ b/lib/widgets/double_tap_back_exit_app.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_deer/util/toast_utils.dart'; @@ -7,10 +6,10 @@ import 'package:flutter_deer/util/toast_utils.dart'; class DoubleTapBackExitApp extends StatefulWidget { const DoubleTapBackExitApp({ - Key key, - @required this.child, + super.key, + required this.child, this.duration = const Duration(milliseconds: 2500), - }): super(key: key); + }); final Widget child; /// 两次点击返回按钮的时间间隔 @@ -22,7 +21,7 @@ class DoubleTapBackExitApp extends StatefulWidget { class _DoubleTapBackExitAppState extends State { - DateTime _lastTime; + DateTime? _lastTime; @override Widget build(BuildContext context) { @@ -33,7 +32,7 @@ class _DoubleTapBackExitAppState extends State { } Future _isExit() async { - if (_lastTime == null || DateTime.now().difference(_lastTime) > widget.duration) { + if (_lastTime == null || DateTime.now().difference(_lastTime!) > widget.duration) { _lastTime = DateTime.now(); Toast.show('再次点击退出应用'); return Future.value(false); @@ -44,4 +43,3 @@ class _DoubleTapBackExitAppState extends State { return Future.value(true); } } - diff --git a/lib/widgets/fractionally_aligned_sized_box.dart b/lib/widgets/fractionally_aligned_sized_box.dart new file mode 100644 index 000000000..7901581e9 --- /dev/null +++ b/lib/widgets/fractionally_aligned_sized_box.dart @@ -0,0 +1,106 @@ +import 'package:flutter/widgets.dart'; + +/// https://github.com/letsar/flutter_slidable +/// A widget that positions its child to a fraction of the total available space. +class FractionallyAlignedSizedBox extends StatelessWidget { + /// Creates a widget that positions its child to a fraction of the total available space. + /// + /// Only two out of the three horizontal values ([leftFactor], [rightFactor], + /// [widthFactor]), and only two out of the three vertical values ([topFactor], + /// [bottomFactor], [heightFactor]), can be set. In each case, at least one of + /// the three must be null. + /// + /// If non-null, the [widthFactor] and [heightFactor] arguments must be + /// non-negative. + const FractionallyAlignedSizedBox({ + super.key, + required this.child, + this.leftFactor, + this.topFactor, + this.rightFactor, + this.bottomFactor, + this.widthFactor, + this.heightFactor, + }) : assert( + leftFactor == null || rightFactor == null || widthFactor == null), + assert( + topFactor == null || bottomFactor == null || heightFactor == null), + assert(widthFactor == null || widthFactor >= 0.0), + assert(heightFactor == null || heightFactor >= 0.0); + + /// The relative distance that the child's left edge is inset from the left of the parent. + final double? leftFactor; + + /// The relative distance that the child's top edge is inset from the top of the parent. + final double? topFactor; + + /// The relative distance that the child's right edge is inset from the right of the parent. + final double? rightFactor; + + /// The relative distance that the child's bottom edge is inset from the bottom of the parent. + final double? bottomFactor; + + /// The child's width relative to its parent's width. + final double? widthFactor; + + /// The child's height relative to its parent's height. + final double? heightFactor; + + /// The widget below this widget in the tree. + final Widget child; + + @override + Widget build(BuildContext context) { + double dx = 0; + double dy = 0; + double? width = widthFactor; + double? height = heightFactor; + + if (widthFactor == null) { + final left = leftFactor ?? 0; + final right = rightFactor ?? 0; + width = 1 - left - right; + + if (width != 1) { + dx = left / (1.0 - width); + } + } + + if (heightFactor == null) { + final top = topFactor ?? 0; + final bottom = bottomFactor ?? 0; + height = 1 - top - bottom; + if (height != 1) { + dy = top / (1.0 - height); + } + } + + if (widthFactor != null && widthFactor != 1) { + if (leftFactor != null) { + dx = leftFactor! / (1 - widthFactor!); + } else if (leftFactor == null && rightFactor != null) { + dx = (1 - widthFactor! - rightFactor!) / (1 - widthFactor!); + } + } + + if (heightFactor != null && heightFactor != 1) { + if (topFactor != null) { + dy = topFactor! / (1 - heightFactor!); + } else if (topFactor == null && bottomFactor != null) { + dy = (1 - heightFactor! - bottomFactor!) / (1 - heightFactor!); + } + } + + return Align( + alignment: FractionalOffset( + dx, + dy, + ), + child: FractionallySizedBox( + widthFactor: width, + heightFactor: height, + child: child, + ), + ); + } +} diff --git a/lib/widgets/load_image.dart b/lib/widgets/load_image.dart index 055c6f790..f3544828e 100644 --- a/lib/widgets/load_image.dart +++ b/lib/widgets/load_image.dart @@ -1,4 +1,3 @@ - import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_deer/util/image_utils.dart'; @@ -7,7 +6,7 @@ import 'package:flutter_deer/util/image_utils.dart'; class LoadImage extends StatelessWidget { const LoadImage(this.image, { - Key key, + super.key, this.width, this.height, this.fit = BoxFit.cover, @@ -15,27 +14,26 @@ class LoadImage extends StatelessWidget { this.holderImg = 'none', this.cacheWidth, this.cacheHeight, - }) : assert(image != null, 'The [image] argument must not be null.'), - super(key: key); + }); final String image; - final double width; - final double height; + final double? width; + final double? height; final BoxFit fit; final ImageFormat format; final String holderImg; - final int cacheWidth; - final int cacheHeight; + final int? cacheWidth; + final int? cacheHeight; @override Widget build(BuildContext context) { if (image.isEmpty || image.startsWith('http')) { - final Widget _image = LoadAssetImage(holderImg, height: height, width: width, fit: fit); + final Widget holder = LoadAssetImage(holderImg, height: height, width: width, fit: fit); return CachedNetworkImage( imageUrl: image, - placeholder: (_, __) => _image, - errorWidget: (_, __, dynamic error) => _image, + placeholder: (_, __) => holder, + errorWidget: (_, __, dynamic error) => holder, width: width, height: height, fit: fit, @@ -59,7 +57,7 @@ class LoadImage extends StatelessWidget { class LoadAssetImage extends StatelessWidget { const LoadAssetImage(this.image, { - Key key, + super.key, this.width, this.height, this.cacheWidth, @@ -67,16 +65,16 @@ class LoadAssetImage extends StatelessWidget { this.fit, this.format = ImageFormat.png, this.color - }): super(key: key); + }); final String image; - final double width; - final double height; - final int cacheWidth; - final int cacheHeight; - final BoxFit fit; + final double? width; + final double? height; + final int? cacheWidth; + final int? cacheHeight; + final BoxFit? fit; final ImageFormat format; - final Color color; + final Color? color; @override Widget build(BuildContext context) { diff --git a/lib/widgets/my_app_bar.dart b/lib/widgets/my_app_bar.dart index 9bd380668..6ddeb6a5c 100644 --- a/lib/widgets/my_app_bar.dart +++ b/lib/widgets/my_app_bar.dart @@ -9,7 +9,7 @@ import 'package:flutter_deer/widgets/my_button.dart'; class MyAppBar extends StatelessWidget implements PreferredSizeWidget { const MyAppBar({ - Key key, + super.key, this.backgroundColor, this.title = '', this.centerTitle = '', @@ -18,42 +18,23 @@ class MyAppBar extends StatelessWidget implements PreferredSizeWidget { this.backImgColor, this.onPressed, this.isBack = true - }): super(key: key); + }); - final Color backgroundColor; + final Color? backgroundColor; final String title; final String centerTitle; final String backImg; - final Color backImgColor; + final Color? backImgColor; final String actionName; - final VoidCallback onPressed; + final VoidCallback? onPressed; final bool isBack; @override Widget build(BuildContext context) { - Color _backgroundColor; + final Color bgColor = backgroundColor ?? context.backgroundColor; - if (backgroundColor == null) { - _backgroundColor = context.backgroundColor; - } else { - _backgroundColor = backgroundColor; - } - - final SystemUiOverlayStyle _overlayStyle = ThemeData.estimateBrightnessForColor(_backgroundColor) == Brightness.dark - ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark; - - final Widget back = isBack ? IconButton( - onPressed: () { - FocusManager.instance.primaryFocus?.unfocus(); - Navigator.maybePop(context); - }, - tooltip: 'Back', - padding: const EdgeInsets.all(12.0), - icon: Image.asset( - backImg, - color: backImgColor ?? ThemeUtils.getIconColor(context), - ), - ) : Gaps.empty; + final SystemUiOverlayStyle overlayStyle = ThemeData.estimateBrightnessForColor(bgColor) == Brightness.dark + ? ThemeUtils.light : ThemeUtils.dark; final Widget action = actionName.isNotEmpty ? Positioned( right: 0.0, @@ -76,24 +57,40 @@ class MyAppBar extends StatelessWidget implements PreferredSizeWidget { ), ) : Gaps.empty; + final Widget back = isBack ? IconButton( + onPressed: () async { + FocusManager.instance.primaryFocus?.unfocus(); + final isBack = await Navigator.maybePop(context); + if (!isBack) { + await SystemNavigator.pop(); + } + }, + tooltip: 'Back', + padding: const EdgeInsets.all(12.0), + icon: Image.asset( + backImg, + color: backImgColor ?? ThemeUtils.getIconColor(context), + ), + ) : Gaps.empty; + final Widget titleWidget = Semantics( namesRoute: true, header: true, child: Container( alignment: centerTitle.isEmpty ? Alignment.centerLeft : Alignment.center, width: double.infinity, + margin: const EdgeInsets.symmetric(horizontal: 48.0), child: Text( title.isEmpty ? centerTitle : title, style: const TextStyle(fontSize: Dimens.font_sp18,), ), - margin: const EdgeInsets.symmetric(horizontal: 48.0), ), ); return AnnotatedRegion( - value: _overlayStyle, + value: overlayStyle, child: Material( - color: _backgroundColor, + color: bgColor, child: SafeArea( child: Stack( alignment: Alignment.centerLeft, diff --git a/lib/widgets/my_button.dart b/lib/widgets/my_button.dart index 2fb8baea2..ac6a233f1 100644 --- a/lib/widgets/my_button.dart +++ b/lib/widgets/my_button.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/util/theme_utils.dart'; @@ -7,7 +6,7 @@ import 'package:flutter_deer/util/theme_utils.dart'; class MyButton extends StatelessWidget { const MyButton({ - Key key, + super.key, this.text = '', this.fontSize = Dimens.font_sp18, this.textColor, @@ -19,18 +18,18 @@ class MyButton extends StatelessWidget { this.padding = const EdgeInsets.symmetric(horizontal: 16.0), this.radius = 2.0, this.side = BorderSide.none, - @required this.onPressed, - }): super(key: key); + required this.onPressed, + }); final String text; final double fontSize; - final Color textColor; - final Color disabledTextColor; - final Color backgroundColor; - final Color disabledBackgroundColor; - final double minHeight; - final double minWidth; - final VoidCallback onPressed; + final Color? textColor; + final Color? disabledTextColor; + final Color? backgroundColor; + final Color? disabledBackgroundColor; + final double? minHeight; + final double? minWidth; + final VoidCallback? onPressed; final EdgeInsetsGeometry padding; final double radius; final BorderSide side; @@ -39,7 +38,6 @@ class MyButton extends StatelessWidget { Widget build(BuildContext context) { final bool isDark = context.isDark; return TextButton( - child: Text(text, style: TextStyle(fontSize: fontSize),), onPressed: onPressed, style: ButtonStyle( // 文字颜色 @@ -62,7 +60,7 @@ class MyButton extends StatelessWidget { return (textColor ?? (isDark ? Colours.dark_button_text : Colors.white)).withOpacity(0.12); }), // 按钮最小大小 - minimumSize: (minWidth == null || minHeight == null) ? null : MaterialStateProperty.all(Size(minWidth, minHeight)), + minimumSize: (minWidth == null || minHeight == null) ? null : MaterialStateProperty.all(Size(minWidth!, minHeight!)), padding: MaterialStateProperty.all(padding), shape: MaterialStateProperty.all( RoundedRectangleBorder( @@ -70,7 +68,8 @@ class MyButton extends StatelessWidget { ), ), side: MaterialStateProperty.all(side), - ) + ), + child: Text(text, style: TextStyle(fontSize: fontSize),) ); } } diff --git a/lib/widgets/my_card.dart b/lib/widgets/my_card.dart index 6d3eb222b..2d287baa9 100644 --- a/lib/widgets/my_card.dart +++ b/lib/widgets/my_card.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/colors.dart'; import 'package:flutter_deer/util/theme_utils.dart'; @@ -6,39 +5,29 @@ import 'package:flutter_deer/util/theme_utils.dart'; class MyCard extends StatelessWidget { const MyCard({ - Key key, - @required this.child, + super.key, + required this.child, this.color, this.shadowColor - }): super(key: key); + }); final Widget child; - final Color color; - final Color shadowColor; + final Color? color; + final Color? shadowColor; @override Widget build(BuildContext context) { - Color _backgroundColor; - Color _shadowColor; final bool isDark = context.isDark; - if (color == null) { - _backgroundColor = isDark ? Colours.dark_bg_gray_ : Colors.white; - } else { - _backgroundColor = color; - } - if (shadowColor == null) { - _shadowColor = isDark ? Colors.transparent : const Color(0x80DCE7FA); - } else { - _shadowColor = isDark ? Colors.transparent : shadowColor; - } + final Color backgroundColor = color ?? (isDark ? Colours.dark_bg_gray_ : Colors.white); + final Color sColor = isDark ? Colors.transparent : (shadowColor ?? const Color(0x80DCE7FA)); return DecoratedBox( decoration: BoxDecoration( - color: _backgroundColor, + color: backgroundColor, borderRadius: BorderRadius.circular(8.0), boxShadow: [ - BoxShadow(color: _shadowColor, offset: const Offset(0.0, 2.0), blurRadius: 8.0, spreadRadius: 0.0), + BoxShadow(color: sColor, offset: const Offset(0.0, 2.0), blurRadius: 8.0), ], ), child: child, diff --git a/lib/widgets/my_flexible_space_bar.dart b/lib/widgets/my_flexible_space_bar.dart index 5853a2c1e..ce0b15512 100644 --- a/lib/widgets/my_flexible_space_bar.dart +++ b/lib/widgets/my_flexible_space_bar.dart @@ -1,10 +1,7 @@ // 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/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; /// The part of a material design [AppBar] that expands and collapses. @@ -28,30 +25,29 @@ class MyFlexibleSpaceBar extends StatefulWidget { /// /// Most commonly used in the [AppBar.flexibleSpace] field. const MyFlexibleSpaceBar({ - Key key, + super.key, this.title, this.background, this.centerTitle, this.titlePadding, this.collapseMode = CollapseMode.parallax, - }) : assert(collapseMode != null), - super(key: key); + }); /// The primary contents of the flexible space bar when expanded. /// /// Typically a [Text] widget. - final Widget title; + final Widget? title; /// Shown behind the [title] when expanded. /// /// Typically an [Image] widget with [Image.fit] set to [BoxFit.cover]. - final Widget background; + final Widget? background; /// Whether the title should be centered. /// /// By default this property is true if the current target platform /// is [TargetPlatform.iOS], false otherwise. - final bool centerTitle; + final bool? centerTitle; /// Collapse effect while scrolling. /// @@ -68,7 +64,7 @@ class MyFlexibleSpaceBar extends StatefulWidget { /// 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; + final EdgeInsetsGeometry? titlePadding; /// Wraps a widget that contains an [AppBar] to convey sizing information down /// to the [FlexibleSpaceBar]. @@ -88,13 +84,12 @@ class MyFlexibleSpaceBar extends StatefulWidget { /// * [FlexibleSpaceBarSettings] which creates a settings object that can be /// used to specify these settings to a [FlexibleSpaceBar]. static Widget createSettings({ - double toolbarOpacity, - double minExtent, - double maxExtent, - @required double currentExtent, - @required Widget child, + double? toolbarOpacity, + double? minExtent, + double? maxExtent, + required double currentExtent, + required Widget child, }) { - assert(currentExtent != null); return FlexibleSpaceBarSettings( toolbarOpacity: toolbarOpacity ?? 1.0, minExtent: minExtent ?? currentExtent, @@ -110,9 +105,9 @@ class MyFlexibleSpaceBar extends StatefulWidget { class _FlexibleSpaceBarState extends State { bool _getEffectiveCenterTitle(ThemeData theme) { - if (widget.centerTitle != null) - return widget.centerTitle; - assert(theme.platform != null); + if (widget.centerTitle != null) { + return widget.centerTitle!; + } switch (theme.platform) { case TargetPlatform.android: case TargetPlatform.fuchsia: @@ -123,24 +118,22 @@ class _FlexibleSpaceBarState extends State { case TargetPlatform.macOS: return true; } - return null; } Alignment _getTitleAlignment(bool effectiveCenterTitle) { - if (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; } - return null; } - double _getCollapsePadding(double t, FlexibleSpaceBarSettings settings) { + double? _getCollapsePadding(double t, FlexibleSpaceBarSettings settings) { switch (widget.collapseMode) { case CollapseMode.pin: return -(settings.maxExtent - settings.currentExtent); @@ -150,7 +143,6 @@ class _FlexibleSpaceBarState extends State { final double deltaExtent = settings.maxExtent - settings.minExtent; return -Tween(begin: 0.0, end: deltaExtent / 4.0).transform(t); } - return null; } final GlobalKey _key = GlobalKey(); @@ -161,8 +153,8 @@ class _FlexibleSpaceBarState extends State { void initState() { //监听Widget是否绘制完毕 WidgetsBinding.instance.addPostFrameCallback((_) { - final RenderBox renderBoxRed = _key.currentContext.findRenderObject() as RenderBox; - _offset = renderBoxRed.size.width / 2; + final RenderBox? renderBoxRed = _key.currentContext!.findRenderObject() as RenderBox?; + _offset = renderBoxRed!.size.width / 2; }); super.initState(); } @@ -170,8 +162,7 @@ class _FlexibleSpaceBarState extends State { @override Widget build(BuildContext context) { final Size size = MediaQuery.of(context).size; - final FlexibleSpaceBarSettings settings = context.dependOnInheritedWidgetOfExactType(); - assert(settings != null, 'A FlexibleSpaceBar must be wrapped in the widget returned by FlexibleSpaceBar.createSettings().'); + final FlexibleSpaceBarSettings settings = context.dependOnInheritedWidgetOfExactType()!; final List children = []; @@ -179,7 +170,7 @@ class _FlexibleSpaceBarState extends State { // 0.0 -> Expanded // 1.0 -> Collapsed to toolbar - final double t = (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0) as double; + final double t = (1.0 - (settings.currentExtent - settings.minExtent) / deltaExtent).clamp(0.0, 1.0); // background image if (widget.background != null) { @@ -188,7 +179,7 @@ class _FlexibleSpaceBarState extends State { left: 0.0, right: 0.0, height: settings.maxExtent, - child: widget.background, + child: widget.background!, )); } @@ -198,7 +189,7 @@ class _FlexibleSpaceBarState extends State { switch (theme.platform) { case TargetPlatform.iOS: case TargetPlatform.macOS: - title = widget.title; + title = widget.title!; break; case TargetPlatform.android: case TargetPlatform.fuchsia: @@ -218,9 +209,9 @@ class _FlexibleSpaceBarState extends State { final double opacity = settings.toolbarOpacity; if (opacity > 0.0) { - TextStyle titleStyle = theme.primaryTextTheme.headline6; + TextStyle titleStyle = theme.primaryTextTheme.titleLarge!; titleStyle = titleStyle.copyWith( - color: titleStyle.color.withOpacity(opacity), + color: titleStyle.color!.withOpacity(opacity), fontWeight: t != 0 ? FontWeight.normal : FontWeight.bold ); final bool effectiveCenterTitle = _getEffectiveCenterTitle(theme); @@ -232,7 +223,7 @@ class _FlexibleSpaceBarState extends State { final double scaleValue = Tween(begin: 1.5, end: 1.0).transform(t); final double width = (size.width - 32.0) / 2 - _offset; final Matrix4 scaleTransform = Matrix4.identity() - ..scale(scaleValue, scaleValue, 1.0)..translate(t * width, 0.0); + ..scale(scaleValue, scaleValue, 1.0)..translate(t * width); final Alignment titleAlignment = _getTitleAlignment(false); children.add(Container( padding: padding, @@ -254,4 +245,3 @@ class _FlexibleSpaceBarState extends State { return ClipRect(child: Stack(children: children)); } } - diff --git a/lib/widgets/my_refresh_list.dart b/lib/widgets/my_refresh_list.dart index 907f0d4a7..e8e92281c 100644 --- a/lib/widgets/my_refresh_list.dart +++ b/lib/widgets/my_refresh_list.dart @@ -1,4 +1,3 @@ - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -10,20 +9,20 @@ import 'package:flutter_deer/widgets/state_layout.dart'; class DeerListView extends StatefulWidget { const DeerListView({ - Key key, - @required this.itemCount, - @required this.itemBuilder, - @required this.onRefresh, + super.key, + required this.itemCount, + required this.itemBuilder, + required this.onRefresh, this.loadMore, this.hasMore = false, this.stateType = StateType.empty, this.pageSize = 10, this.padding, this.itemExtent, - }): super(key: key); + }); final RefreshCallback onRefresh; - final LoadMoreCallback loadMore; + final LoadMoreCallback? loadMore; final int itemCount; final bool hasMore; final IndexedWidgetBuilder itemBuilder; @@ -31,8 +30,8 @@ class DeerListView extends StatefulWidget { /// 一页的数量,默认为10 final int pageSize; /// padding属性使用时注意会破坏原有的SafeArea,需要自行计算bottom大小 - final EdgeInsetsGeometry padding; - final double itemExtent; + final EdgeInsetsGeometry? padding; + final double? itemExtent; @override _DeerListViewState createState() => _DeerListViewState(); @@ -91,7 +90,7 @@ class _DeerListViewState extends State { return; } _isLoading = true; - await widget.loadMore(); + await widget.loadMore?.call(); _isLoading = false; } @@ -99,7 +98,7 @@ class _DeerListViewState extends State { class MoreWidget extends StatelessWidget { - const MoreWidget(this.itemCount, this.hasMore, this.pageSize, {Key key}): super(key: key); + const MoreWidget(this.itemCount, this.hasMore, this.pageSize, {super.key}); final int itemCount; final bool hasMore; @@ -112,7 +111,6 @@ class MoreWidget extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 10.0), child: Row( mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, children: [ if (hasMore) const CupertinoActivityIndicator(), if (hasMore) Gaps.hGap5, @@ -123,4 +121,3 @@ class MoreWidget extends StatelessWidget { ); } } - diff --git a/lib/widgets/my_scroll_view.dart b/lib/widgets/my_scroll_view.dart index a559447bd..83bf233dd 100644 --- a/lib/widgets/my_scroll_view.dart +++ b/lib/widgets/my_scroll_view.dart @@ -1,9 +1,7 @@ - import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; -import 'package:keyboard_actions/keyboard_actions_config.dart'; /// 本项目通用的布局(SingleChildScrollView) /// 1.底部存在按钮 @@ -13,8 +11,8 @@ class MyScrollView extends StatelessWidget { /// 注意:同时存在底部按钮与keyboardConfig配置时,为保证软键盘弹出高度正常。需要在`Scaffold`使用 `resizeToAvoidBottomInset: defaultTargetPlatform != TargetPlatform.iOS,` /// 除非Android与iOS平台均使用keyboard_actions const MyScrollView({ - Key key, - @required this.children, + super.key, + required this.children, this.padding, this.physics = const BouncingScrollPhysics(), this.crossAxisAlignment = CrossAxisAlignment.start, @@ -22,14 +20,14 @@ class MyScrollView extends StatelessWidget { this.keyboardConfig, this.tapOutsideToDismiss = false, this.overScroll = 16.0, - }): super(key: key); + }); final List children; - final EdgeInsetsGeometry padding; + final EdgeInsetsGeometry? padding; final ScrollPhysics physics; final CrossAxisAlignment crossAxisAlignment; - final Widget bottomButton; - final KeyboardActionsConfig keyboardConfig; + final Widget? bottomButton; + final KeyboardActionsConfig? keyboardConfig; /// 键盘外部按下将其关闭 final bool tapOutsideToDismiss; /// 默认弹起位置在TextField的文字下面,可以添加此属性继续向上滑动一段距离。用来露出完整的TextField。 @@ -48,7 +46,7 @@ class MyScrollView extends StatelessWidget { if (padding != null) { contents = Padding( - padding: padding, + padding: padding!, child: contents ); } @@ -56,8 +54,8 @@ class MyScrollView extends StatelessWidget { contents = KeyboardActions( isDialog: bottomButton != null, overscroll: overScroll, - config: keyboardConfig, - tapOutsideToDismiss: tapOutsideToDismiss, + config: keyboardConfig!, + tapOutsideBehavior: tapOutsideToDismiss ? TapOutsideBehavior.opaqueDismiss : TapOutsideBehavior.none, child: contents ); @@ -76,7 +74,7 @@ class MyScrollView extends StatelessWidget { child: contents ), SafeArea( - child: bottomButton + child: bottomButton! ) ], ); diff --git a/lib/widgets/search_bar.dart b/lib/widgets/my_search_bar.dart similarity index 85% rename from lib/widgets/search_bar.dart rename to lib/widgets/my_search_bar.dart index 7dcb3bca1..d48dc236d 100644 --- a/lib/widgets/search_bar.dart +++ b/lib/widgets/my_search_bar.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; @@ -9,27 +8,27 @@ import 'package:flutter_deer/widgets/my_button.dart'; import 'load_image.dart'; /// 搜索页的AppBar -class SearchBar extends StatefulWidget implements PreferredSizeWidget { +class MySearchBar extends StatefulWidget implements PreferredSizeWidget { - const SearchBar({ - Key key, + const MySearchBar({ + super.key, this.hintText = '', this.backImg = 'assets/images/ic_back_black.png', this.onPressed, - }): super(key: key); + }); final String backImg; final String hintText; - final Function(String) onPressed; + final void Function(String)? onPressed; @override - _SearchBarState createState() => _SearchBarState(); + _MySearchBarState createState() => _MySearchBarState(); @override Size get preferredSize => const Size.fromHeight(48.0); } -class _SearchBarState extends State { +class _MySearchBarState extends State { final TextEditingController _controller = TextEditingController(); final FocusNode _focus = FocusNode(); @@ -40,7 +39,16 @@ class _SearchBarState extends State { _controller.dispose(); super.dispose(); } - + + // @override + // void initState() { + // WidgetsBinding.instance!.addPostFrameCallback((_) async { + // SystemChannels.textInput.invokeMethod('TextInput.updateConfig', const TextInputConfiguration().toJson()); + // SystemChannels.textInput.invokeMethod('TextInput.hide'); + // }); + // super.initState(); + // } + @override Widget build(BuildContext context) { final bool isDark = context.isDark; @@ -108,15 +116,14 @@ class _SearchBarState extends State { // autofocus: true, controller: _controller, focusNode: _focus, - maxLines: 1, textInputAction: TextInputAction.search, onSubmitted: (String val) { _focus.unfocus(); // 点击软键盘的动作按钮时的回调 - widget.onPressed(val); + widget.onPressed?.call(val); }, decoration: InputDecoration( - contentPadding: const EdgeInsets.only(top: 0.0, left: -8.0, right: -16.0, bottom: 14.0), + contentPadding: const EdgeInsets.only(left: -8.0, right: -16.0, bottom: 14.0), border: InputBorder.none, icon: Padding( padding: const EdgeInsets.only(top: 8.0, bottom: 8.0, left: 8.0), @@ -152,12 +159,12 @@ class _SearchBarState extends State { text: '搜索', onPressed:() { _focus.unfocus(); - widget.onPressed(_controller.text); + widget.onPressed?.call(_controller.text); }, ); return AnnotatedRegion( - value: isDark ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark, + value: isDark ? ThemeUtils.light : ThemeUtils.dark, child: Material( color: context.backgroundColor, child: SafeArea( @@ -174,4 +181,4 @@ class _SearchBarState extends State { ), ); } -} \ No newline at end of file +} diff --git a/lib/widgets/pie_chart/pie_chart.dart b/lib/widgets/pie_chart/pie_chart.dart index 338601e14..2f92e85c4 100644 --- a/lib/widgets/pie_chart/pie_chart.dart +++ b/lib/widgets/pie_chart/pie_chart.dart @@ -1,4 +1,3 @@ - import 'dart:math' as math; import 'package:flutter/material.dart'; @@ -11,12 +10,10 @@ import 'package:flutter_deer/widgets/pie_chart/pie_data.dart'; class PieChart extends StatefulWidget { const PieChart({ - Key key, - @required this.data, - @required this.name - }) : assert(data != null, 'The [data] argument must not be null.'), - assert(name != null, 'The [name] argument must not be null.'), - super(key: key); + super.key, + required this.data, + required this.name + }); final List data; final String name; @@ -32,10 +29,10 @@ class PieChart extends StatefulWidget { class _PieChartState extends State with SingleTickerProviderStateMixin { - int count; - Animation animation; - AnimationController controller; - List oldData; + late int count; + late Animation animation; + late AnimationController controller; + late List oldData; @override void initState() { @@ -76,13 +73,13 @@ class _PieChartState extends State with SingleTickerProviderStateMixin shape: BoxShape.circle, color: bgColor, boxShadow: [ - BoxShadow(color: shadowColor, offset: const Offset(0.0, 4.0), blurRadius: 8.0, spreadRadius: 0.0), + BoxShadow(color: shadowColor, offset: const Offset(0.0, 4.0), blurRadius: 8.0), ], ), child: RepaintBoundary( child: AnimatedBuilder( animation: animation, - builder: (_, Widget child) { + builder: (_, Widget? child) { return CustomPaint( painter: PieChartPainter( widget.data, @@ -115,14 +112,14 @@ class _PieChartState extends State with SingleTickerProviderStateMixin class PieChartPainter extends CustomPainter { PieChartPainter(this.data, double angleFactor, this.bgColor, this.name, this.count) { - if (data.length == null || data.isEmpty) { + if (data.isEmpty) { return; } int count = 0; for (int i = 0; i < data.length; i++) { count += data[i].number; } - PieData pieData; + PieData? pieData; if (data.length == 11) { // 获取“其他”数据 pieData = data[10]; @@ -146,26 +143,26 @@ class PieChartPainter extends CustomPainter { _mPaint = Paint(); totalAngle = angleFactor * math.pi * 2; } - - Rect mCircle; - Paint _mPaint; + + late Rect mCircle; + late Paint _mPaint; // 半径 - double mRadius; - List data; - double totalAngle; + late double mRadius; + late List data; + late double totalAngle; // 起始角度 - double prevAngle; - Color bgColor; + late double prevAngle; + late Color bgColor; // 总数量 - int count; + late int count; // 图表名称 - String name; + late String name; @override void paint(Canvas canvas, Size size) { - if (data.length == null || data.isEmpty) { + if (data.isEmpty) { return; } prevAngle = -math.pi; @@ -187,7 +184,7 @@ class PieChartPainter extends CustomPainter { final double x = (size.height * 0.74 / 2) * math.cos(prevAngle + (totalAngle * data[i].percentage / 2)); final double y = (size.height * 0.74 / 2) * math.sin(prevAngle + (totalAngle * data[i].percentage / 2)); // 保留一位小数 - final String percentage = (data[i].percentage * 100).toStringAsFixed(1) + '%'; + final String percentage = '${(data[i].percentage * 100).toStringAsFixed(1)}%'; drawPercentage(canvas, percentage, x, y, size); prevAngle = prevAngle + totalAngle * data[i].percentage; } @@ -231,7 +228,7 @@ class PieChartPainter extends CustomPainter { final List nodes = []; final double height = size.height / data.length; for (int i = 0; i < data.length; i++) { - final String percentage = (data[i].percentage * 100).toStringAsFixed(1) + '%'; + final String percentage = '${(data[i].percentage * 100).toStringAsFixed(1)}%'; final CustomPainterSemantics node = CustomPainterSemantics( rect: Rect.fromLTRB( 0, height * i, @@ -239,7 +236,7 @@ class PieChartPainter extends CustomPainter { ), properties: SemanticsProperties( sortKey: OrdinalSortKey(i.toDouble()), - label: name + '$count件' + data[i].name + '占比'+ percentage, + label: '$name${'$count件'}${data[i].name}占比$percentage', readOnly: true, textDirection: TextDirection.ltr, ), @@ -251,4 +248,4 @@ class PieChartPainter extends CustomPainter { } return nodes; } -} \ No newline at end of file +} diff --git a/lib/widgets/pie_chart/pie_data.dart b/lib/widgets/pie_chart/pie_data.dart index 2f52fc74b..9a555ca98 100644 --- a/lib/widgets/pie_chart/pie_data.dart +++ b/lib/widgets/pie_chart/pie_data.dart @@ -2,15 +2,15 @@ import 'package:flutter/material.dart'; class PieData { /// 颜色 - Color color; + late Color color; /// 百分比 - num percentage; + late num percentage; /// 数量 - int number; + late int number; /// 名称 - String name; + late String name; @override String toString() => 'name: $name, color: $color, ' 'number: $number, percentage: $percentage'; -} \ No newline at end of file +} diff --git a/lib/widgets/popup_window.dart b/lib/widgets/popup_window.dart index 797caca5e..136e9407e 100644 --- a/lib/widgets/popup_window.dart +++ b/lib/widgets/popup_window.dart @@ -4,48 +4,57 @@ import 'package:flutter/material.dart'; ///create by elileo on 2018/12/21 ///https://github.com/elileo1/flutter_travel_friends/blob/master/lib/widget/PopupWindow.dart /// -/// weilu update: 去除了IntrinsicWidth限制,添加了默认蒙版 -const Duration _kWindowDuration = Duration(milliseconds: 300); +/// weilu update: +/// 1.去除了IntrinsicWidth限制,添加了默认蒙版。 +/// 2.简化position计算。 +const Duration _kWindowDuration = Duration.zero; const double _kWindowCloseIntervalEnd = 2.0 / 3.0; -const double _kWindowMaxWidth = 240.0; -const double _kWindowMinWidth = 48.0; -const double _kWindowVerticalPadding = 0.0; -const double _kWindowScreenPadding = 0.0; +const double _kWindowScreenPadding = 0.001; ///弹窗方法 -Future showPopupWindow({ - @required BuildContext context, - RelativeRect position, - @required Widget child, - double elevation = 8.0, - String semanticLabel, - bool fullWidth, +Future showPopupWindow({ + required BuildContext context, + required RenderBox anchor, + required Widget child, + Offset? offset, + String? semanticLabel, bool isShowBg = false, }) { - assert(context != null); - String label = semanticLabel; + switch (defaultTargetPlatform) { case TargetPlatform.iOS: case TargetPlatform.macOS: - label = semanticLabel; break; case TargetPlatform.android: case TargetPlatform.fuchsia: case TargetPlatform.linux: case TargetPlatform.windows: - label = semanticLabel ?? MaterialLocalizations.of(context)?.popupMenuLabel; + semanticLabel ??= MaterialLocalizations.of(context).popupMenuLabel; } + final RenderBox? overlay = Overlay.of(context).context.findRenderObject() as RenderBox?; + + // 默认位置锚点下方 + final Offset defaultOffset = Offset(0, anchor.size.height); + if (offset == null) { + offset = defaultOffset; + } else { + offset = offset + defaultOffset; + } + // 获得控件左下方的坐标 + final a = anchor.localToGlobal(offset, ancestor: overlay); + // 获得控件右下方的坐标 + final b = anchor.localToGlobal(anchor.size.bottomLeft(offset), ancestor: overlay); + final RelativeRect position = RelativeRect.fromRect( + Rect.fromPoints(a, b), + Offset.zero & overlay!.size, + ); return Navigator.push(context, _PopupWindowRoute( position: position, child: child, - elevation: elevation, - semanticLabel: label, - theme: Theme.of(context), - barrierLabel: - MaterialLocalizations.of(context).modalBarrierDismissLabel, - fullWidth: fullWidth, + semanticLabel: semanticLabel, + barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel, isShowBg: isShowBg )); } @@ -53,27 +62,21 @@ Future showPopupWindow({ ///自定义弹窗路由:参照_PopupMenuRoute修改的 class _PopupWindowRoute extends PopupRoute { _PopupWindowRoute({ - RouteSettings settings, - this.child, - this.position, - this.elevation = 8.0, - this.theme, - this.barrierLabel, - this.semanticLabel, - this.fullWidth, - this.isShowBg, - }) : super(settings: settings); + super.settings, + required this.child, + required this.position, + required this.barrierLabel, + required this.semanticLabel, + required this.isShowBg, + }); final Widget child; final RelativeRect position; - double elevation; - final ThemeData theme; - final String semanticLabel; - final bool fullWidth; + final String? semanticLabel; final bool isShowBg; @override - Color get barrierColor => null; + Color? get barrierColor => null; @override bool get barrierDismissible => true; @@ -95,14 +98,10 @@ class _PopupWindowRoute extends PopupRoute { @override Widget buildPage(BuildContext context, Animation animation, Animation secondaryAnimation) { - Widget win = _PopupWindow( + final Widget win = _PopupWindow( route: this, semanticLabel: semanticLabel, - fullWidth: fullWidth, ); - if (theme != null) { - win = Theme(data: theme, child: win); - } return MediaQuery.removePadding( context: context, @@ -122,7 +121,7 @@ class _PopupWindowRoute extends PopupRoute { color: isShowBg ? const Color(0x99000000) : null, child: CustomSingleChildLayout( delegate: _PopupWindowLayoutDelegate( - position, null, Directionality.of(context) + position, Directionality.of(context) ), child: win, ), @@ -138,15 +137,13 @@ class _PopupWindowRoute extends PopupRoute { ///自定义弹窗控件:对自定义的弹窗内容进行再包装,添加长宽、动画等约束条件 class _PopupWindow extends StatelessWidget { const _PopupWindow({ - Key key, - this.route, - this.semanticLabel, - this.fullWidth = false, - }) : super(key: key); + super.key, + required this.route, + required this.semanticLabel, + }); final _PopupWindowRoute route; - final String semanticLabel; - final bool fullWidth; + final String? semanticLabel; @override Widget build(BuildContext context) { @@ -158,36 +155,25 @@ class _PopupWindow extends StatelessWidget { final CurveTween width = CurveTween(curve: const Interval(0.0, unit)); final CurveTween height = CurveTween(curve: const Interval(0.0, unit * length)); - final Widget child = ConstrainedBox( - constraints: BoxConstraints( - minWidth: fullWidth ? double.infinity : _kWindowMinWidth, - maxWidth: fullWidth ? double.infinity : _kWindowMaxWidth, - ), - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(vertical: _kWindowVerticalPadding), - child: route.child, - ) + final Widget child = SingleChildScrollView( + child: route.child, ); return AnimatedBuilder( - animation: route.animation, - builder: (BuildContext context, Widget child) { + animation: route.animation!, + builder: (BuildContext context, Widget? child) { return Opacity( - opacity: opacity.evaluate(route.animation), - child: Material( - type: route.elevation == 0 ? MaterialType.transparency : MaterialType.card, - elevation: route.elevation, - child: Align( - alignment: AlignmentDirectional.topEnd, - widthFactor: width.evaluate(route.animation), - heightFactor: height.evaluate(route.animation), - child: Semantics( - scopesRoute: true, - namesRoute: true, - explicitChildNodes: true, - label: semanticLabel, - child: child, - ), + opacity: opacity.evaluate(route.animation!), + child: Align( + alignment: AlignmentDirectional.topEnd, + widthFactor: width.evaluate(route.animation!), + heightFactor: height.evaluate(route.animation!), + child: Semantics( + scopesRoute: true, + namesRoute: true, + explicitChildNodes: true, + label: semanticLabel, + child: child, ), ), ); @@ -200,10 +186,9 @@ class _PopupWindow extends StatelessWidget { ///自定义委托内容:子控件大小及其位置计算 class _PopupWindowLayoutDelegate extends SingleChildLayoutDelegate { _PopupWindowLayoutDelegate( - this.position, this.selectedItemOffset, this.textDirection); + this.position, this.textDirection); final RelativeRect position; - final double selectedItemOffset; final TextDirection textDirection; @override @@ -221,14 +206,7 @@ class _PopupWindowLayoutDelegate extends SingleChildLayoutDelegate { // getConstraintsForChild. // Find the ideal vertical position. - double y; - if (selectedItemOffset == null) { - y = position.top; - } else { - y = position.top + - (size.height - position.top - position.bottom) / 2.0 - - selectedItemOffset; - } + double y = position.top; // Find the ideal horizontal position. double x; @@ -240,7 +218,6 @@ class _PopupWindowLayoutDelegate extends SingleChildLayoutDelegate { x = position.left; } else { // Menu button is equidistant from both edges, so grow in reading direction. - assert(textDirection != null); switch (textDirection) { case TextDirection.rtl: x = size.width - position.right - childSize.width; @@ -253,14 +230,17 @@ class _PopupWindowLayoutDelegate extends SingleChildLayoutDelegate { // Avoid going outside an area defined as the rectangle 8.0 pixels from the // edge of the screen in every direction. - if (x < _kWindowScreenPadding) + if (x < _kWindowScreenPadding) { x = _kWindowScreenPadding; - else if (x + childSize.width > size.width - _kWindowScreenPadding) + } else if (x + childSize.width > size.width - _kWindowScreenPadding) { x = size.width - childSize.width - _kWindowScreenPadding; - if (y < _kWindowScreenPadding) + } + + if (y < _kWindowScreenPadding) { y = _kWindowScreenPadding; - else if (y + childSize.height > size.height - _kWindowScreenPadding) + } else if (y + childSize.height > size.height - _kWindowScreenPadding) { y = size.height - childSize.height - _kWindowScreenPadding; + } return Offset(x, y); } @@ -268,4 +248,4 @@ class _PopupWindowLayoutDelegate extends SingleChildLayoutDelegate { bool shouldRelayout(_PopupWindowLayoutDelegate oldDelegate) { return position != oldDelegate.position; } -} \ No newline at end of file +} diff --git a/lib/widgets/progress_dialog.dart b/lib/widgets/progress_dialog.dart index ee1a15725..dafae1e20 100644 --- a/lib/widgets/progress_dialog.dart +++ b/lib/widgets/progress_dialog.dart @@ -1,4 +1,3 @@ - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; @@ -7,9 +6,9 @@ import 'package:flutter_deer/res/resources.dart'; class ProgressDialog extends Dialog { const ProgressDialog({ - Key key, + super.key, this.hintText = '', - }):super(key: key); + }); final String hintText; @@ -19,14 +18,7 @@ class ProgressDialog extends Dialog { final Widget progress = Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - Theme( - data: ThemeData( - cupertinoOverrideTheme: const CupertinoThemeData( - brightness: Brightness.dark, // 局部指定夜间模式,加载圈颜色会设置为白色 - ), - ), - child: const CupertinoActivityIndicator(radius: 14.0), - ), + const CupertinoActivityIndicator(radius: 14.0, color: Colors.grey,), Gaps.vGap8, Text(hintText, style: const TextStyle(color: Colors.white),) ], @@ -49,4 +41,4 @@ class ProgressDialog extends Dialog { ), ); } -} \ No newline at end of file +} diff --git a/lib/widgets/selected_image.dart b/lib/widgets/selected_image.dart index 367985806..3d2cde447 100644 --- a/lib/widgets/selected_image.dart +++ b/lib/widgets/selected_image.dart @@ -1,7 +1,8 @@ - import 'dart:io'; +import 'package:common_utils/common_utils.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_deer/res/resources.dart'; import 'package:flutter_deer/util/device_utils.dart'; import 'package:flutter_deer/util/image_utils.dart'; @@ -12,10 +13,14 @@ import 'package:image_picker/image_picker.dart'; class SelectedImage extends StatefulWidget { const SelectedImage({ - Key key, + super.key, + this.url, + this.heroTag, this.size = 80.0, - }): super(key: key); + }); + final String? url; + final String? heroTag; final double size; @override @@ -25,18 +30,18 @@ class SelectedImage extends StatefulWidget { class SelectedImageState extends State { final ImagePicker _picker = ImagePicker(); - ImageProvider _imageProvider; - PickedFile pickedFile; + ImageProvider? _imageProvider; + XFile? pickedFile; Future _getImage() async { try { - pickedFile = await _picker.getImage(source: ImageSource.gallery, maxWidth: 800); + pickedFile = await _picker.pickImage(source: ImageSource.gallery, maxWidth: 800); if (pickedFile != null) { if (Device.isWeb) { - _imageProvider = NetworkImage(pickedFile.path); + _imageProvider = NetworkImage(pickedFile!.path); } else { - _imageProvider = FileImage(File(pickedFile.path)); + _imageProvider = FileImage(File(pickedFile!.path)); } } else { @@ -46,38 +51,47 @@ class SelectedImageState extends State { }); } catch (e) { - Toast.show('没有权限,无法打开相册!'); + if (e is MissingPluginException) { + Toast.show('当前平台暂不支持!'); + } else { + Toast.show('没有权限,无法打开相册!'); + } } } @override Widget build(BuildContext context) { - // color为null时,Web报错NoSuchMethodError: invalid member on null: 'red' (2.0.3),因此这里指定色值。 - final ColorFilter _colorFilter = ColorFilter.mode( + final ColorFilter colorFilter = ColorFilter.mode( ThemeUtils.isDark(context) ? Colours.dark_unselected_item_color : Colours.text_gray, BlendMode.srcIn ); + + Widget image = Container( + width: widget.size, + height: widget.size, + decoration: BoxDecoration( + // 图片圆角展示 + borderRadius: BorderRadius.circular(16.0), + image: DecorationImage( + image: _imageProvider ?? ImageUtils.getImageProvider(widget.url, holderImg: 'store/icon_zj'), + fit: BoxFit.cover, + colorFilter: _imageProvider == null && TextUtil.isEmpty(widget.url) ? colorFilter : null + ), + ), + ); + + if (widget.heroTag != null && !Device.isWeb) { + image = Hero(tag: widget.heroTag!, child: image); + } + return Semantics( label: '选择图片', hint: '跳转相册选择图片', child: InkWell( borderRadius: BorderRadius.circular(16.0), onTap: _getImage, - child: Container( - width: widget.size, - height: widget.size, - decoration: BoxDecoration( - // 图片圆角展示 - borderRadius: BorderRadius.circular(16.0), - image: DecorationImage( - image: _imageProvider ?? ImageUtils.getAssetImage('store/icon_zj'), - fit: BoxFit.cover, - colorFilter: _imageProvider == null ? _colorFilter : null - ), - ), - ), + child: image, ), ); } } - diff --git a/lib/widgets/selected_item.dart b/lib/widgets/selected_item.dart index 32b9540f7..9d3cf81e0 100644 --- a/lib/widgets/selected_item.dart +++ b/lib/widgets/selected_item.dart @@ -1,23 +1,22 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; class SelectedItem extends StatelessWidget { const SelectedItem({ - Key key, + super.key, this.onTap, - @required this.title, + required this.title, this.content = '', this.textAlign = TextAlign.start, this.style - }): super(key: key); + }); - final GestureTapCallback onTap; + final GestureTapCallback? onTap; final String title; final String content; final TextAlign textAlign; - final TextStyle style; + final TextStyle? style; @override Widget build(BuildContext context) { diff --git a/lib/widgets/state_layout.dart b/lib/widgets/state_layout.dart index cd4c2365b..9e76afa9e 100644 --- a/lib/widgets/state_layout.dart +++ b/lib/widgets/state_layout.dart @@ -1,4 +1,3 @@ - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_deer/res/resources.dart'; @@ -9,18 +8,17 @@ import 'package:flutter_deer/widgets/load_image.dart'; class StateLayout extends StatelessWidget { const StateLayout({ - Key key, - @required this.type, + super.key, + required this.type, this.hintText - }):super(key: key); + }); final StateType type; - final String hintText; + final String? hintText; @override Widget build(BuildContext context) { return Column( - crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center, children: [ if (type == StateType.loading) @@ -37,7 +35,7 @@ class StateLayout extends StatelessWidget { const SizedBox(width: double.infinity, height: Dimens.gap_dp16,), Text( hintText ?? type.hintText, - style: Theme.of(context).textTheme.subtitle2.copyWith(fontSize: Dimens.font_sp14), + style: Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp14), ), Gaps.vGap50, ], @@ -74,4 +72,4 @@ extension StateTypeExtension on StateType { '无网络连接', '暂无消息', '马上添加提现账号吧', '', '' ][index]; -} \ No newline at end of file +} diff --git a/lib/widgets/text_field_item.dart b/lib/widgets/text_field_item.dart index 56a965b0a..082c127b1 100644 --- a/lib/widgets/text_field_item.dart +++ b/lib/widgets/text_field_item.dart @@ -1,5 +1,3 @@ - -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_deer/res/resources.dart'; @@ -9,19 +7,19 @@ import 'package:flutter_deer/util/input_formatter/number_text_input_formatter.da class TextFieldItem extends StatelessWidget { const TextFieldItem({ - Key key, + super.key, this.controller, - @required this.title, + required this.title, this.keyboardType = TextInputType.text, this.hintText = '', this.focusNode, - }): super(key: key); + }); - final TextEditingController controller; + final TextEditingController? controller; final String title; final String hintText; final TextInputType keyboardType; - final FocusNode focusNode; + final FocusNode? focusNode; @override Widget build(BuildContext context) { @@ -63,7 +61,7 @@ class TextFieldItem extends StatelessWidget { ); } - List _getInputFormatters() { + List? _getInputFormatters() { if (keyboardType == const TextInputType.numberWithOptions(decimal: true)) { return [UsNumberTextInputFormatter()]; } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index fd853e17d..676d9c0ac 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,14 +5,24 @@ import FlutterMacOS import Foundation -import path_provider_macos -import shared_preferences_macos -import sqflite +import device_info_plus +import file_selector_macos +import path_provider_foundation +import screen_retriever +import shared_preferences_foundation +import sqflite_darwin import url_launcher_macos +import webview_flutter_wkwebview +import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) + FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) 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 index dade8dfad..f063117c2 100644 --- a/macos/Podfile +++ b/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.11' +platform :osx, '10.14' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' @@ -36,5 +36,12 @@ end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_macos_build_settings(target) + target.build_configurations.each do |config| + if config.build_settings['MACOSX_DEPLOYMENT_TARGET'].to_f < 10.9 + config.build_settings['MACOSX_DEPLOYMENT_TARGET'] = '10.14' + end + # https://github.com/flutter/flutter/issues/94914 + config.build_settings['ONLY_ACTIVE_ARCH'] = 'NO' + end end end diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index de024dae5..79c46f1bd 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -159,7 +159,6 @@ E19D36BBE941C0EC3CF86BFB /* Pods-Runner.release.xcconfig */, 9C7621F7B4CD9D86D2CF5117 /* Pods-Runner.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -183,7 +182,6 @@ 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, B2092651FB09C4FF4658E668 /* [CP] Embed Pods Frameworks */, ); buildRules = ( @@ -203,7 +201,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = "The Flutter Authors"; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -254,23 +252,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - 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; @@ -298,19 +279,25 @@ ); inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/FMDB/FMDB.framework", - "${BUILT_PRODUCTS_DIR}/path_provider_macos/path_provider_macos.framework", - "${BUILT_PRODUCTS_DIR}/shared_preferences_macos/shared_preferences_macos.framework", + "${BUILT_PRODUCTS_DIR}/device_info_plus/device_info_plus.framework", + "${BUILT_PRODUCTS_DIR}/file_selector_macos/file_selector_macos.framework", + "${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework", + "${BUILT_PRODUCTS_DIR}/screen_retriever/screen_retriever.framework", + "${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework", "${BUILT_PRODUCTS_DIR}/sqflite/sqflite.framework", "${BUILT_PRODUCTS_DIR}/url_launcher_macos/url_launcher_macos.framework", + "${BUILT_PRODUCTS_DIR}/window_manager/window_manager.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/FMDB.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_macos.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_macos.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/file_selector_macos.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/screen_retriever.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqflite.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_macos.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/window_manager.framework", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; @@ -413,7 +400,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -496,7 +483,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -543,7 +530,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.14; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index f49abc7d7..c0969faa1 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Bool { return true diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements index c946719a1..9f689d112 100644 --- a/macos/Runner/DebugProfile.entitlements +++ b/macos/Runner/DebugProfile.entitlements @@ -10,5 +10,7 @@ com.apple.security.network.client + com.apple.security.files.user-selected.read-only + diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements index f0966c9dc..d3bfabb4d 100644 --- a/macos/Runner/Release.entitlements +++ b/macos/Runner/Release.entitlements @@ -8,5 +8,7 @@ com.apple.security.network.client + com.apple.security.files.user-selected.read-only + diff --git a/preview/lottie.gif b/preview/lottie.gif new file mode 100644 index 000000000..a1f259b96 Binary files /dev/null and b/preview/lottie.gif differ diff --git a/pubspec.yaml b/pubspec.yaml index def8fdbdc..3ebccfdcc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,74 +1,88 @@ name: flutter_deer description: Flutter Deer -version: 1.1.6+16 -author: 唯鹿 +version: 1.3.3+22 +# 唯鹿 homepage: https://weilu.blog.csdn.net/ publish_to: 'none' environment: - sdk: ">=2.10.0 <3.0.0" - flutter: ">=2.0.0" + sdk: ">=3.4.0 <4.0.0" + flutter: ">=3.19.0" dependencies: flutter: sdk: flutter flutter_localizations: sdk: flutter + # 去除网页URL中的“#”(hash) https://flutter.cn/docs/development/ui/navigation/url-strategies + url_strategy: 0.3.0 # Localization https://github.com/dart-lang/intl - intl: ^0.17.0 + intl: ^0.20.2 # Toast插件 https://github.com/OpenFlutter/flutter_oktoast - oktoast: ^3.0.0 - # 网络库 https://github.com/flutterchina/dio - dio: ^4.0.0 + oktoast: ^3.4.0 + # 网络库 https://github.com/cfug/dio + dio: ^5.9.0 # https://github.com/ReactiveX/rxdart - rxdart: ^0.25.0 + rxdart: ^0.28.0 # Dart 常用工具类库 https://github.com/Sky24n/common_utils - common_utils: 1.2.4 + common_utils: 2.1.0 + sp_util: 2.0.3 # Flutter 常用工具类库 https://github.com/Sky24n/flustars - flustars: ^0.3.3 - # Flutter 轮播图 https://github.com/best-flutter/flutter_swiper - flutter_swiper: ^1.1.6 # flutter_swiper很久不维护,备用空安全版本:flutter_card_swiper: ^0.4.0 - # 启动URL的插件(支持Web) https://github.com/flutter/plugins/tree/master/packages/url_launcher - url_launcher: 6.0.3 - # 图片选择插件(支持Web) https://github.com/flutter/plugins/tree/master/packages/image_picker - image_picker: 0.7.4 + flustars_flutter3: ^3.0.0 # flustars很久不维护,可以使用这个替代 + # Flutter 轮播图 https://github.com/mdddj/flutter_swiper_null_safety + flutter_swiper_null_safety_flutter3: ^4.0.3 # flutter_swiper很久不维护,可以使用这个替代 + # 启动URL的插件(支持Web) https://github.com/flutter/packages/tree/main/packages/url_launcher + url_launcher: 6.3.0 + # 图片选择插件 https://github.com/flutter/packages/tree/main/packages/image_picker + image_picker: 1.1.2 # 侧滑删除 https://github.com/letsar/flutter_slidable - flutter_slidable: ^0.5.7 - # WebView插件 https://github.com/flutter/plugins/tree/master/packages/webview_flutter - webview_flutter: 2.0.2 + flutter_slidable: ^4.0.0 + # WebView插件 https://github.com/flutter/packages/tree/main/packages/webview_flutter + webview_flutter: 4.8.0 # 处理键盘事件 https://github.com/diegoveloper/flutter_keyboard_actions - keyboard_actions: ^3.4.0 - # 列表悬浮头 https://github.com/fluttercommunity/flutter_sticky_headers - sticky_headers: ^0.2.0 + keyboard_actions: ^4.2.0 # 城市选择列表 https://github.com/flutterchina/azlistview - azlistview: ^1.1.1 - # 手势识别 https://github.com/aleksanderwozniak/simple_gesture_detector - # simple_gesture_detector: ^0.1.4 + azlistview: ^2.0.0 # 路由框架 https://github.com/theyakka/fluro - fluro: ^2.0.3 + fluro: ^2.0.5 # 图片缓存 https://github.com/renefloor/flutter_cached_network_image - cached_network_image: ^2.5.1 + cached_network_image: ^3.4.0 # 格式化String https://github.com/Naddiseo/dart-sprintf - sprintf: ^6.0.0 + sprintf: ^7.0.0 # 状态管理 https://github.com/rrousselGit/provider - provider: ^5.0.0 + provider: ^6.1.2 # 扫码 https://github.com/juliuscanute/qr_code_scanner - qr_code_scanner: 0.4.0 - # App Shortcuts https://github.com/flutter/plugins/tree/master/packages/quick_actions - quick_actions: 0.6.0 - # 刮刮卡 https://github.com/vintage/scratcher - scratcher: ^2.0.1 - # 振动(支持Web) https://github.com/benjamindean/flutter_vibration - vibration: ^1.7.3 - # 获取当前设备信息 https://github.com/flutter/plugins/tree/master/packages/device_info - device_info: 2.0.0 + qr_code_scanner: + git: + url: 'https://github.com/NeverOvO/qr_code_scanner.git' + ref: '3fe7b88' + # App Shortcuts https://github.com/flutter/packages/tree/main/packages/quick_actions + quick_actions: 1.1.0 + # 振动 https://github.com/benjamindean/flutter_vibration + vibration: 3.1.3 + vibration_web: 1.6.8 + # 获取当前设备信息 https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus + device_info_plus: 11.1.1 + # 桌面应用调整窗口的大小和位置 https://github.com/leanflutter/window_manager + window_manager: 0.4.0 # 高德2D地图插件(支持Web) https://github.com/simplezhli/flutter_2d_amap flutter_2d_amap: git: - ref: 'fdf1e412' + ref: '2f9657e4' url: 'https://github.com/simplezhli/flutter_2d_amap.git' + # demo 用到的库 + # 刮刮卡 https://github.com/vintage/scratcher + scratcher: ^2.5.0 + # 动画效果 https://github.com/xvrh/lottie-flutter + lottie: ^3.3.0 + win32: 5.5.3 + +# https://github.com/simplezhli/flutter_deer/issues/187 +dependency_overrides: + scrollable_positioned_list: ^0.3.2 + dev_dependencies: # Widget测试 @@ -83,7 +97,7 @@ dev_dependencies: # flutter_goldens: # sdk: flutter # 单元测试 - test: ^1.16.5 + test: ^1.16.8 # For information on the generic Dart part of this file, see the # following page: https:/w.dartlang.org/tools/pub/pubspec @@ -109,7 +123,8 @@ flutter: - assets/images/shop/ - assets/images/account/ - assets/images/statistic/ - + - assets/lottie/ + # To add custom fonts to your application, 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 diff --git a/test/account/account_accessibility_test.dart b/test/account/account_accessibility_test.dart index d6103b244..4d7177cef 100644 --- a/test/account/account_accessibility_test.dart +++ b/test/account/account_accessibility_test.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/account/page/account_page.dart'; import 'package:flutter_deer/account/page/account_record_list_page.dart'; @@ -11,7 +10,7 @@ import 'package:flutter_deer/account/page/withdrawal_page.dart'; import 'package:flutter_deer/account/page/withdrawal_password_page.dart'; import 'package:flutter_deer/account/page/withdrawal_record_list_page.dart'; import 'package:flutter_deer/account/page/withdrawal_result_page.dart'; -import 'package:flutter_deer/provider/theme_provider.dart'; +import 'package:flutter_deer/setting/provider/theme_provider.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -70,7 +69,6 @@ void main() { map.forEach((name, page) { testWidgets('$name $themeName', (WidgetTester tester) async { - tester.binding.addTime(const Duration(seconds: 3)); final SemanticsHandle handle = tester.ensureSemantics(); await tester.pumpWidget(MaterialApp(theme: theme, home: page)); await expectLater(tester, meetsGuideline(textContrastGuideline)); diff --git a/test/goods/goods_accessibility_test.dart b/test/goods/goods_accessibility_test.dart index a34133013..74274a09d 100644 --- a/test/goods/goods_accessibility_test.dart +++ b/test/goods/goods_accessibility_test.dart @@ -1,10 +1,10 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/goods/page/goods_edit_page.dart'; import 'package:flutter_deer/goods/page/goods_page.dart'; import 'package:flutter_deer/goods/page/goods_search_page.dart'; import 'package:flutter_deer/goods/page/goods_size_edit_page.dart'; import 'package:flutter_deer/goods/page/goods_size_page.dart'; +import 'package:flutter_deer/setting/provider/theme_provider.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -17,11 +17,12 @@ void main() { map['goods_size_edit_page'] = const GoodsSizeEditPage(); group('goods => 检测页面可点击目标大小是否大于44 * 44', () { + final ThemeData themeData = ThemeProvider().getTheme(); map.forEach((name, page) { testWidgets(name, (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); - await tester.pumpWidget(MaterialApp(home: page)); + await tester.pumpWidget(MaterialApp(home: page, theme: themeData,)); if (name == 'goods_page') { // GoodsListPage 内有一个2秒的延时 await tester.pumpAndSettle(const Duration(seconds: 2)); @@ -33,11 +34,12 @@ void main() { }); group('goods => 检测页面可点击目标是否都有语义', () { + final ThemeData themeData = ThemeProvider().getTheme(); map.forEach((name, page) { testWidgets(name, (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); - await tester.pumpWidget(MaterialApp(home: page)); + await tester.pumpWidget(MaterialApp(home: page, theme: themeData,)); if (name == 'goods_page') { await tester.pumpAndSettle(const Duration(seconds: 2)); } diff --git a/test/login/login_accessibility_test.dart b/test/login/login_accessibility_test.dart index 668b332a4..8fa312b4f 100644 --- a/test/login/login_accessibility_test.dart +++ b/test/login/login_accessibility_test.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/login/page/login_page.dart'; import 'package:flutter_deer/login/page/register_page.dart'; @@ -6,7 +5,7 @@ import 'package:flutter_deer/login/page/reset_password_page.dart'; import 'package:flutter_deer/login/page/sms_login_page.dart'; import 'package:flutter_deer/login/page/update_password_page.dart'; import 'package:flutter_deer/main.dart'; -import 'package:flutter_deer/provider/theme_provider.dart'; +import 'package:flutter_deer/setting/provider/theme_provider.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { diff --git a/test/net/dio_test.dart b/test/net/dio_test.dart index a5aa2a7c5..ee9f6fe68 100644 --- a/test/net/dio_test.dart +++ b/test/net/dio_test.dart @@ -1,5 +1,5 @@ - import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter_deer/net/net.dart'; import 'package:flutter_deer/shop/models/user_entity.dart'; import 'package:test/test.dart'; @@ -17,12 +17,12 @@ void main() { await DioUtils.instance.requestNetwork( Method.get, HttpApi.users, onSuccess: (data) { - expect(data.name, '唯鹿'); + expect(data?.name, '唯鹿'); }, - onError: (_, __) { - print('$_, $__'); + onError: (code, msg) { + debugPrint('$code, $msg'); } ); }); }); -} \ No newline at end of file +} diff --git a/test/order/order_accessibility_test.dart b/test/order/order_accessibility_test.dart index 8f0faee4b..892691c07 100644 --- a/test/order/order_accessibility_test.dart +++ b/test/order/order_accessibility_test.dart @@ -1,4 +1,3 @@ - import 'package:flutter/material.dart'; import 'package:flutter_deer/order/page/order_info_page.dart'; import 'package:flutter_deer/order/page/order_page.dart'; diff --git a/test/setting/setting_accessibility_test.dart b/test/setting/setting_accessibility_test.dart index 045f1096a..6453e5e43 100644 --- a/test/setting/setting_accessibility_test.dart +++ b/test/setting/setting_accessibility_test.dart @@ -1,10 +1,10 @@ - import 'package:flutter/material.dart'; -import 'package:flutter_deer/provider/theme_provider.dart'; +import 'package:flutter_deer/main.dart'; import 'package:flutter_deer/setting/page/about_page.dart'; import 'package:flutter_deer/setting/page/account_manager_page.dart'; import 'package:flutter_deer/setting/page/setting_page.dart'; import 'package:flutter_deer/setting/page/theme_page.dart'; +import 'package:flutter_deer/setting/provider/theme_provider.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -19,7 +19,7 @@ void main() { map.forEach((name, page) { testWidgets(name, (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); - await tester.pumpWidget(MaterialApp(home: page)); + await tester.pumpWidget(MyApp(home: page)); await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); handle.dispose(); }); @@ -30,7 +30,7 @@ void main() { map.forEach((name, page) { testWidgets(name, (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); - await tester.pumpWidget(MaterialApp(home: page)); + await tester.pumpWidget(MyApp(home: page)); await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); handle.dispose(); }); @@ -54,9 +54,8 @@ void main() { map.forEach((name, page) { testWidgets('$name $themeName', (WidgetTester tester) async { - tester.binding.addTime(const Duration(seconds: 3)); final SemanticsHandle handle = tester.ensureSemantics(); - await tester.pumpWidget(MaterialApp(theme: theme, home: page)); + await tester.pumpWidget(MyApp(theme: theme, home: page)); await expectLater(tester, meetsGuideline(textContrastGuideline)); handle.dispose(); }); diff --git a/test/shop/shop_accessibility_test.dart b/test/shop/shop_accessibility_test.dart index 2ea1dba78..ce2886333 100644 --- a/test/shop/shop_accessibility_test.dart +++ b/test/shop/shop_accessibility_test.dart @@ -1,5 +1,5 @@ - import 'package:flutter/material.dart'; +import 'package:flutter_deer/setting/provider/theme_provider.dart'; import 'package:flutter_deer/shop/page/freight_config_page.dart'; import 'package:flutter_deer/shop/page/input_text_page.dart'; import 'package:flutter_deer/shop/page/message_page.dart'; @@ -19,10 +19,11 @@ void main() { map['freight_config_page'] = const FreightConfigPage(); group('shop => 检测页面可点击目标大小是否大于44 * 44', () { + final ThemeData themeData = ThemeProvider().getTheme(); map.forEach((name, page) { testWidgets(name, (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); - await tester.pumpWidget(MaterialApp(home: page)); + await tester.pumpWidget(MaterialApp(home: page, theme: themeData,)); await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); handle.dispose(); }, skip: name == 'select_address_page' || name == 'freight_config_page'); @@ -30,10 +31,11 @@ void main() { }); group('shop => 检测页面可点击目标是否都有语义', () { + final ThemeData themeData = ThemeProvider().getTheme(); map.forEach((name, page) { testWidgets(name, (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); - await tester.pumpWidget(MaterialApp(home: page)); + await tester.pumpWidget(MaterialApp(home: page, theme: themeData,)); await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); handle.dispose(); }); diff --git a/test/statistics/statistic_accessibility_test.dart b/test/statistics/statistic_accessibility_test.dart index 587cf6f7b..c2b51fed4 100644 --- a/test/statistics/statistic_accessibility_test.dart +++ b/test/statistics/statistic_accessibility_test.dart @@ -1,5 +1,5 @@ - import 'package:flutter/material.dart'; +import 'package:flutter_deer/setting/provider/theme_provider.dart'; import 'package:flutter_deer/statistics/page/goods_statistics_page.dart'; import 'package:flutter_deer/statistics/page/order_statistics_page.dart'; import 'package:flutter_deer/statistics/page/statistics_page.dart'; @@ -13,10 +13,11 @@ void main() { map['goods_statistics_page'] = const GoodsStatisticsPage(); group('statistics => 检测页面可点击目标大小是否大于44 * 44', () { + final ThemeData themeData = ThemeProvider().getTheme(); map.forEach((name, page) { testWidgets(name, (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); - await tester.pumpWidget(MaterialApp(home: page)); + await tester.pumpWidget(MaterialApp(home: page, theme: themeData,)); await expectLater(tester, meetsGuideline(iOSTapTargetGuideline)); handle.dispose(); }, skip: name == 'order_statistics_page'); @@ -24,10 +25,11 @@ void main() { }); group('statistics => 检测页面可点击目标是否都有语义', () { + final ThemeData themeData = ThemeProvider().getTheme(); map.forEach((name, page) { testWidgets(name, (WidgetTester tester) async { final SemanticsHandle handle = tester.ensureSemantics(); - await tester.pumpWidget(MaterialApp(home: page)); + await tester.pumpWidget(MaterialApp(home: page, theme: themeData,)); await expectLater(tester, meetsGuideline(labeledTapTargetGuideline)); handle.dispose(); }); diff --git a/test/store/store_accessibility_test.dart b/test/store/store_accessibility_test.dart index 96d697d7c..6e73afee4 100644 --- a/test/store/store_accessibility_test.dart +++ b/test/store/store_accessibility_test.dart @@ -1,6 +1,5 @@ - import 'package:flutter/material.dart'; -import 'package:flutter_deer/provider/theme_provider.dart'; +import 'package:flutter_deer/setting/provider/theme_provider.dart'; import 'package:flutter_deer/store/page/store_audit_page.dart'; import 'package:flutter_deer/store/page/store_audit_result_page.dart'; import 'package:flutter_test/flutter_test.dart'; diff --git a/test_driver/account/account.dart b/test_driver/account/account.dart index dd51c5450..2ea60f83c 100644 --- a/test_driver/account/account.dart +++ b/test_driver/account/account.dart @@ -1,12 +1,13 @@ + import 'package:flutter/material.dart'; -import 'package:flutter_deer/common/common.dart'; import 'package:flutter_deer/home/home_page.dart'; -import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_deer/main.dart'; +import 'package:flutter_deer/res/constant.dart'; +import 'package:flutter_driver/driver_extension.dart'; /// 运行 flutter drive --target=test_driver/account/account.dart void main() { enableFlutterDriverExtension(); Constant.isDriverTest = true; runApp(MyApp(home: const Home())); -} \ No newline at end of file +} diff --git a/test_driver/account/account_test.dart b/test_driver/account/account_test.dart index e4ffc88e8..fc5da86c6 100644 --- a/test_driver/account/account_test.dart +++ b/test_driver/account/account_test.dart @@ -1,3 +1,4 @@ + import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; @@ -6,7 +7,7 @@ import '../tools/test_utils.dart'; void main() { group('账户部分:', () { - FlutterDriver driver; + late FlutterDriver driver; setUpAll(() async { driver = await FlutterDriver.connect(); @@ -18,7 +19,7 @@ void main() { }); tearDownAll(() async { - await driver?.close(); + await driver.close(); }); test('账户流水页测试',() async { @@ -126,6 +127,7 @@ void main() { await delayed(); /// 进入设置页,便于执行设置模块测试操作 await driver.tap(find.byValueKey('setting')); + await delayed(); }); }); -} \ No newline at end of file +} diff --git a/test_driver/driver.dart b/test_driver/driver.dart index 0238785c5..5689d91ff 100644 --- a/test_driver/driver.dart +++ b/test_driver/driver.dart @@ -1,6 +1,7 @@ -import 'package:flutter_deer/common/common.dart'; -import 'package:flutter_driver/driver_extension.dart'; + import 'package:flutter_deer/main.dart' as app; +import 'package:flutter_deer/res/constant.dart'; +import 'package:flutter_driver/driver_extension.dart'; /// 集成测试运行 flutter drive --profile --target=test_driver/driver.dart (模拟器去除--profile) /// 集成测试暂不支持拖动、长按等方式。 https://github.com/flutter/flutter/issues/27552 @@ -9,4 +10,4 @@ void main() { enableFlutterDriverExtension(); Constant.isDriverTest = true; app.main(); -} \ No newline at end of file +} diff --git a/test_driver/driver_test.dart b/test_driver/driver_test.dart index 9f5c4ea7c..22469fb8f 100644 --- a/test_driver/driver_test.dart +++ b/test_driver/driver_test.dart @@ -24,4 +24,4 @@ void main() { shop_test.main(); account_test.main(); setting_test.main(['backHome']); -} \ No newline at end of file +} diff --git a/test_driver/goods/goods.dart b/test_driver/goods/goods.dart index 45d5537ba..cf8b2d329 100644 --- a/test_driver/goods/goods.dart +++ b/test_driver/goods/goods.dart @@ -1,10 +1,13 @@ + import 'package:flutter/material.dart'; import 'package:flutter_deer/home/home_page.dart'; -import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_deer/main.dart'; +import 'package:flutter_deer/res/constant.dart'; +import 'package:flutter_driver/driver_extension.dart'; /// 运行 flutter drive --target=test_driver/goods/goods.dart void main() { enableFlutterDriverExtension(); + Constant.isDriverTest = true; runApp(MyApp(home: const Home())); -} \ No newline at end of file +} diff --git a/test_driver/goods/goods_test.dart b/test_driver/goods/goods_test.dart index 3fd94731f..e5214c87e 100644 --- a/test_driver/goods/goods_test.dart +++ b/test_driver/goods/goods_test.dart @@ -1,3 +1,4 @@ + import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; @@ -6,7 +7,7 @@ import '../tools/test_utils.dart'; void main() { group('商品部分:', () { - FlutterDriver driver; + late FlutterDriver driver; setUpAll(() async { driver = await FlutterDriver.connect(); @@ -18,7 +19,7 @@ void main() { }); tearDownAll(() async { - await driver?.close(); + await driver.close(); }); test('商品页测试',() async { @@ -27,6 +28,7 @@ void main() { await driver.tap(find.text('待售')); await delayed(); final SerializableFinder pageView = find.byValueKey('pageView'); + await driver.waitFor(pageView); await driver.scroll(pageView, 400.0, 0, scrollDuration); await delayed(); @@ -76,13 +78,15 @@ void main() { test('商品规格页测试',() async { await driver.tap(find.text('商品规格')); - await delayed();await delayed(); - await driver.tap(find.byValueKey('hint')); await delayed(); - await driver.tap(find.byValueKey('name_edit')); - await delayed(); - await driver.tap(find.text('取消')); + await driver.tap(find.byValueKey('hint')); await delayed(); + /// 在集成测试中不能点击特定的TextSpan,这里测试部分放在integration_test/goods_test.dart 实现 + /// https://github.com/flutter/flutter/issues/67123 + // await driver.tap(find.byValueKey('name_edit')); + // await delayed(); + // await driver.tap(find.text('取消')); + // await delayed(); await driver.tap(find.byValueKey('2')); await delayed(); await driver.tap(find.byTooltip('Back')); @@ -98,4 +102,4 @@ void main() { await delayed(); }); }); -} \ No newline at end of file +} diff --git a/test_driver/home/splash_page.dart b/test_driver/home/splash_page.dart index 64a14a9d7..a17929e69 100644 --- a/test_driver/home/splash_page.dart +++ b/test_driver/home/splash_page.dart @@ -1,8 +1,9 @@ -import 'package:flutter_driver/driver_extension.dart'; + import 'package:flutter_deer/main.dart' as app; +import 'package:flutter_driver/driver_extension.dart'; /// 运行 flutter drive --target=test_driver/home/splash_page.dart void main() { enableFlutterDriverExtension(); app.main(); -} \ No newline at end of file +} diff --git a/test_driver/home/splash_page_test.dart b/test_driver/home/splash_page_test.dart index ff979e358..44890f30c 100644 --- a/test_driver/home/splash_page_test.dart +++ b/test_driver/home/splash_page_test.dart @@ -1,3 +1,4 @@ + import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; @@ -5,7 +6,7 @@ import '../tools/test_utils.dart'; void main([List args = const []]) { group('引导页:', () { - FlutterDriver driver; + late FlutterDriver driver; // 测试之前连接程序 setUpAll(() async { @@ -19,7 +20,7 @@ void main([List args = const []]) { // 在测试完成后,关闭程序的连接。 tearDownAll(() async { - await driver?.close(); + await driver.close(); }); test('测试引导页滑动',() async { @@ -37,4 +38,4 @@ void main([List args = const []]) { await delayed(); }); }); -} \ No newline at end of file +} diff --git a/test_driver/login/login_page.dart b/test_driver/login/login_page.dart index c0e2b51d1..51776dca0 100644 --- a/test_driver/login/login_page.dart +++ b/test_driver/login/login_page.dart @@ -1,10 +1,11 @@ + import 'package:flutter/material.dart'; import 'package:flutter_deer/login/page/login_page.dart'; -import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_deer/main.dart'; +import 'package:flutter_driver/driver_extension.dart'; /// 运行 flutter drive --target=test_driver/login/login_page.dart void main() { enableFlutterDriverExtension(); runApp(MyApp(home: const LoginPage())); -} \ No newline at end of file +} diff --git a/test_driver/login/login_page_test.dart b/test_driver/login/login_page_test.dart index fe327bada..72afd8c51 100644 --- a/test_driver/login/login_page_test.dart +++ b/test_driver/login/login_page_test.dart @@ -1,3 +1,4 @@ + import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; @@ -6,7 +7,7 @@ import '../tools/test_utils.dart'; void main() { group('登录部分:', () { - FlutterDriver driver; + late FlutterDriver driver; setUpAll(() async { driver = await FlutterDriver.connect(); @@ -18,7 +19,7 @@ void main() { }); tearDownAll(() async { - await driver?.close(); + await driver.close(); }); test('登录页按钮点击',() async { @@ -31,44 +32,54 @@ void main() { await driver.tap(find.byTooltip('Back')); await delayed(); await driver.tap(find.byValueKey('noAccountRegister')); + await delayed(); }); test('注册页测试',() async { await driver.tap(find.byValueKey('getVerificationCode'));/// 无法成功触发事件,需要输入手机号 - + await delayed(); + final SerializableFinder textField = find.byValueKey('phone'); await driver.tap(textField); // 点击输入框,给予焦点 + await delayed(); await driver.enterText('15000000000'); // 输入内容 await delayed(); await driver.tap(find.byValueKey('getVerificationCode')); + await delayed(); final SerializableFinder textField2 = find.byValueKey('vcode'); await driver.tap(textField2); + await delayed(); await driver.enterText('123456'); await delayed(); final SerializableFinder textField3 = find.byValueKey('password'); await driver.tap(textField3); + await delayed(); await driver.enterText('111111'); await delayed(); await driver.tap(find.byValueKey('register')); // 点击注册 - + await delayed(); + // 清除输入框文字 await driver.tap(find.byValueKey('password_delete')); await delayed(); await driver.tap(find.byTooltip('Back')); + await delayed(); }, timeout: const Timeout(Duration(seconds: 30))); test('登录页测试',() async { final SerializableFinder textField = find.byValueKey('phone'); await driver.tap(textField); + await delayed(); await driver.enterText('15000000000'); await delayed(); final SerializableFinder textField2 = find.byValueKey('password'); await driver.tap(textField2); + await delayed(); await driver.enterText('111111'); await delayed(); // 点击密码可见两次 @@ -77,6 +88,7 @@ void main() { await driver.tap(find.byValueKey('password_showPwd')); await delayed(); await driver.tap(find.byValueKey('login')); // 点击登录 + await delayed(); }, timeout: const Timeout(Duration(seconds: 30))); }); -} \ No newline at end of file +} diff --git a/test_driver/order/order.dart b/test_driver/order/order.dart index 179be2a7c..ad7b04bb1 100644 --- a/test_driver/order/order.dart +++ b/test_driver/order/order.dart @@ -1,12 +1,13 @@ + import 'package:flutter/material.dart'; -import 'package:flutter_deer/common/common.dart'; import 'package:flutter_deer/home/home_page.dart'; -import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_deer/main.dart'; +import 'package:flutter_deer/res/constant.dart'; +import 'package:flutter_driver/driver_extension.dart'; /// 运行 flutter drive --target=test_driver/order/order.dart void main() { enableFlutterDriverExtension(); Constant.isDriverTest = true; runApp(MyApp(home: const Home())); -} \ No newline at end of file +} diff --git a/test_driver/order/order_test.dart b/test_driver/order/order_test.dart index 4fb56fc48..c68ad2c42 100644 --- a/test_driver/order/order_test.dart +++ b/test_driver/order/order_test.dart @@ -1,3 +1,4 @@ + import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; @@ -6,7 +7,7 @@ import '../tools/test_utils.dart'; void main() { group('订单部分:', () { - FlutterDriver driver; + late FlutterDriver driver; setUpAll(() async { driver = await FlutterDriver.connect(); @@ -18,12 +19,12 @@ void main() { }); tearDownAll(() async { - await driver?.close(); + await driver.close(); }); test('滑动订单列表',() async { await driver.tap(find.byTooltip('订单')); - + await delayed(); /// 水平滑动 final SerializableFinder pageView = find.byValueKey('pageView'); await driver.scroll(pageView, -400.0, 0, scrollDuration); @@ -54,32 +55,35 @@ void main() { await delayed(); await driver.tap(find.text('确定')); await driver.scroll(find.byValueKey('order_list'), 0.0, 500.0, scrollDuration); + await delayed(); }); - // test('订单详情页',() async { - // final SerializableFinder orderItem = find.byValueKey('order_item_2'); - // await driver.tap(orderItem); - // await driver.tap(find.text('订单跟踪')); - // await delayed(); - // await driver.tap(find.byTooltip('Back')); - // await delayed(); - // await driver.scroll(find.byValueKey('order_info'), 0.0, -1000.0, scrollDuration); - // await delayed(); - // await driver.tap(find.byTooltip('Back')); - // await delayed(); - // }); - // - // test('订单搜索页测试',() async { - // await driver.tap(find.byTooltip('搜索')); - // await driver.tap(find.byValueKey('search_text_field'), timeout: const Duration(minutes: 1),); - // await driver.enterText('flutter'); - // await driver.tap(find.text('搜索')); - // final SerializableFinder orderList = find.byValueKey('order_search_list'); - // await driver.waitFor(orderList, timeout: const Duration(minutes: 1),); - // await driver.scroll(orderList, 0.0, -300.0, scrollDuration); - // await delayed(); - // await driver.tap(find.byValueKey('search_back')); - // await delayed(); - // }, timeout: const Timeout.factor(5)); + test('订单详情页',() async { + final SerializableFinder orderItem = find.byValueKey('order_item_2'); + await driver.tap(orderItem); + await delayed(); + await driver.tap(find.text('订单跟踪')); + await delayed(); + await driver.tap(find.byTooltip('Back')); + await delayed(); + await driver.scroll(find.byValueKey('order_info'), 0.0, -1000.0, scrollDuration); + await delayed(); + await driver.tap(find.byTooltip('Back')); + await delayed(); + }); + + test('订单搜索页测试',() async { + await driver.tap(find.byTooltip('搜索')); + await delayed(); + await driver.tap(find.byValueKey('search_text_field'), timeout: const Duration(minutes: 1),); + await driver.enterText('flutter'); + await driver.tap(find.text('搜索')); + final SerializableFinder orderList = find.byValueKey('order_search_list'); + await driver.waitFor(orderList, timeout: const Duration(minutes: 1),); + await driver.scroll(orderList, 0.0, -300.0, scrollDuration); + await delayed(); + await driver.tap(find.byValueKey('search_back')); + await delayed(); + }, timeout: const Timeout.factor(5)); }); -} \ No newline at end of file +} diff --git a/test_driver/setting/setting.dart b/test_driver/setting/setting.dart index 009c61298..990ea46c9 100644 --- a/test_driver/setting/setting.dart +++ b/test_driver/setting/setting.dart @@ -1,14 +1,17 @@ -import 'package:sp_util/sp_util.dart'; + import 'package:flutter/material.dart'; +import 'package:flutter_deer/main.dart'; +import 'package:flutter_deer/res/constant.dart'; import 'package:flutter_deer/setting/page/setting_page.dart'; import 'package:flutter_driver/driver_extension.dart'; -import 'package:flutter_deer/main.dart'; +import 'package:sp_util/sp_util.dart'; /// 运行 flutter drive --target=test_driver/setting/setting.dart Future main() async { enableFlutterDriverExtension(); + Constant.isDriverTest = true; WidgetsFlutterBinding.ensureInitialized(); /// sp初始化 await SpUtil.getInstance(); runApp(MyApp(home: const SettingPage())); -} \ No newline at end of file +} diff --git a/test_driver/setting/setting_test.dart b/test_driver/setting/setting_test.dart index 9b35cf496..206a04cfb 100644 --- a/test_driver/setting/setting_test.dart +++ b/test_driver/setting/setting_test.dart @@ -1,3 +1,4 @@ + import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; @@ -6,7 +7,7 @@ import '../tools/test_utils.dart'; void main([List args = const []]) { group('设置部分:', () { - FlutterDriver driver; + late FlutterDriver driver; setUpAll(() async { driver = await FlutterDriver.connect(); @@ -18,7 +19,7 @@ void main([List args = const []]) { }); tearDownAll(() async { - await driver?.close(); + await driver.close(); }); test('设置页测试',() async { @@ -51,7 +52,7 @@ void main([List args = const []]) { await driver.tap(find.text('夜间模式')); await delayed(); await driver.tap(find.text('开启')); - await Future.delayed(const Duration(seconds: 2)); + await Future.delayed(const Duration(seconds: 4)); await driver.tap(find.byTooltip('Back')); await delayed(); // 查看效果 @@ -67,6 +68,7 @@ void main([List args = const []]) { await driver.tap(find.byTooltip('店铺')); await delayed(); await driver.tap(find.byValueKey('setting')); + await delayed(); } }); @@ -74,7 +76,7 @@ void main([List args = const []]) { await driver.tap(find.text('多语言')); await delayed(); await driver.tap(find.text('English')); - await Future.delayed(const Duration(seconds: 1)); + await Future.delayed(const Duration(seconds: 2)); await driver.tap(find.byTooltip('Back')); await delayed(); @@ -93,6 +95,7 @@ void main([List args = const []]) { await driver.tap(find.byTooltip('Back')); await delayed(); await driver.tap(find.byValueKey('noAccountRegister')); + await delayed(); }); }); -} \ No newline at end of file +} diff --git a/test_driver/shop/shop.dart b/test_driver/shop/shop.dart index 8580ce5b9..9629d39d7 100644 --- a/test_driver/shop/shop.dart +++ b/test_driver/shop/shop.dart @@ -1,12 +1,13 @@ + import 'package:flutter/material.dart'; -import 'package:flutter_deer/common/common.dart'; import 'package:flutter_deer/home/home_page.dart'; -import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_deer/main.dart'; +import 'package:flutter_deer/res/constant.dart'; +import 'package:flutter_driver/driver_extension.dart'; /// 运行 flutter drive --target=test_driver/shop/shop.dart void main() { enableFlutterDriverExtension(); Constant.isDriverTest = true; runApp(MyApp(home: const Home())); -} \ No newline at end of file +} diff --git a/test_driver/shop/shop_test.dart b/test_driver/shop/shop_test.dart index 79813dff9..dc08ab6fc 100644 --- a/test_driver/shop/shop_test.dart +++ b/test_driver/shop/shop_test.dart @@ -1,3 +1,4 @@ + import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; @@ -6,7 +7,7 @@ import '../tools/test_utils.dart'; void main() { group('店铺部分:', () { - FlutterDriver driver; + late FlutterDriver driver; setUpAll(() async { driver = await FlutterDriver.connect(); @@ -18,7 +19,7 @@ void main() { }); tearDownAll(() async { - await driver?.close(); + await driver.close(); }); test('店铺页测试',() async { @@ -85,4 +86,4 @@ void main() { }); }); -} \ No newline at end of file +} diff --git a/test_driver/statistic/statistic.dart b/test_driver/statistic/statistic.dart index 857ac62f0..7d458621c 100644 --- a/test_driver/statistic/statistic.dart +++ b/test_driver/statistic/statistic.dart @@ -1,10 +1,11 @@ + import 'package:flutter/material.dart'; import 'package:flutter_deer/home/home_page.dart'; -import 'package:flutter_driver/driver_extension.dart'; import 'package:flutter_deer/main.dart'; +import 'package:flutter_driver/driver_extension.dart'; /// 运行 flutter drive --target=test_driver/statistic/statistic.dart void main() { enableFlutterDriverExtension(); runApp(MyApp(home: const Home())); -} \ No newline at end of file +} diff --git a/test_driver/statistic/statistic_test.dart b/test_driver/statistic/statistic_test.dart index 768e5f6da..7e8c3ae24 100644 --- a/test_driver/statistic/statistic_test.dart +++ b/test_driver/statistic/statistic_test.dart @@ -1,3 +1,4 @@ + import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; @@ -6,7 +7,7 @@ import '../tools/test_utils.dart'; void main() { group('统计部分:', () { - FlutterDriver driver; + late FlutterDriver driver; setUpAll(() async { driver = await FlutterDriver.connect(); @@ -18,7 +19,7 @@ void main() { }); tearDownAll(() async { - await driver?.close(); + await driver.close(); }); test('统计页测试',() async { @@ -41,6 +42,7 @@ void main() { await driver.scroll(find.byValueKey('goods_statistics_list'), 0, 300, scrollDuration); await delayed(); await driver.tap(find.byTooltip('Back')); + await delayed(); }); test('订单统计页测试',() async { @@ -53,6 +55,7 @@ void main() { await driver.tap(find.byValueKey('year')); await delayed(); await driver.tap(find.byTooltip('Back')); + await delayed(); }); }); -} \ No newline at end of file +} diff --git a/test_driver/store/store.dart b/test_driver/store/store.dart index 773e2726c..d01a1d8b9 100644 --- a/test_driver/store/store.dart +++ b/test_driver/store/store.dart @@ -1,3 +1,4 @@ + import 'package:flutter/material.dart'; import 'package:flutter_deer/main.dart'; import 'package:flutter_deer/store/page/store_audit_page.dart'; @@ -7,4 +8,4 @@ import 'package:flutter_driver/driver_extension.dart'; void main() { enableFlutterDriverExtension(); runApp(MyApp(home: const StoreAuditPage())); -} \ No newline at end of file +} diff --git a/test_driver/store/store_test.dart b/test_driver/store/store_test.dart index b3e3f471b..d09e517bb 100644 --- a/test_driver/store/store_test.dart +++ b/test_driver/store/store_test.dart @@ -1,3 +1,4 @@ + import 'package:flutter_driver/flutter_driver.dart'; import 'package:test/test.dart'; @@ -6,7 +7,7 @@ import '../tools/test_utils.dart'; void main() { group('审核部分:', () { - FlutterDriver driver; + late FlutterDriver driver; setUpAll(() async { driver = await FlutterDriver.connect(); @@ -18,11 +19,12 @@ void main() { }); tearDownAll(() async { - await driver?.close(); + await driver.close(); }); test('店铺审核资料页测试',() async { await driver.tap(find.text('主营范围')); + await delayed(); final SerializableFinder sortList = find.byValueKey('goods_sort'); await delayed(); @@ -33,11 +35,13 @@ void main() { await delayed(); await driver.tap(find.text('提交')); + await delayed(); }); test('审核结果页测试',() async { await delayed(); await driver.tap(find.text('进入')); + await delayed(); }); }); -} \ No newline at end of file +} diff --git a/test_driver/tools/test_utils.dart b/test_driver/tools/test_utils.dart index b8c7aed91..536b06557 100644 --- a/test_driver/tools/test_utils.dart +++ b/test_driver/tools/test_utils.dart @@ -4,4 +4,4 @@ Future delayed ({int milliseconds = 666}) async { return Future.delayed(Duration(milliseconds: milliseconds)); } -const Duration scrollDuration = Duration(milliseconds: 300); \ No newline at end of file +const Duration scrollDuration = Duration(milliseconds: 300); diff --git a/web/index.html b/web/index.html index 8ede012e4..a116661a1 100644 --- a/web/index.html +++ b/web/index.html @@ -1,35 +1,49 @@ - - - + + + + + - - - - - + + + + + - - + + - flutter_deer - + flutter_deer + - - - - - + + + + + diff --git a/web/index1.html b/web/index1.html new file mode 100644 index 000000000..e45d526fa --- /dev/null +++ b/web/index1.html @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + flutter_deer + + + + + + + + + diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index ddfcf7c32..0d3b5b1ff 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -2,11 +2,22 @@ // Generated file. Do not edit. // +// clang-format off + #include "generated_plugin_registrant.h" -#include +#include +#include +#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { - UrlLauncherPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("UrlLauncherPlugin")); + FileSelectorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSelectorWindows")); + ScreenRetrieverPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); + WindowManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowManagerPlugin")); } diff --git a/windows/flutter/generated_plugin_registrant.h b/windows/flutter/generated_plugin_registrant.h index 9846246b4..dc139d85a 100644 --- a/windows/flutter/generated_plugin_registrant.h +++ b/windows/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/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 411af46dd..c0677f7bc 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,7 +3,13 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_selector_windows + screen_retriever url_launcher_windows + window_manager +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST ) set(PLUGIN_BUNDLED_LIBRARIES) @@ -14,3 +20,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}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc index f266417ca..1cf425438 100644 --- a/windows/runner/Runner.rc +++ b/windows/runner/Runner.rc @@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico" // Version // -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER +#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 +#define VERSION_AS_NUMBER 1,0,0,0 #endif -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION #else #define VERSION_AS_STRING "1.0.0" #endif diff --git a/windows/runner/main.cpp b/windows/runner/main.cpp index 0c33461fb..d50e1317a 100644 --- a/windows/runner/main.cpp +++ b/windows/runner/main.cpp @@ -8,6 +8,14 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, _In_ wchar_t *command_line, _In_ int show_command) { + + // https://leanflutter.org/zh/blog/making-the-app-single-instanced + HWND hwnd = ::FindWindow(L"FLUTTER_RUNNER_WIN32_WINDOW", L"flutter_deer"); + if (hwnd != NULL) { + ::ShowWindow(hwnd, SW_NORMAL); + ::SetForegroundWindow(hwnd); + return EXIT_FAILURE; + } // 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()) {