Repository: simplezhli/flutter_deer
Branch: master
Commit: 94d0620d2825
Files: 347
Total size: 1.5 MB
Directory structure:
gitextract_mfrrwjd0/
├── .github/
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ └── deer-issue-template.md
│ └── workflows/
│ ├── flutter-drive.yml
│ └── flutter-web-deploy.yml
├── .gitignore
├── .metadata
├── LICENSE
├── README-EN.md
├── README.md
├── analysis_options.yaml
├── android/
│ ├── app/
│ │ ├── build.gradle
│ │ ├── key.properties
│ │ ├── proguard-rules.pro
│ │ ├── src/
│ │ │ ├── debug/
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── main/
│ │ │ │ ├── AndroidManifest.xml
│ │ │ │ ├── java/
│ │ │ │ │ └── com/
│ │ │ │ │ └── weilu/
│ │ │ │ │ └── deer/
│ │ │ │ │ ├── DeerPickerProvider.java
│ │ │ │ │ ├── FileProvider7.java
│ │ │ │ │ ├── InstallAPKPlugin.java
│ │ │ │ │ ├── MainActivity.java
│ │ │ │ │ └── MyApp.java
│ │ │ │ └── res/
│ │ │ │ ├── drawable/
│ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ │ └── ic_launcher.xml
│ │ │ │ ├── values/
│ │ │ │ │ ├── colors.xml
│ │ │ │ │ └── styles.xml
│ │ │ │ ├── values-night/
│ │ │ │ │ └── colors.xml
│ │ │ │ ├── values-v27/
│ │ │ │ │ └── styles.xml
│ │ │ │ └── xml/
│ │ │ │ ├── file_paths.xml
│ │ │ │ └── network_security_config.xml
│ │ │ └── profile/
│ │ │ └── AndroidManifest.xml
│ │ └── test.jks
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ └── settings.gradle
├── assets/
│ ├── data/
│ │ ├── bank.json
│ │ ├── bank_2.json
│ │ ├── city.json
│ │ ├── sort_0.json
│ │ ├── sort_1.json
│ │ └── sort_2.json
│ └── lottie/
│ └── bunny_new_mouth.json
├── devtools_options.yaml
├── docs/
│ ├── Android问题汇总.md
│ ├── CHANGELOG.md
│ ├── Web问题汇总.md
│ └── iOS问题汇总.md
├── integration_test/
│ ├── goods_test.dart
│ ├── integration_test.dart
│ └── login_test.dart
├── ios/
│ ├── .gitignore
│ ├── Flutter/
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Runner/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ ├── LaunchImage.imageset/
│ │ │ │ ├── Contents.json
│ │ │ │ └── README.md
│ │ │ └── flutter_dash_black.imageset/
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
│ ├── Runner.xcodeproj/
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace/
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata/
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata/
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
│ └── RunnerTests/
│ └── RunnerTests.swift
├── l10n.yaml
├── lib/
│ ├── account/
│ │ ├── account_router.dart
│ │ ├── models/
│ │ │ ├── bank_entity.dart
│ │ │ ├── city_entity.dart
│ │ │ └── withdrawal_account_model.dart
│ │ ├── page/
│ │ │ ├── account_page.dart
│ │ │ ├── account_record_list_page.dart
│ │ │ ├── add_withdrawal_account_page.dart
│ │ │ ├── bank_select_page.dart
│ │ │ ├── city_select_page.dart
│ │ │ ├── withdrawal_account_list_page.dart
│ │ │ ├── withdrawal_account_page.dart
│ │ │ ├── withdrawal_page.dart
│ │ │ ├── withdrawal_password_page.dart
│ │ │ ├── withdrawal_record_list_page.dart
│ │ │ └── withdrawal_result_page.dart
│ │ └── widgets/
│ │ ├── rise_number_text.dart
│ │ ├── sms_verify_dialog.dart
│ │ ├── withdrawal_account_item.dart
│ │ └── withdrawal_password_setting.dart
│ ├── demo/
│ │ ├── demo_page.dart
│ │ ├── focus/
│ │ │ └── focus_demo_page.dart
│ │ ├── lottie/
│ │ │ ├── bunny.dart
│ │ │ └── lottie_demo.dart
│ │ ├── navigator/
│ │ │ ├── book_entity.dart
│ │ │ ├── books_app_state.dart
│ │ │ ├── books_main.dart
│ │ │ ├── delegate/
│ │ │ │ ├── inner_router_delegate.dart
│ │ │ │ └── router_delegate.dart
│ │ │ ├── parser/
│ │ │ │ └── route_information_parser.dart
│ │ │ └── screen/
│ │ │ ├── app_shell.dart
│ │ │ ├── book_details_screen.dart
│ │ │ ├── books_list_screen.dart
│ │ │ └── setting_screen.dart
│ │ ├── overlay/
│ │ │ ├── bottom_navigation/
│ │ │ │ └── my_bottom_navigation_bar.dart
│ │ │ ├── overlay_main.dart
│ │ │ ├── page/
│ │ │ │ ├── overlay_demo_page.dart
│ │ │ │ └── test_page.dart
│ │ │ └── route/
│ │ │ ├── application.dart
│ │ │ └── my_navigator_observer.dart
│ │ ├── ripple/
│ │ │ └── ripples_animation_page.dart
│ │ ├── scratcher/
│ │ │ └── scratch_card_demo_page.dart
│ │ └── widgets/
│ │ └── neumorphic.dart
│ ├── generated/
│ │ └── json/
│ │ ├── bank_entity.g.dart
│ │ ├── base/
│ │ │ ├── json_convert_content.dart
│ │ │ └── json_field.dart
│ │ ├── city_entity.g.dart
│ │ ├── goods_sort_entity.g.dart
│ │ ├── search_entity.g.dart
│ │ └── user_entity.g.dart
│ ├── goods/
│ │ ├── goods_router.dart
│ │ ├── models/
│ │ │ ├── goods_item_entity.dart
│ │ │ ├── goods_size_model.dart
│ │ │ └── goods_sort_entity.dart
│ │ ├── page/
│ │ │ ├── goods_edit_page.dart
│ │ │ ├── goods_list_page.dart
│ │ │ ├── goods_page.dart
│ │ │ ├── goods_search_page.dart
│ │ │ ├── goods_size_edit_page.dart
│ │ │ ├── goods_size_page.dart
│ │ │ └── qr_code_scanner_page.dart
│ │ ├── provider/
│ │ │ ├── goods_page_provider.dart
│ │ │ └── goods_sort_provider.dart
│ │ └── widgets/
│ │ ├── goods_add_menu.dart
│ │ ├── goods_delete_bottom_sheet.dart
│ │ ├── goods_item.dart
│ │ ├── goods_size_dialog.dart
│ │ ├── goods_sort_bottom_sheet.dart
│ │ ├── goods_sort_menu.dart
│ │ └── menu_reveal.dart
│ ├── home/
│ │ ├── home_page.dart
│ │ ├── provider/
│ │ │ └── home_provider.dart
│ │ ├── splash_page.dart
│ │ └── webview_page.dart
│ ├── l10n/
│ │ ├── deer_localizations.dart
│ │ ├── deer_localizations_en.dart
│ │ ├── deer_localizations_zh.dart
│ │ ├── intl_en.arb
│ │ └── intl_zh.arb
│ ├── login/
│ │ ├── login_router.dart
│ │ ├── page/
│ │ │ ├── login_page.dart
│ │ │ ├── register_page.dart
│ │ │ ├── reset_password_page.dart
│ │ │ ├── sms_login_page.dart
│ │ │ └── update_password_page.dart
│ │ └── widgets/
│ │ └── my_text_field.dart
│ ├── main.dart
│ ├── mvp/
│ │ ├── base_page.dart
│ │ ├── base_page_presenter.dart
│ │ ├── base_presenter.dart
│ │ ├── i_lifecycle.dart
│ │ ├── mvps.dart
│ │ └── power_presenter.dart
│ ├── net/
│ │ ├── base_entity.dart
│ │ ├── dio_utils.dart
│ │ ├── error_handle.dart
│ │ ├── http_api.dart
│ │ ├── intercept.dart
│ │ └── net.dart
│ ├── order/
│ │ ├── iview/
│ │ │ └── order_search_iview.dart
│ │ ├── models/
│ │ │ └── search_entity.dart
│ │ ├── order_router.dart
│ │ ├── page/
│ │ │ ├── order_info_page.dart
│ │ │ ├── order_list_page.dart
│ │ │ ├── order_page.dart
│ │ │ ├── order_search_page.dart
│ │ │ └── order_track_page.dart
│ │ ├── presenter/
│ │ │ └── order_search_presenter.dart
│ │ ├── provider/
│ │ │ ├── base_list_provider.dart
│ │ │ └── order_page_provider.dart
│ │ └── widgets/
│ │ ├── order_item.dart
│ │ ├── order_tag_item.dart
│ │ └── pay_type_dialog.dart
│ ├── res/
│ │ ├── colors.dart
│ │ ├── constant.dart
│ │ ├── dimens.dart
│ │ ├── gaps.dart
│ │ ├── resources.dart
│ │ └── styles.dart
│ ├── routers/
│ │ ├── fluro_navigator.dart
│ │ ├── i_router.dart
│ │ ├── not_found_page.dart
│ │ ├── routers.dart
│ │ └── web_page_transitions.dart
│ ├── setting/
│ │ ├── page/
│ │ │ ├── about_page.dart
│ │ │ ├── account_manager_page.dart
│ │ │ ├── locale_page.dart
│ │ │ ├── setting_page.dart
│ │ │ └── theme_page.dart
│ │ ├── provider/
│ │ │ ├── locale_provider.dart
│ │ │ └── theme_provider.dart
│ │ ├── setting_router.dart
│ │ └── widgets/
│ │ ├── exit_dialog.dart
│ │ └── update_dialog.dart
│ ├── shop/
│ │ ├── iview/
│ │ │ └── shop_iview.dart
│ │ ├── models/
│ │ │ ├── freight_config_model.dart
│ │ │ └── user_entity.dart
│ │ ├── page/
│ │ │ ├── freight_config_page.dart
│ │ │ ├── input_text_page.dart
│ │ │ ├── message_page.dart
│ │ │ ├── select_address_page.dart
│ │ │ ├── shop_page.dart
│ │ │ └── shop_setting_page.dart
│ │ ├── presenter/
│ │ │ └── shop_presenter.dart
│ │ ├── provider/
│ │ │ └── user_provider.dart
│ │ ├── shop_router.dart
│ │ └── widgets/
│ │ ├── pay_type_dialog.dart
│ │ ├── price_input_dialog.dart
│ │ ├── range_price_input_dialog.dart
│ │ └── send_type_dialog.dart
│ ├── statistics/
│ │ ├── page/
│ │ │ ├── goods_statistics_page.dart
│ │ │ ├── order_statistics_page.dart
│ │ │ └── statistics_page.dart
│ │ ├── statistics_router.dart
│ │ └── widgets/
│ │ └── selected_date.dart
│ ├── store/
│ │ ├── page/
│ │ │ ├── store_audit_page.dart
│ │ │ └── store_audit_result_page.dart
│ │ └── store_router.dart
│ ├── util/
│ │ ├── app_navigator.dart
│ │ ├── change_notifier_manage.dart
│ │ ├── date_utils.dart
│ │ ├── device_utils.dart
│ │ ├── handle_error_utils.dart
│ │ ├── image_utils.dart
│ │ ├── input_formatter/
│ │ │ ├── fix_ios_input_formatter.dart
│ │ │ └── number_text_input_formatter.dart
│ │ ├── log_utils.dart
│ │ ├── other_utils.dart
│ │ ├── screen_utils.dart
│ │ ├── theme_utils.dart
│ │ ├── toast_utils.dart
│ │ └── version_utils.dart
│ └── widgets/
│ ├── base_dialog.dart
│ ├── bezier_chart/
│ │ ├── bezier_chart.dart
│ │ ├── bezier_chart_config.dart
│ │ ├── bezier_chart_widget.dart
│ │ ├── bezier_line.dart
│ │ └── my_single_child_scroll_view.dart
│ ├── click_item.dart
│ ├── double_tap_back_exit_app.dart
│ ├── fractionally_aligned_sized_box.dart
│ ├── load_image.dart
│ ├── my_app_bar.dart
│ ├── my_button.dart
│ ├── my_card.dart
│ ├── my_flexible_space_bar.dart
│ ├── my_refresh_list.dart
│ ├── my_scroll_view.dart
│ ├── my_search_bar.dart
│ ├── pie_chart/
│ │ ├── pie_chart.dart
│ │ └── pie_data.dart
│ ├── popup_window.dart
│ ├── progress_dialog.dart
│ ├── selected_image.dart
│ ├── selected_item.dart
│ ├── state_layout.dart
│ └── text_field_item.dart
├── macos/
│ ├── .gitignore
│ ├── Flutter/
│ │ ├── Flutter-Debug.xcconfig
│ │ ├── Flutter-Release.xcconfig
│ │ └── GeneratedPluginRegistrant.swift
│ ├── Podfile
│ ├── Runner/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ └── AppIcon.appiconset/
│ │ │ └── Contents.json
│ │ ├── Base.lproj/
│ │ │ └── MainMenu.xib
│ │ ├── Configs/
│ │ │ ├── AppInfo.xcconfig
│ │ │ ├── Debug.xcconfig
│ │ │ ├── Release.xcconfig
│ │ │ └── Warnings.xcconfig
│ │ ├── DebugProfile.entitlements
│ │ ├── Info.plist
│ │ ├── MainFlutterWindow.swift
│ │ └── Release.entitlements
│ ├── Runner.xcodeproj/
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace/
│ │ │ └── xcshareddata/
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── Runner.xcscheme
│ └── Runner.xcworkspace/
│ ├── contents.xcworkspacedata
│ └── xcshareddata/
│ └── IDEWorkspaceChecks.plist
├── pubspec.yaml
├── shader/
│ ├── README.md
│ └── flutter_01.sksl.json
├── test/
│ ├── accessibility_test.dart
│ ├── account/
│ │ └── account_accessibility_test.dart
│ ├── goods/
│ │ └── goods_accessibility_test.dart
│ ├── login/
│ │ └── login_accessibility_test.dart
│ ├── net/
│ │ └── dio_test.dart
│ ├── order/
│ │ └── order_accessibility_test.dart
│ ├── setting/
│ │ └── setting_accessibility_test.dart
│ ├── shop/
│ │ └── shop_accessibility_test.dart
│ ├── statistics/
│ │ └── statistic_accessibility_test.dart
│ ├── store/
│ │ └── store_accessibility_test.dart
│ └── widget_test.dart
├── test_driver/
│ ├── account/
│ │ ├── account.dart
│ │ └── account_test.dart
│ ├── driver.dart
│ ├── driver_test.dart
│ ├── goods/
│ │ ├── goods.dart
│ │ └── goods_test.dart
│ ├── home/
│ │ ├── splash_page.dart
│ │ └── splash_page_test.dart
│ ├── login/
│ │ ├── login_page.dart
│ │ └── login_page_test.dart
│ ├── order/
│ │ ├── order.dart
│ │ └── order_test.dart
│ ├── setting/
│ │ ├── setting.dart
│ │ └── setting_test.dart
│ ├── shop/
│ │ ├── shop.dart
│ │ └── shop_test.dart
│ ├── statistic/
│ │ ├── statistic.dart
│ │ └── statistic_test.dart
│ ├── store/
│ │ ├── store.dart
│ │ └── store_test.dart
│ └── tools/
│ └── test_utils.dart
├── web/
│ ├── index.html
│ ├── index1.html
│ └── manifest.json
└── windows/
├── .gitignore
├── CMakeLists.txt
├── flutter/
│ ├── CMakeLists.txt
│ ├── generated_plugin_registrant.cc
│ ├── generated_plugin_registrant.h
│ └── generated_plugins.cmake
└── runner/
├── CMakeLists.txt
├── Runner.rc
├── flutter_window.cpp
├── flutter_window.h
├── main.cpp
├── resource.h
├── run_loop.cpp
├── run_loop.h
├── runner.exe.manifest
├── utils.cpp
├── utils.h
├── win32_window.cpp
└── win32_window.h
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://github.com/simplezhli/flutter_deer/blob/master/preview/jikeshijian.jpg
================================================
FILE: .github/ISSUE_TEMPLATE/deer-issue-template.md
================================================
---
name: deer issue template
about: 请确保使用最新的代码并详细阅读了readme和查找了已有的issue,避免提出重复的问题。
title: ''
labels: ''
assignees: ''
---
### 运行环境 ###
- [x] 电脑系统:如:`Windows 11`
- [x] 设备型号:如:`小米13`
- [x] 设备系统版本:如 `Android 13`
- [x] Flutter 版本:如 `3.13.0`
### 具体问题描述 ###
#### 问题截图 ####
#### 异常日志 ####
================================================
FILE: .github/workflows/flutter-drive.yml
================================================
# https://medium.com/flutter-community/run-flutter-driver-tests-on-github-actions-13c639c7e4ab
# https://github.com/nazarcybulskij/CI_CD_Flutter_Demo
# https://weilu.blog.csdn.net/article/details/114744416
# Name of your workflow.
name: flutter_deer driver
# Trigger the workflow on push or pull request.
on: [push, pull_request]
# A workflow run is made up of one or more jobs.
jobs:
# id of job, a string that is unique to the "jobs" node above.
drive_ios:
# Creates a build matrix for your jobs. You can define different
# variations of an environment to run each job in.
strategy:
# A set of different configurations of the virtual environment.
matrix:
device:
- "iPhone 17 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
# Contains a sequence of tasks.
steps:
# A name for your step to display on GitHub.
# - 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"
# 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@v3
# Sets up a flutter environment.
# https://github.com/marketplace/actions/flutter-action
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.41.0'
channel: 'stable' # or: 'dev' or 'beta'
- run: "flutter clean"
- name: "Run Flutter Driver tests"
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.
runs-on: macos-latest
# creates a build matrix for your jobs
strategy:
# set of different configurations of the virtual environment.
matrix:
api-level: [35]
target: [google_apis]
steps:
- 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: '3.41.0'
channel: 'stable' # or: 'dev' or 'beta'
- name: "Run Flutter Driver tests"
# GitHub Action for installing, configuring and running Android Emulators (work only Mac OS)
# https://github.com/marketplace/actions/android-emulator-runner
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
arch: arm64-v8a
profile: Nexus 6
script: "flutter drive --target=test_driver/driver.dart"
accessibility_test:
#The type of machine to run the job on. [windows,macos, ubuntu , self-hosted]
runs-on: macos-latest
#sequence of tasks called
steps:
# The branch or tag ref that triggered the workflow will be checked out.
# https://github.com/actions/checkout
- uses: actions/checkout@v3
# Setup a flutter environment.
# https://github.com/marketplace/actions/flutter-action
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.41.0'
channel: 'stable'
- run: "flutter pub get"
- name: "Run Flutter Accessibility Tests"
run: "flutter test test/accessibility_test.dart"
================================================
FILE: .github/workflows/flutter-web-deploy.yml
================================================
name: flutter_deer web deploy
# push 提交中修改`pubspec.yaml`触发此workflow。
# 为了避免每次部署,这里使用一个不存在的文件名。
on:
push:
paths:
- 'pubspec1.yaml'
jobs:
web_build_and_deploy:
runs-on: macos-latest
steps:
- uses: actions/checkout@v2.3.1
- uses: subosito/flutter-action@v2
with:
flutter-version: '3.35.1'
channel: 'stable'
architecture: x64
- name: "Web Build 🔧"
run: |
flutter pub get
flutter build web
- name: "Web Deploy 🚀"
# https://github.com/JamesIves/github-pages-deploy-action
uses: JamesIves/github-pages-deploy-action@4.0.0
with:
token: '${{ secrets.GITHUB_TOKEN }}'
branch: gh-pages
folder: build/web
================================================
FILE: .gitignore
================================================
# Miscellaneous
*.class
*.log
*.pyc
*.swp
*.lock
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# Visual Studio Code related
.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
**/android/app/.cxx
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
**/ios/Flutter/flutter_export_environment.sh
**/ios/Flutter/.last_build_id
# Web related
lib/generated_plugin_registrant.dart
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
/ios/build/
================================================
FILE: .metadata
================================================
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "b45fa18946ecc2d9b4009952c636ba7e2ffbb787"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
base_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
- platform: ios
create_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
base_revision: b45fa18946ecc2d9b4009952c636ba7e2ffbb787
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README-EN.md
================================================
# Flutter Deer
## English | [中文](README.md)
This project is an exercise in learning Flutter for personal growth and development.
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
Some of the page effects are as follows:
|  |  |  |  |
| :--------------------------------: | :---------------------------------: | :-------------------------------: | :-------------------------------: |
|  |  |  |  |
|  |  |  |  |
|  |  |  |  |
|  |  |  |  |
|  |  |  |  |
|  |  |  | |
**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
* QR code scanning functionality (using the qr_code_scanner plugin)
* Menu switching animations (circular expansion, 3D flip)
* Swipe-to-delete
* City selection
* Three-level linkage selection similar to JD's city selection
* Various custom dialogs
* Sticky header for lists
* Password input keyboard
* 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:
For the Android version, kindly click on the link provided: [Download here](https://www.pgyer.com/oEm8me), and enter the password: `111111`.
As for iOS, you will need to download and run the code on your own.
For web experience, please visit: https://simplezhli.github.io/flutter_deer/
## The project's operational environment.
[](https://github.com/simplezhli/flutter_deer/actions/workflows/flutter-drive.yml)
1. Flutter version 3.41.0
2. Dart version 3.11.0
## Precautions to be taken.
- 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 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 certain plugin limitations, this project is only available for preview on Windows and macOS. Those interested may run and experience it themselves.
- 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.
- This project uses the [FlutterJsonBeanFactory](https://github.com/zhangruiyu/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
- [Flutter开发中的一些Tips(一)](https://weilu.blog.csdn.net/article/details/90546727)
- [Flutter开发中的一些Tips(二)](https://weilu.blog.csdn.net/article/details/94849020)
- [Flutter开发中的一些Tips(三)](https://weilu.blog.csdn.net/article/details/100108123)
- [Flutter适配深色模式(DarkMode)](https://weilu.blog.csdn.net/article/details/102531559)
- [说说Flutter中的RepaintBoundary](https://weilu.blog.csdn.net/article/details/103452637)
- [说说Flutter中的Semantics](https://weilu.blog.csdn.net/article/details/103823259)
- [说说Flutter中最熟悉的陌生人 —— Key](https://weilu.blog.csdn.net/article/details/104745624)
- [说说Flutter中的无名英雄 —— Focus](https://weilu.blog.csdn.net/article/details/107132031)
- [Flutter性能优化实践 —— UI篇](https://weilu.blog.csdn.net/article/details/106046434)
- [玩玩Flutter的拖拽——实现一款万能遥控器](https://weilu.blog.csdn.net/article/details/105237677)
- [玩玩Flutter Web —— 实现高德地图插件](https://weilu.blog.csdn.net/article/details/106465792)
- [在GitHub Actions上进行Flutter 的测试和部署](https://weilu.blog.csdn.net/article/details/114744416)
- [Flutter动画曲线Curves 效果一览](https://weilu.blog.csdn.net/article/details/95632571)
- [Flutter状态管理之Riverpod](https://weilu.blog.csdn.net/article/details/108352306)
- [【译】正确操作Dart中的字符串](https://weilu.blog.csdn.net/article/details/107857569)
- [【译】学习Flutter中新的Navigator和Router系统](https://weilu.blog.csdn.net/article/details/108902282)
## Tripartite library used
| library | Functionality |
| -------------------------- | --------------- |
| [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 map from Amap** |
| [cached_network_image](https://github.com/renefloor/flutter_cached_network_image) | **Image loading** |
| [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) | **Handling keyboard events** |
| [azlistview](https://github.com/flutterchina/azlistview) | **City selection list** |
| [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) | **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.
## Plan:
* [x] Web support.
* [x] Migrate to null-safety.
* [ ] Migrate to Navigator 2.0.
## Thanks For
- [flutter_wanandroid](https://github.com/Sky24n/flutter_wanandroid)
## License
Copyright 2019 simplezhli
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# Flutter Deer
## [English](README-EN.md) | 中文
本项目为个人学习Flutter的练习项目。
通过设置、修改、组合自带部件以及自定义来实现具体的设计效果,满足日常开发的需求。
本项目设计图见design目录,你可以通过我提供的设计图有目标的去练习。所有的实现仅是个人的学习理解,如果有更好的实现方案欢迎交流。
## 预览
部分页面效果如下:
|  |  |  |  |
| :--------------------------------: | :---------------------------------: | :-------------------------------: | :-------------------------------: |
|  |  |  |  |
|  |  |  |  |
|  |  |  |  |
|  |  |  |  |
|  |  |  |  |
|  |  |  | |
**觉得还可以的话,来个Star、Fork支持一波!本项目持续维护中,有问题欢迎提Issue。**
## 实现内容(已迁移到空安全)
* mvp模式
* 使用`provider` (6.x 版本)做状态管理
* 基于`dio` (5.x 版本)的网络请求封装
* 完整的集成测试、可访问性测试。
* 支持深色模式
* 本地化(感谢 @ghedwards)
* 使用`Sliver` 系列组件实现复杂滚动效果
* 使用高德地图定位选择地址(支持Web)
* 通用Widget的处理封装
* 下拉刷新 + 上拉加载更多
* 应用检查更新
* PopupWindow
* 扫码功能(qr_code_scanner插件)
* 菜单切换动画(圆形扩散、3D翻转)
* 侧滑删除
* 城市选择
* 类似京东选择城市的三级联动
* 各种自定义Dialog
* 列表头部吸顶
* 密码输入键盘
* 验证码输入框
* 自定义简易日历
* 曲线图及[饼状图](https://dartpad.cn/d06f8f737d6eb2d87978eb2d14b87864)
* 模块化路由管理
* 更多Demo(水波纹动画、刮刮卡、lottie)
* 更多的细节优化
具体可以下载体验:
Android版安装包:[点击去下载](https://github.com/simplezhli/flutter_deer/releases)。
iOS需要自行下载代码运行。
Web体验地址:https://simplezhli.github.io/flutter_deer/
## 项目运行环境
[](https://github.com/simplezhli/flutter_deer/actions/workflows/flutter-drive.yml)
1. Flutter version 3.41.0
2. Dart version 3.11.0
## 注意事项
- `debug`模式下会有部分卡顿现象,这属于正常现象。良好的体验需要打`release` 包。
iOS可以执行命令`flutter build ios` 以创建`release`版本。
Android可以执行命令`flutter build apk` 以创建`release`版本。
- 项目运行有问题可以在[iOS问题汇总](./docs/iOS问题汇总.md)、[Android问题汇总](./docs/Android问题汇总.md)中尝试寻找解决办法。
- 由于部分插件的原因,本项目在Windows、macOS仅做预览(主要为原生功能方面,UI问题不大)。有兴趣的可自行运行体验。
- 可以执行集成测试命令`flutter drive --target=test_driver/driver.dart` 查看功能演示。
- 因为页面有点多,一开始可能会导致页面无法与设计图对应上。我在代码注释中有添加设计图的相对路径,可以搜索或查找到对应页面,希望对你有帮助。
- 本项目使用[FlutterJsonBeanFactory](https://github.com/zhangruiyu/FlutterJsonBeanFactory)插件来生成Bean。
- Web受制于js等资源过大和部署在Github上,访问会慢一些。
## 心得总结(推荐阅读)
- [Flutter开发中的一些Tips(一)](https://weilu.blog.csdn.net/article/details/90546727)
- [Flutter开发中的一些Tips(二)](https://weilu.blog.csdn.net/article/details/94849020)
- [Flutter开发中的一些Tips(三)](https://weilu.blog.csdn.net/article/details/100108123)
- [Flutter适配深色模式(DarkMode)](https://weilu.blog.csdn.net/article/details/102531559)
- [说说Flutter中的RepaintBoundary](https://weilu.blog.csdn.net/article/details/103452637)
- [说说Flutter中的Semantics](https://weilu.blog.csdn.net/article/details/103823259)
- [说说Flutter中最熟悉的陌生人 —— Key](https://weilu.blog.csdn.net/article/details/104745624)
- [说说Flutter中的无名英雄 —— Focus](https://weilu.blog.csdn.net/article/details/107132031)
- [Flutter性能优化实践 —— UI篇](https://weilu.blog.csdn.net/article/details/106046434)
- [玩玩Flutter的拖拽——实现一款万能遥控器](https://weilu.blog.csdn.net/article/details/105237677)
- [玩玩Flutter Web —— 实现高德地图插件](https://weilu.blog.csdn.net/article/details/106465792)
- [在GitHub Actions上进行Flutter 的测试和部署](https://weilu.blog.csdn.net/article/details/114744416)
- [Flutter动画曲线Curves 效果一览](https://weilu.blog.csdn.net/article/details/95632571)
- [Flutter状态管理之Riverpod](https://weilu.blog.csdn.net/article/details/108352306)
- [【译】正确操作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/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) | **图片加载** |
| [fluro](https://github.com/theyakka/fluro) | **路由管理** |
| [flutter_oktoast](https://github.com/OpenFlutter/flutter_oktoast) | **Toast** |
| [common_utils](https://github.com/Sky24n/common_utils) | **Dart 常用工具类库** |
| [flutter_slidable](https://github.com/letsar/flutter_slidable) | **侧滑删除** |
| [flustars](https://github.com/Sky24n/flustars) | **Flutter 常用工具类库** |
| [flutter_swiper](https://github.com/best-flutter/flutter_swiper) | **Flutter 轮播组件** |
| [url_launcher](https://github.com/flutter/plugins/tree/master/packages/url_launcher) | **启动URL的插件** |
| [image_picker](https://github.com/flutter/plugins/tree/master/packages/image_picker) | **图片选择插件** |
| [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) | **处理键盘事件** |
| [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)文件
## 后续计划:
* [x] 添加地图功能,具体实现插件见 [flutter_2d_amap](https://github.com/simplezhli/flutter_2d_amap)
* [x] 下拉刷新 + 上拉加载更多
* [x] 引入状态管理,预计使用 [provider](https://github.com/rrousselGit/provider)
* [x] 页面添加设计图路径注释,方便寻找对应的设计图。
* [x] 添加集成测试。
* [x] 深色模式支持。
* [x] 添加`Semantics`(语义)
* [x] Web端支持。
* [x] 迁移到空安全。(安装包减少135KB,10.3M -> 10.1M)
* [ ] 迁移至Navigator 2.0。
## 已知存在问题:
- 部分使用的到的三方库没有适配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))。
- 其他历史问题见docs目录下的问题汇总。
## Thanks For
- [flutter_wanandroid](https://github.com/Sky24n/flutter_wanandroid)
## License
Copyright 2019 simplezhli
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
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,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: analysis_options.yaml
================================================
# Specify analysis options.
#
# 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)
# - 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 for code in the flutter/flutter
# repository.
analyzer:
language:
strict-casts: true
strict-inference: true
strict-raw-types: true
errors:
# 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
# Turned off until null-safe rollout is complete.
# 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
- "lib/l10n/**"
- "lib/generated/json/**"
- "lib/widgets/bezier_chart/**"
# - "test/**"
- "test_driver/**"
formatter:
page_width: 100
linter:
rules:
# 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_specify_types
# - always_use_package_imports # we do this commonly
- annotate_overrides
- annotate_redeclares
# - avoid_annotating_with_dynamic # conflicts with always_specify_types
- avoid_bool_literals_in_conditional_expressions
# - 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
- avoid_dynamic_calls
- avoid_empty_else
- avoid_equals_and_hash_code_on_mutable_classes
- avoid_escaping_inner_quotes
- avoid_field_initializers_in_const_classes
# - 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
# - 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 # 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
- avoid_relative_lib_imports
- avoid_renaming_method_parameters
- avoid_return_types_on_setters
- avoid_returning_null_for_void
# - 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
- avoid_types_as_parameter_names
# - avoid_types_on_closure_parameters # conflicts with always_specify_types
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters
- avoid_void_async
# - 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 # 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
- 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 # 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
# - 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
- literal_only_boolean_expressions
- missing_code_block_language_in_doc_comment
- missing_whitespace_between_adjacent_strings
- no_adjacent_strings_in_list
- 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 # this does get disabled in a few places where we have legacy code that uses strings et al
- overridden_fields
- 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 # not required by flutter style
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_const_constructors
- prefer_const_constructors_in_immutables
- prefer_const_declarations
- prefer_const_literals_to_create_immutables
# - prefer_constructors_over_static_methods # far too many false positives
- prefer_contains
# - prefer_double_quotes # opposite of prefer_single_quotes
# - 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
- prefer_generic_function_type_aliases
- prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators
- prefer_initializing_formals
- prefer_inlined_adds
# - 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 # 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
- prefer_void_to_null
- 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
- sort_constructors_first
# - sort_pub_dependencies # prevents separating pinned transitive dependencies
- sort_unnamed_constructors_first
- test_types_in_equals
- throw_in_finally
- tighten_type_of_initializing_formals
# - type_annotate_public_apis # subset of always_specify_types
- type_init_formals
- 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_aware_operator_on_extension_on_nullable
- unnecessary_null_checks
- unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_overrides
- unnecessary_parenthesis
# - 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
- 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
- use_is_even_rather_than_modulo
- use_key_in_widget_constructors
- use_late_for_private_fields_and_variables
- use_named_constants
- use_raw_strings
- use_rethrow_when_possible
- 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
================================================
FILE: android/app/build.gradle
================================================
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()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
def keystorePropertiesFile = rootProject.file("app/key.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
compileSdkVersion 36
ndkVersion = "28.2.13676358"
defaultConfig {
applicationId "com.weilu.deer"
minSdkVersion flutter.minSdkVersion
targetSdkVersion 35
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
debug {
signingConfig signingConfigs.release
}
release {
// flutter build apk
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.release
// https://github.com/flutter/flutter/issues/47462 #48015
// #58967 gradle 4.0.0
shrinkResources false
zipAlignEnabled false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
namespace 'com.weilu.deer'
lint {
disable 'InvalidPackage'
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.0.21"
}
================================================
FILE: android/app/key.properties
================================================
storePassword=111111
keyPassword=111111
keyAlias=key
storeFile=../app/test.jks
================================================
FILE: android/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#定位
-keep class com.amap.api.location.**{*;}
-keep class com.amap.api.fence.**{*;}
-keep class com.loc.**{*;}
-keep class com.autonavi.aps.amapapi.model.**{*;}
#搜索
-keep class com.amap.api.services.**{*;}
#2D地图
-keep class com.amap.api.maps2d.**{*;}
-keep class com.amap.api.mapcore2d.**{*;}
================================================
FILE: android/app/src/debug/AndroidManifest.xml
================================================
================================================
FILE: android/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: android/app/src/main/java/com/weilu/deer/DeerPickerProvider.java
================================================
package com.weilu.deer;
import androidx.core.content.FileProvider;
/**
* @author weilu
* 作者:weilu on 2019/8/08 15:15
*/
public class DeerPickerProvider extends FileProvider {
}
================================================
FILE: android/app/src/main/java/com/weilu/deer/FileProvider7.java
================================================
package com.weilu.deer;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import java.io.File;
import androidx.core.content.FileProvider;
/**
* @author weilu
* 作者:weilu on 2017/6/20 14:44
*/
public class FileProvider7 {
public static Uri getUriForFile(Context context, File file) {
Uri fileUri = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
fileUri = getUriForFile24(context, file);
} else {
fileUri = Uri.fromFile(file);
}
return fileUri;
}
private static Uri getUriForFile24(Context context, File file) {
return FileProvider.getUriForFile(context, context.getPackageName() + ".Deer", file);
}
public static void setIntentDataAndType(Context context, Intent intent, String type, File file, boolean writeAble) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setDataAndType(getUriForFile(context, file), type);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (writeAble) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
} else {
intent.setDataAndType(Uri.fromFile(file), type);
}
}
public static void setIntentData(Context context, Intent intent, File file, boolean writeAble) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setData(getUriForFile(context, file));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
if (writeAble) {
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
} else {
intent.setData(Uri.fromFile(file));
}
}
}
================================================
FILE: android/app/src/main/java/com/weilu/deer/InstallAPKPlugin.java
================================================
package com.weilu.deer;
import android.app.Activity;
import android.content.Intent;
import java.io.File;
import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodChannel;
/**
* @author weilu
*/
public class InstallAPKPlugin implements FlutterPlugin {
private MethodChannel channel;
private final Activity mActivity;
public InstallAPKPlugin(Activity activity) {
this.mActivity = activity;
}
@Override
public void onAttachedToEngine(@NonNull FlutterPlugin.FlutterPluginBinding binding) {
setupMethodChannel(binding.getBinaryMessenger());
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPlugin.FlutterPluginBinding binding) {
tearDownChannel();
}
private void setupMethodChannel(BinaryMessenger messenger) {
channel = new MethodChannel(messenger, "version");
channel.setMethodCallHandler((methodCall, result) -> {
if ("install".equals(methodCall.method)) {
String path = methodCall.argument("path");
openFile(path);
} else {
result.notImplemented();
}
});
}
/**
* 安装 文件(APK)
*/
private void openFile(String path) {
Intent intents = new Intent();
intents.setAction(Intent.ACTION_VIEW);
intents.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
FileProvider7.setIntentDataAndType(mActivity, intents, "application/vnd.android.package-archive", new File(path), false);
mActivity.startActivity(intents);
}
private void tearDownChannel() {
channel.setMethodCallHandler(null);
channel = null;
}
}
================================================
FILE: android/app/src/main/java/com/weilu/deer/MainActivity.java
================================================
package com.weilu.deer;
import android.graphics.Color;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
/**
* @author weilu
*/
public class MainActivity extends FlutterActivity {
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
flutterEngine.getPlugins().add(new InstallAPKPlugin(this));
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/// 设置状态栏透明,导航栏沉浸。
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
getWindow().setStatusBarColor(Color.TRANSPARENT);
}
}
================================================
FILE: android/app/src/main/java/com/weilu/deer/MyApp.java
================================================
package com.weilu.deer;
import android.app.Application;
/**
* @Description:
* @Author: weilu
* @Time: 2019/8/5 0005 17:08.
*/
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
}
}
================================================
FILE: android/app/src/main/res/drawable/launch_background.xml
================================================
================================================
FILE: android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: android/app/src/main/res/values/colors.xml
================================================
#FFFFFFFF
================================================
FILE: android/app/src/main/res/values/styles.xml
================================================
================================================
FILE: android/app/src/main/res/values-night/colors.xml
================================================
#FF18191A
================================================
FILE: android/app/src/main/res/values-v27/styles.xml
================================================
================================================
FILE: android/app/src/main/res/xml/file_paths.xml
================================================
================================================
FILE: android/app/src/main/res/xml/network_security_config.xml
================================================
================================================
FILE: android/app/src/profile/AndroidManifest.xml
================================================
================================================
FILE: android/build.gradle
================================================
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' }
google()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
================================================
FILE: android/gradle/wrapper/gradle-wrapper.properties
================================================
#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
================================================
FILE: android/gradle.properties
================================================
android.useAndroidX=true
android.enableJetifier=true
# 提升编译速度配置 https://blog.csdn.net/weixin_33943347/article/details/91361727
org.gradle.daemon=true
org.gradle.configureondemand=true
org.gradle.parallel=true
org.gradle.jvmargs=-Xmx3072m -XX:+UseParallelGC -XX:MaxPermSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# 开启gradle缓存
org.gradle.caching=true
#开启 kotlin 的增量和并行编译
kotlin.incremental=true
kotlin.incremental.java=true
kotlin.incremental.js=true
kotlin.caching.enabled=true
kotlin.parallel.tasks.in.project=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=false
android.nonFinalResIds=false
================================================
FILE: android/settings.gradle
================================================
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
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version '8.12.3' apply false
id "org.jetbrains.kotlin.android" version "2.0.21" apply false
}
include ":app"
================================================
FILE: assets/data/bank.json
================================================
[
{
"id": 1,
"bankName": "民生银行",
"firstLetter": "M"
},
{
"id": 2,
"bankName": "工商银行",
"firstLetter": "G"
},
{
"id": 3,
"bankName": "农业银行",
"firstLetter": "N"
},
{
"id": 4,
"bankName": "中国银行",
"firstLetter": "Z"
},
{
"id": 5,
"bankName": "建设银行",
"firstLetter": "J"
},
{
"id": 6,
"bankName": "交通银行",
"firstLetter": "J"
},
{
"id": 7,
"bankName": "中信银行",
"firstLetter": "Z"
},
{
"id": 8,
"bankName": "招商银行",
"firstLetter": "Z"
},
{
"id": 9,
"bankName": "兴业银行",
"firstLetter": "X"
},
{
"id": 10,
"bankName": "浦发银行",
"firstLetter": "P"
},
{
"id": 16,
"bankName": "光大银行",
"firstLetter": "G"
},
{
"id": 17,
"bankName": "华夏银行",
"firstLetter": "H"
},
{
"id": 18,
"bankName": "广发银行",
"firstLetter": "G"
},
{
"id": 19,
"bankName": "平安银行",
"firstLetter": "P"
},
{
"id": 20,
"bankName": "北京银行",
"firstLetter": "B"
},
{
"id": 43,
"bankName": "上海银行",
"firstLetter": "S"
},
{
"id": 44,
"bankName": "南京银行",
"firstLetter": "N"
},
{
"id": 48,
"bankName": "杭州银行",
"firstLetter": "H"
},
{
"id": 49,
"bankName": "宁波银行",
"firstLetter": "N"
},
{
"id": 54,
"bankName": "浙江稠州商业银行",
"firstLetter": "Z"
},
{
"id": 83,
"bankName": "汉口银行",
"firstLetter": "H"
},
{
"id": 84,
"bankName": "长沙银行",
"firstLetter": "C"
},
{
"id": 116,
"bankName": "浙商银行",
"firstLetter": "Z"
},
{
"id": 118,
"bankName": "渤海银行",
"firstLetter": "B"
},
{
"id": 127,
"bankName": "上海农商银行",
"firstLetter": "S"
},
{
"id": 128,
"bankName": "北京农商行",
"firstLetter": "B"
},
{
"id": 143,
"bankName": "中国邮储银行",
"firstLetter": "Y"
}
]
================================================
FILE: assets/data/bank_2.json
================================================
[
{
"id": 13035,
"bankName": "西安市城南支行",
"firstLetter": "X"
},
{
"id": 13036,
"bankName": "西安经济技术开发区支行",
"firstLetter": "X"
},
{
"id": 13037,
"bankName": "西安市东新街支行",
"firstLetter": "X"
},
{
"id": 13038,
"bankName": "西安南大街支行",
"firstLetter": "X"
},
{
"id": 13039,
"bankName": "西安市解放路支行",
"firstLetter": "X"
},
{
"id": 13040,
"bankName": "西安市太华路支行",
"firstLetter": "X"
},
{
"id": 13041,
"bankName": "西安民乐园支行",
"firstLetter": "X"
},
{
"id": 13042,
"bankName": "西安市东大街支行",
"firstLetter": "X"
},
{
"id": 13043,
"bankName": "西安南院门支行",
"firstLetter": "X"
},
{
"id": 13044,
"bankName": "西安东关支行",
"firstLetter": "X"
},
{
"id": 13045,
"bankName": "西安和平路支行",
"firstLetter": "X"
},
{
"id": 13046,
"bankName": "西安互助路支行",
"firstLetter": "X"
},
{
"id": 13047,
"bankName": "西安市北大街支行",
"firstLetter": "X"
},
{
"id": 13048,
"bankName": "西安西大街支行",
"firstLetter": "X"
},
{
"id": 13049,
"bankName": "西安星火路支行",
"firstLetter": "X"
},
{
"id": 13050,
"bankName": "西安市南关支行",
"firstLetter": "X"
},
{
"id": 13051,
"bankName": "西安小寨支行",
"firstLetter": "X"
},
{
"id": 13052,
"bankName": "西安雁塔路支行",
"firstLetter": "X"
},
{
"id": 13053,
"bankName": "西安含光路支行",
"firstLetter": "X"
},
{
"id": 13054,
"bankName": "西安铁路局支行",
"firstLetter": "X"
},
{
"id": 13055,
"bankName": "西安电子工业区支行",
"firstLetter": "X"
},
{
"id": 13056,
"bankName": "西安大雁塔支行",
"firstLetter": "X"
},
{
"id": 13057,
"bankName": "西安市土门支行",
"firstLetter": "X"
},
{
"id": 13058,
"bankName": "西安市纺织城支行",
"firstLetter": "X"
},
{
"id": 13059,
"bankName": "西安市韩森寨支行",
"firstLetter": "X"
},
{
"id": 13060,
"bankName": "西安咸宁路支行",
"firstLetter": "X"
},
{
"id": 13061,
"bankName": "西安市韩森寨支行万寿路分理处",
"firstLetter": "X"
},
{
"id": 13062,
"bankName": "西安市阎良区支行",
"firstLetter": "X"
},
{
"id": 13063,
"bankName": "西安市周至县支行",
"firstLetter": "X"
},
{
"id": 13064,
"bankName": "西安市户县支行",
"firstLetter": "X"
},
{
"id": 13065,
"bankName": "西安市临潼区支行",
"firstLetter": "X"
},
{
"id": 13066,
"bankName": "西安市高新技术开发区支行",
"firstLetter": "X"
},
{
"id": 13067,
"bankName": "西安劳动南路支行",
"firstLetter": "X"
},
{
"id": 13068,
"bankName": "西安市未央支行",
"firstLetter": "X"
},
{
"id": 13069,
"bankName": "西安徐家湾支行",
"firstLetter": "X"
},
{
"id": 17731,
"bankName": "西安莲湖路支行",
"firstLetter": "X"
},
{
"id": 17747,
"bankName": "陕西省西安凤城八路分理处",
"firstLetter": "S"
},
{
"id": 17748,
"bankName": "西安长乐中路支行",
"firstLetter": "X"
},
{
"id": 17749,
"bankNumber": "102791013020",
"bankName": "陕西省西安咸宁中路支行",
"firstLetter": "S"
},
{
"id": 17750,
"bankName": "西安万寿南路分理处",
"firstLetter": "X"
},
{
"id": 17759,
"bankName": "陕西省西安紫薇田园都市分理处",
"firstLetter": "S"
}
]
================================================
FILE: assets/data/city.json
================================================
[
{
"name": "阿拉善盟",
"cityCode": "0483",
"firstCharacter": "A"
},
{
"name": "鞍山市",
"cityCode": "0412",
"firstCharacter": "A"
},
{
"name": "安庆市",
"cityCode": "0556",
"firstCharacter": "A"
},
{
"name": "安阳市",
"cityCode": "0372",
"firstCharacter": "A"
},
{
"name": "阿坝藏族羌族自治州",
"cityCode": "0837",
"firstCharacter": "A"
},
{
"name": "安顺市",
"cityCode": "0853",
"firstCharacter": "A"
},
{
"name": "安康市",
"cityCode": "0915",
"firstCharacter": "A"
},
{
"name": "阿克苏地区",
"cityCode": "0997",
"firstCharacter": "A"
},
{
"name": "阿勒泰地区",
"cityCode": "0906",
"firstCharacter": "A"
},
{
"name": "北京市",
"cityCode": "010",
"firstCharacter": "B"
},
{
"name": "保定市",
"cityCode": "0312",
"firstCharacter": "B"
},
{
"name": "包头市",
"cityCode": "0472",
"firstCharacter": "B"
},
{
"name": "巴彦淖尔市",
"cityCode": "0478",
"firstCharacter": "B"
},
{
"name": "本溪市",
"cityCode": "0414",
"firstCharacter": "B"
},
{
"name": "白山市",
"cityCode": "0439",
"firstCharacter": "B"
},
{
"name": "白城市",
"cityCode": "0436",
"firstCharacter": "B"
},
{
"name": "蚌埠市",
"cityCode": "0552",
"firstCharacter": "B"
},
{
"name": "亳州市",
"cityCode": "0558",
"firstCharacter": "B"
},
{
"name": "滨州市",
"cityCode": "0543",
"firstCharacter": "B"
},
{
"name": "北海市",
"cityCode": "0779",
"firstCharacter": "B"
},
{
"name": "百色市",
"cityCode": "0776",
"firstCharacter": "B"
},
{
"name": "巴中市",
"cityCode": "0827",
"firstCharacter": "B"
},
{
"name": "毕节市",
"cityCode": "0857",
"firstCharacter": "B"
},
{
"name": "保山市",
"cityCode": "0875",
"firstCharacter": "B"
},
{
"name": "宝鸡市",
"cityCode": "0917",
"firstCharacter": "B"
},
{
"name": "白银市",
"cityCode": "0943",
"firstCharacter": "B"
},
{
"name": "博尔塔拉蒙古自治州",
"cityCode": "0909",
"firstCharacter": "B"
},
{
"name": "巴音郭楞蒙古自治州",
"cityCode": "0996",
"firstCharacter": "B"
},
{
"name": "承德市",
"cityCode": "0314",
"firstCharacter": "C"
},
{
"name": "沧州市",
"cityCode": "0317",
"firstCharacter": "C"
},
{
"name": "长治市",
"cityCode": "0355",
"firstCharacter": "C"
},
{
"name": "赤峰市",
"cityCode": "0476",
"firstCharacter": "C"
},
{
"name": "朝阳市",
"cityCode": "0421",
"firstCharacter": "C"
},
{
"name": "长春市",
"cityCode": "0431",
"firstCharacter": "C"
},
{
"name": "常州市",
"cityCode": "0519",
"firstCharacter": "C"
},
{
"name": "滁州市",
"cityCode": "0550",
"firstCharacter": "C"
},
{
"name": "池州市",
"cityCode": "0566",
"firstCharacter": "C"
},
{
"name": "长沙市",
"cityCode": "0731",
"firstCharacter": "C"
},
{
"name": "常德市",
"cityCode": "0736",
"firstCharacter": "C"
},
{
"name": "潮州市",
"cityCode": "0768",
"firstCharacter": "C"
},
{
"name": "崇左市",
"cityCode": "0771",
"firstCharacter": "C"
},
{
"name": "重庆市",
"cityCode": "023",
"firstCharacter": "C"
},
{
"name": "成都市",
"cityCode": "028",
"firstCharacter": "C"
},
{
"name": "楚雄彝族自治州",
"cityCode": "0878",
"firstCharacter": "C"
},
{
"name": "昌吉回族自治州",
"cityCode": "0994",
"firstCharacter": "C"
},
{
"name": "嘉义市",
"cityCode": "05",
"firstCharacter": "C"
},
{
"name": "彰化县",
"cityCode": "04",
"firstCharacter": "C"
},
{
"name": "嘉义县",
"cityCode": "05",
"firstCharacter": "C"
},
{
"name": "大同市",
"cityCode": "0352",
"firstCharacter": "D"
},
{
"name": "大连市",
"cityCode": "0411",
"firstCharacter": "D"
},
{
"name": "丹东市",
"cityCode": "0415",
"firstCharacter": "D"
},
{
"name": "大庆市",
"cityCode": "0459",
"firstCharacter": "D"
},
{
"name": "德州市",
"cityCode": "0534",
"firstCharacter": "D"
},
{
"name": "东莞市",
"cityCode": "0769",
"firstCharacter": "D"
},
{
"name": "德阳市",
"cityCode": "0838",
"firstCharacter": "D"
},
{
"name": "达州市",
"cityCode": "0818",
"firstCharacter": "D"
},
{
"name": "大理白族自治州",
"cityCode": "0872",
"firstCharacter": "D"
},
{
"name": "德宏傣族景颇族自治州",
"cityCode": "0692",
"firstCharacter": "D"
},
{
"name": "迪庆藏族自治州",
"cityCode": "0887",
"firstCharacter": "D"
},
{
"name": "定西市",
"cityCode": "0932",
"firstCharacter": "D"
},
{
"name": "鄂州市",
"cityCode": "0711",
"firstCharacter": "E"
},
{
"name": "恩施土家族苗族自治州",
"cityCode": "0718",
"firstCharacter": "E"
},
{
"name": "抚顺市",
"cityCode": "024",
"firstCharacter": "F"
},
{
"name": "阜阳市",
"cityCode": "0558",
"firstCharacter": "F"
},
{
"name": "福州市",
"cityCode": "0591",
"firstCharacter": "F"
},
{
"name": "抚州市",
"cityCode": "0794",
"firstCharacter": "F"
},
{
"name": "佛山市",
"cityCode": "0757",
"firstCharacter": "F"
},
{
"name": "防城港市",
"cityCode": "0770",
"firstCharacter": "F"
},
{
"name": "赣州市",
"cityCode": "0797",
"firstCharacter": "G"
},
{
"name": "广州市",
"cityCode": "020",
"firstCharacter": "G"
},
{
"name": "桂林市",
"cityCode": "0773",
"firstCharacter": "G"
},
{
"name": "贵港市",
"cityCode": "0775",
"firstCharacter": "G"
},
{
"name": "广元市",
"cityCode": "0839",
"firstCharacter": "G"
},
{
"name": "广安市",
"cityCode": "0826",
"firstCharacter": "G"
},
{
"name": "贵阳市",
"cityCode": "0851",
"firstCharacter": "G"
},
{
"name": "甘南藏族自治州",
"cityCode": "0941",
"firstCharacter": "G"
},
{
"name": "邯郸市",
"cityCode": "0310",
"firstCharacter": "H"
},
{
"name": "衡水市",
"cityCode": "0318",
"firstCharacter": "H"
},
{
"name": "呼和浩特市",
"cityCode": "0471",
"firstCharacter": "H"
},
{
"name": "呼伦贝尔市",
"cityCode": "0470",
"firstCharacter": "H"
},
{
"name": "兴安盟",
"cityCode": "0482",
"firstCharacter": "H"
},
{
"name": "葫芦岛市",
"cityCode": "0429",
"firstCharacter": "H"
},
{
"name": "哈尔滨市",
"cityCode": "0451",
"firstCharacter": "H"
},
{
"name": "鹤岗市",
"cityCode": "0468",
"firstCharacter": "H"
},
{
"name": "黑河市",
"cityCode": "0456",
"firstCharacter": "H"
},
{
"name": "淮安市",
"cityCode": "0517",
"firstCharacter": "H"
},
{
"name": "杭州市",
"cityCode": "0571",
"firstCharacter": "H"
},
{
"name": "湖州市",
"cityCode": "0572",
"firstCharacter": "H"
},
{
"name": "合肥市",
"cityCode": "0551",
"firstCharacter": "H"
},
{
"name": "淮南市",
"cityCode": "0554",
"firstCharacter": "H"
},
{
"name": "淮北市",
"cityCode": "0561",
"firstCharacter": "H"
},
{
"name": "黄山市",
"cityCode": "0559",
"firstCharacter": "H"
},
{
"name": "菏泽市",
"cityCode": "0530",
"firstCharacter": "H"
},
{
"name": "鹤壁市",
"cityCode": "0392",
"firstCharacter": "H"
},
{
"name": "黄石市",
"cityCode": "0714",
"firstCharacter": "H"
},
{
"name": "黄冈市",
"cityCode": "0713",
"firstCharacter": "H"
},
{
"name": "衡阳市",
"cityCode": "0734",
"firstCharacter": "H"
},
{
"name": "怀化市",
"cityCode": "0745",
"firstCharacter": "H"
},
{
"name": "海口市",
"cityCode": "0898",
"firstCharacter": "H"
},
{
"name": "汉中市",
"cityCode": "0916",
"firstCharacter": "H"
},
{
"name": "伊犁哈萨克自治州",
"cityCode": "0999",
"firstCharacter": "I"
},
{
"name": "晋城市",
"cityCode": "0356",
"firstCharacter": "J"
},
{
"name": "晋中市",
"cityCode": "0354",
"firstCharacter": "J"
},
{
"name": "吉林市",
"cityCode": "0432",
"firstCharacter": "J"
},
{
"name": "金华市",
"cityCode": "0579",
"firstCharacter": "J"
},
{
"name": "济南市",
"cityCode": "0531",
"firstCharacter": "J"
},
{
"name": "焦作市",
"cityCode": "0391",
"firstCharacter": "J"
},
{
"name": "开封市",
"cityCode": "0378",
"firstCharacter": "K"
},
{
"name": "昆明市",
"cityCode": "0871",
"firstCharacter": "K"
},
{
"name": "克孜勒苏柯尔克孜自治州",
"cityCode": "0908",
"firstCharacter": "K"
},
{
"name": "九龙",
"cityCode": "00852",
"firstCharacter": "K"
},
{
"name": "临汾市",
"cityCode": "0357",
"firstCharacter": "L"
},
{
"name": "丽水市",
"cityCode": "0578",
"firstCharacter": "L"
},
{
"name": "临沂市",
"cityCode": "0539",
"firstCharacter": "L"
},
{
"name": "洛阳市",
"cityCode": "0379",
"firstCharacter": "L"
},
{
"name": "拉萨市",
"cityCode": "0891",
"firstCharacter": "L"
},
{
"name": "牡丹江市",
"cityCode": "0453",
"firstCharacter": "M"
},
{
"name": "马鞍山市",
"cityCode": "0555",
"firstCharacter": "M"
},
{
"name": "茂名市",
"cityCode": "0668",
"firstCharacter": "M"
},
{
"name": "南京市",
"cityCode": "025",
"firstCharacter": "N"
},
{
"name": "南通市",
"cityCode": "0513",
"firstCharacter": "N"
},
{
"name": "宁波市",
"cityCode": "0574",
"firstCharacter": "N"
},
{
"name": "莆田市",
"cityCode": "0594",
"firstCharacter": "P"
},
{
"name": "平顶山市",
"cityCode": "0375",
"firstCharacter": "P"
},
{
"name": "衢州市",
"cityCode": "0570",
"firstCharacter": "Q"
},
{
"name": "泉州市",
"cityCode": "0595",
"firstCharacter": "Q"
},
{
"name": "青岛市",
"cityCode": "0532",
"firstCharacter": "Q"
},
{
"name": "庆阳市",
"cityCode": "0934",
"firstCharacter": "Q"
},
{
"name": "日照市",
"cityCode": "0633",
"firstCharacter": "R"
},
{
"name": "石家庄市",
"cityCode": "0311",
"firstCharacter": "S"
},
{
"name": "朔州市",
"cityCode": "0349",
"firstCharacter": "S"
},
{
"name": "沈阳市",
"cityCode": "024",
"firstCharacter": "S"
},
{
"name": "苏州市",
"cityCode": "0512",
"firstCharacter": "S"
},
{
"name": "十堰市",
"cityCode": "0719",
"firstCharacter": "S"
},
{
"name": "三沙市",
"cityCode": "0898",
"firstCharacter": "S"
},
{
"name": "石嘴山市",
"cityCode": "0952",
"firstCharacter": "S"
},
{
"name": "天津市",
"cityCode": "022",
"firstCharacter": "T"
},
{
"name": "唐山市",
"cityCode": "0315",
"firstCharacter": "T"
},
{
"name": "太原市",
"cityCode": "0351",
"firstCharacter": "T"
},
{
"name": "台州市",
"cityCode": "0576",
"firstCharacter": "T"
},
{
"name": "吐鲁番地区",
"cityCode": "0995",
"firstCharacter": "T"
},
{
"name": "乌兰察布市",
"cityCode": "0474",
"firstCharacter": "W"
},
{
"name": "乌鲁木齐市",
"cityCode": "0991",
"firstCharacter": "W"
},
{
"name": "潍坊市",
"cityCode": "0536",
"firstCharacter": "W"
},
{
"name": "威海市",
"cityCode": "0631",
"firstCharacter": "W"
},
{
"name": "武汉市",
"cityCode": "0022222",
"firstCharacter": "W"
},
{
"name": "邢台市",
"cityCode": "0319",
"firstCharacter": "X"
},
{
"name": "忻州市",
"cityCode": "0350",
"firstCharacter": "X"
},
{
"name": "信阳市",
"cityCode": "0376",
"firstCharacter": "X"
},
{
"name": "阳泉市",
"cityCode": "0353",
"firstCharacter": "Y"
},
{
"name": "运城市",
"cityCode": "0359",
"firstCharacter": "Y"
},
{
"name": "营口市",
"cityCode": "0417",
"firstCharacter": "Y"
},
{
"name": "宜昌市",
"cityCode": "0717",
"firstCharacter": "Y"
},
{
"name": "岳阳市",
"cityCode": "0730",
"firstCharacter": "Y"
},
{
"name": "玉溪市",
"cityCode": "0877",
"firstCharacter": "Y"
},
{
"name": "舟山群岛新区",
"cityCode": "0580",
"firstCharacter": "Z"
},
{
"name": "郑州市",
"cityCode": "0371",
"firstCharacter": "Z"
},
{
"name": "肇庆市",
"cityCode": "0758",
"firstCharacter": "Z"
},
{
"name": "张掖市",
"cityCode": "0936",
"firstCharacter": "Z"
}
]
================================================
FILE: assets/data/sort_0.json
================================================
[
{
"id": "1",
"name": "超市便利"
},
{
"id": "2",
"name": "生鲜果蔬"
},
{
"id": "3",
"name": "零食小吃"
},
{
"id": "4",
"name": "美食餐饮"
},
{
"id": "5",
"name": "鲜花烘培"
},
{
"id": "6",
"name": "生活服务"
},
{
"id": "7",
"name": "其他"
},
{
"id": "8",
"name": "综合"
},
{
"id": "10",
"name": "美容个护"
},
{
"id": "11",
"name": "家居生活"
},
{
"id": "12",
"name": "服饰箱包"
},
{
"id": "13",
"name": "母婴玩具"
},
{
"id": "15",
"name": "海淘进口"
},
{
"id": "755",
"name": "快递代收"
},
{
"id": "756",
"name": "食品保健"
},
{
"id": "764",
"name": "家居生活"
},
{
"id": "769",
"name": "米面杂粮"
},
{
"id": "786",
"name": "水果生鲜"
},
{
"id": "807",
"name": "社区健身"
},
{
"id": "811",
"name": "艺术礼品"
},
{
"id": "814",
"name": "今日特卖"
},
{
"id": "816",
"name": "周边旅游"
},
{
"id": "820",
"name": "家装建材"
},
{
"id": "823",
"name": "虚拟商品"
},
{
"id": "14057",
"name": "生活用品"
},
{
"id": "14181",
"name": "手机数码"
}
]
================================================
FILE: assets/data/sort_1.json
================================================
[
{
"id": "15677",
"name": "厨房用具"
},
{
"id": "15690",
"name": "精美餐具"
},
{
"id": "15698",
"name": "家纺"
},
{
"id": "15717",
"name": "家具"
},
{
"id": "15740",
"name": "灯具"
},
{
"id": "15755",
"name": "生活日用"
},
{
"id": "15765",
"name": "宠物用品"
},
{
"id": "15773",
"name": "家装建材"
},
{
"id": "15795",
"name": "赠品"
},
{
"id": "15797",
"name": "家装软饰"
},
{
"id": "15814",
"name": "收纳用品"
},
{
"id": "26541",
"name": "演出票务"
},
{
"id": "26551",
"name": "健康体检"
},
{
"id": "26554",
"name": "教育培训"
},
{
"id": "26561",
"name": "汽车保养"
},
{
"id": "26563",
"name": "影视会员"
},
{
"id": "26565",
"name": "摄影、摄像"
}
]
================================================
FILE: assets/data/sort_2.json
================================================
[
{
"id": "15691",
"name": "酒具/杯具"
},
{
"id": "15692",
"name": "水具"
},
{
"id": "15693",
"name": "筷勺/刀叉"
},
{
"id": "15694",
"name": "碗碟"
},
{
"id": "15695",
"name": "组合套装"
},
{
"id": "15696",
"name": "美食工具"
},
{
"id": "15697",
"name": "茶具/咖啡具"
}
]
================================================
FILE: assets/lottie/bunny_new_mouth.json
================================================
{
"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
}
================================================
FILE: devtools_options.yaml
================================================
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
================================================
FILE: docs/Android问题汇总.md
================================================
# Android 问题汇总
## Could not resolve io.flutter:flutter_embedding_xxx
### 大致异常如下:
```java
Could not resolve all files for configuration ':app:debugCompileClasspath'.
> Could not resolve io.flutter:flutter_embedding_debug:1.0.0-6bc433c6b6b5b98dcf4cc11aff31cdee90849f32.
...
```
### 解决方法:
修改`flutter\packages\flutter_tools\gradle`下的`flutter.gradle`文件。
替换`MAVEN_REPO`为: http://download.flutter.io
或者修改flutter项目`android`目录的`build.gradle`文件,在`repositories`中添加:
```java
maven {
url `http://download.flutter.io`
}
```
参考: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/
```
## 历史问题
- 3.10.0已知问题(~~#124546~~ ~~#126560~~ ~~#131319~~ ~~#73388~~)。
- 1.22.0已知问题(~~#67262~~ ~~#67213~~)。
- 1.17.0已知问题(~~#25767~~ ~~#47191~~)。
- 1.12.13已知问题(~~#47804~~ ~~#47270~~ ~~#47635~~ ~~#47137~~ ~~#47462~~ ~~#47021~~ ~~#39494~~)。
- 1.12.13已修复。~~在1.9.1上,TextField在语言环境为中文时,[光标与输入文字不居中显示](https://github.com/flutter/flutter/issues/40248),可暂时使用`textBaseline: TextBaseline.alphabetic` 处理此问提。~~
- 1.9.1已支持,使用`keyboardType: TextInputType.visiblePassword`即可。~~输入框在不设置`obscureText`属性的情况下(false),[无法弹出密码模式键盘](https://github.com/flutter/flutter/issues/31738),可暂时使用`BlacklistingTextInputFormatter`去除可能会输入的中文。~~
================================================
FILE: docs/CHANGELOG.md
================================================
# 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
* 密码键盘添加输入振动。
* 迁移到新的本地化方案。
* 设置页添加Deer Web入口。
* 适配dio 4.0版本。
* 更换扫码插件。
## 1.1.5
* Web支持基本完成。
* 迁移废弃的FlatButton为TextButton。
* 添加多语言切换。
* Flutter SDK升至2.0.0。
## 1.1.4
* 添加navigator示例。
* 添加金额数字输入限制。
* Flutter SDK升至1.22.3。
## 1.1.3
* 设置页添加Demo入口。
* 代码优化。
## 1.1.2
* 编译版本Flutter 1.17.3。
* 避免动画执行打断。
* unfocus方式调整。
* 扫码插件更新。
* 适配Android 10。
## 1.1.1
* logo更换。
* 添加Android 8.0自适应图标。
* 动画、代码优化。
## 1.1.0
* 提现账号页添加3D翻转动画。
* 侧滑删除操作优化。
* PageView 滑动卡顿优化。
* 添加accessibility test。
## 1.0.9
* 文字大小不受手机系统设置影响。
* 商品分类选择状态保留。
* Flutter版本升至1.12.13+hotfix.8。
* 优化布局,提升性能。
## 1.0.8
* 启动页适配深色模式。
* 更好的无障碍支持。
## 1.0.7
* 优化商品列表
================================================
FILE: docs/Web问题汇总.md
================================================
# Web 问题汇总(flutter 2.10.3)
## service worker
flutter 2.2中新的`service worker`加载机制目前发现兼容不够,部分浏览器无法正常工作。(web/index1.html)
## CanvasKit渲染(默认PC浏览器)
### 中文文字、表情等加载延迟导致乱码现象
主要原因是完整的字体表情包太大,不能是一次加载完成,按需加载过程导致此类现象。
具体问题跟进可以关注:
- [[web] Emojis take a few seconds to render on canvaskit ](https://github.com/flutter/flutter/issues/76248)
- [[Web] [CanvasKit][Feature Request]: Load fonts as soon as detecting browser locale](https://github.com/flutter/flutter/issues/77023)
## 指定渲染引擎
```
flutter run -d chrome --release --web-renderer html
// 或
flutter run -d chrome --release --web-renderer canvaskit
```
> 总结:HTML渲染相较于CanvasKit渲染,UI还原度差一些,但综合性能相对较好。
## 已解决问题
### 使用`Transform`。
在变换Widget时,添加的`LinearGradient`没有渐变效果。。。(例子见lib/account/widgets/withdrawal_account_item.dart)
目前处理方法是添加`RepaintBoundary`。
### 按钮的大小在移动端与Web端不同
可以`ThemeData`中全局指定`visualDensity`属性为`VisualDensity.standard`。
详情见[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)
================================================
FILE: docs/iOS问题汇总.md
================================================
# iOS 问题汇总
## CDN: trunk Repo update failed
### 问题如下:
```java
[!] CDN: trunk Repo update failed - xx error(s):
CDN: trunk URL couldn't be downloaded: https://raw.githubusercontent.com/CocoaPods/Specs/master/Specs/...
...
```
### 原因:
CocoaPods 1.8后将CDN切换为默认的spec repo源。
### 解决方法:
1.Podfile文件中添加source源:
```
source 'https://github.com/CocoaPods/Specs.git'
```
或者指定为国内的镜像:
```
source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'
```
2.执行`pod repo remove trunk`移除trunk源。
参考:https://www.jianshu.com/p/bf1cbe49cb5d
## CDN: trunk URL couldn't be downloaded
在host文件中添加:
```
151.101.76.133 raw.githubusercontent.com
```
如果这个ip无效,可以根据这个[查询真实IP](https://www.cnblogs.com/ljcgood66/p/12852044.html)自行查询。
参考:https://www.ioiox.com/archives/62.html
https://mirrors.tuna.tsinghua.edu.cn/help/CocoaPods/
## 历史问题
- [~~iOS使用Let's Encript证书,App卡死~~](https://github.com/flutterchina/dio/issues/703#issuecomment-748737446)
- 1.17.0已知问题(~~#38323~~ ~~#47191~~)。
- 1.17.0已修复。~~在iOS手机上开启深色模式时,[无法将状态栏文字修改为黑色](https://github.com/flutter/flutter/issues/41067)。~~
================================================
FILE: integration_test/goods_test.dart
================================================
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.
});
}
================================================
FILE: integration_test/integration_test.dart
================================================
import 'package:integration_test/integration_test_driver.dart';
Future main() => integrationDriver();
================================================
FILE: integration_test/login_test.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_deer/login/page/login_page.dart';
import 'package:flutter_deer/login/page/register_page.dart';
import 'package:flutter_deer/main.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/login_test.dart
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('登录部分:', () {
tearDown(() {
debugPrint('< Success');
});
testWidgets('登录页按钮点击',(WidgetTester tester) async {
runApp(MyApp(home: const LoginPage()));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('actionName')));
await tester.pumpAndSettle();
await tester.tap(find.byTooltip('Back'));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('forgotPassword')));
await tester.pumpAndSettle();
await tester.tap(find.byTooltip('Back'));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('noAccountRegister')));
});
testWidgets('注册页测试',(WidgetTester tester) async {
runApp(MyApp(home: const RegisterPage()));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('getVerificationCode')));/// 无法成功触发事件,需要输入手机号
final Finder textField = find.byKey(const Key('phone'));
await tester.enterText(textField, '15000000000'); // 输入内容
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('getVerificationCode')));
final Finder textField2 = find.byKey(const Key('vcode'));
await tester.enterText(textField2, '123456');
await tester.pumpAndSettle();
final Finder textField3 = find.byKey(const Key('password'));
await tester.enterText(textField3, '111111');
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('register'))); // 点击注册
// 清除输入框文字
await tester.pumpAndSettle();
expect(find.text('111111'), findsOneWidget);
await tester.tap(find.byKey(const Key('password_delete')));
expect(find.text('111111'), findsNothing);
await tester.pumpAndSettle();
await tester.tap(find.byTooltip('Back'));
}, timeout: const Timeout(Duration(seconds: 30)));
testWidgets('登录页测试',(WidgetTester tester) async {
runApp(MyApp(home: const LoginPage()));
await tester.pumpAndSettle();
final Finder textField = find.byKey(const Key('phone'));
await tester.enterText(textField, '15000000000');
await tester.pumpAndSettle();
final Finder textField2 = find.byKey(const Key('password'));
await tester.enterText(textField2, '111111');
await tester.pumpAndSettle();
// 点击密码可见两次
await tester.tap(find.byKey(const Key('password_showPwd')));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('password_showPwd')));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('login'))); // 点击登录
}, timeout: const Timeout(Duration(seconds: 30)));
});
}
================================================
FILE: ios/.gitignore
================================================
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
================================================
FILE: ios/Flutter/AppFrameworkInfo.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleExecutable
App
CFBundleIdentifier
io.flutter.flutter.app
CFBundleInfoDictionaryVersion
6.0
CFBundleName
App
CFBundlePackageType
FMWK
CFBundleShortVersionString
1.0
CFBundleSignature
????
CFBundleVersion
1.0
================================================
FILE: ios/Flutter/Debug.xcconfig
================================================
#include "Generated.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
================================================
FILE: ios/Flutter/Release.xcconfig
================================================
#include "Generated.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
================================================
FILE: ios/Podfile
================================================
# Uncomment this line to define a global platform for your project
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'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks! :linkage => :static
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
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 < 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
================================================
FILE: ios/Runner/AppDelegate.swift
================================================
import UIKit
import Flutter
@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 versionChannel = FlutterMethodChannel(name: "version", binaryMessenger: controller as! FlutterBinaryMessenger)
versionChannel.setMethodCallHandler { (call, result) in
if "jumpAppStore" == call.method {
let urlString = "https://itunes.apple.com/us/app/id444934666"
let url = URL(string: urlString)
if UIApplication.shared.canOpenURL(url!) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(url!, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(url!)
}
}
} else {
result(FlutterMethodNotImplemented)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
================================================
FILE: ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
================================================
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
================================================
FILE: ios/Runner/Assets.xcassets/flutter_dash_black.imageset/Contents.json
================================================
{
"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"
}
}
================================================
FILE: ios/Runner/Base.lproj/LaunchScreen.storyboard
================================================
================================================
FILE: ios/Runner/Base.lproj/Main.storyboard
================================================
================================================
FILE: ios/Runner/Info.plist
================================================
CFBundleAllowMixedLocalizations
CFBundleDevelopmentRegion
zh_CN
CFBundleLocalizations
en
zh_CN
NSCameraUsageDescription
“Flutter Deer”请求在您使用期间获取您的相机权限,使用扫描条形码等功能
NSLocationWhenInUseUsageDescription
“Flutter Deer”请求在您使用期间获取定位权限,这将带来更好的用户体验
NSPhotoLibraryUsageDescription
“Flutter Deer”请求在您使用期间获取您的相册权限
io.flutter.embedded_views_preview
CFBundleDevelopmentRegion
en
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
Flutter Deer
CFBundlePackageType
APPL
CFBundleShortVersionString
$(FLUTTER_BUILD_NAME)
CFBundleSignature
????
CFBundleVersion
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
Main
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
UISupportedInterfaceOrientations~ipad
UIInterfaceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown
UIViewControllerBasedStatusBarAppearance
CADisableMinimumFrameDurationOnPhone
UIApplicationSupportsIndirectInputEvents
================================================
FILE: ios/Runner/Runner-Bridging-Header.h
================================================
#import "GeneratedPluginRegistrant.h"
================================================
FILE: ios/Runner.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
28CA5DF32ED2502B09C1B5B5 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 23E83CCE70FB1DC8687346B7 /* Pods_RunnerTests.framework */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
CEE44467A522F07798BADB83 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7482934083A212EB1C22ECB5 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
100B55C9A72F2B3AC421762C /* 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 = ""; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
23E83CCE70FB1DC8687346B7 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
7482934083A212EB1C22ECB5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
8ECD0D1A9223A475629BEA03 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
B0BE96799F1FD60D31676059 /* 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 = ""; };
C0E376174B5BC02BA30538F1 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
DCFD1919E54EE05891C3C64B /* 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 = ""; };
F470C531425BBD2CC373C47A /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
CEE44467A522F07798BADB83 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
F70BD244216EA273847021A6 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
28CA5DF32ED2502B09C1B5B5 /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2549A0620386D486F08127CA /* Pods */ = {
isa = PBXGroup;
children = (
DCFD1919E54EE05891C3C64B /* Pods-Runner.debug.xcconfig */,
100B55C9A72F2B3AC421762C /* Pods-Runner.release.xcconfig */,
B0BE96799F1FD60D31676059 /* Pods-Runner.profile.xcconfig */,
F470C531425BBD2CC373C47A /* Pods-RunnerTests.debug.xcconfig */,
C0E376174B5BC02BA30538F1 /* Pods-RunnerTests.release.xcconfig */,
8ECD0D1A9223A475629BEA03 /* Pods-RunnerTests.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "";
};
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
2549A0620386D486F08127CA /* Pods */,
B512157F1DA3852EEAB02C39 /* Frameworks */,
);
sourceTree = "";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "";
};
B512157F1DA3852EEAB02C39 /* Frameworks */ = {
isa = PBXGroup;
children = (
7482934083A212EB1C22ECB5 /* Pods_Runner.framework */,
23E83CCE70FB1DC8687346B7 /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
F213357E348FD666B41F58DB /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
F70BD244216EA273847021A6 /* Frameworks */,
);
buildRules = (
);
dependencies = (
331C8086294A63A400263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
D9FB969B7990732944954166 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
04431B192DCA9096E0D9DFE8 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C807F294A63A400263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
04431B192DCA9096E0D9DFE8 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
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";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
D9FB969B7990732944954166 /* [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;
};
F213357E348FD666B41F58DB /* [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-RunnerTests-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 */
331C807D294A63A400263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.weilu.flutterDeer;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = F470C531425BBD2CC373C47A /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.weilu.flutterDeer.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = C0E376174B5BC02BA30538F1 /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.weilu.flutterDeer.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 8ECD0D1A9223A475629BEA03 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.weilu.flutterDeer.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.weilu.flutterDeer;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.weilu.flutterDeer;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
IDEDidComputeMac32BitWarning
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
PreviewsEnabled
================================================
FILE: ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
================================================
================================================
FILE: ios/Runner.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
IDEDidComputeMac32BitWarning
================================================
FILE: ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
PreviewsEnabled
================================================
FILE: ios/RunnerTests/RunnerTests.swift
================================================
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}
================================================
FILE: l10n.yaml
================================================
# 文档: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
output-class: DeerLocalizations
use-deferred-loading: false
================================================
FILE: lib/account/account_router.dart
================================================
import 'package:fluro/fluro.dart';
import 'package:flutter_deer/routers/i_router.dart';
import 'page/account_page.dart';
import 'page/account_record_list_page.dart';
import 'page/add_withdrawal_account_page.dart';
import 'page/bank_select_page.dart';
import 'page/city_select_page.dart';
import 'page/withdrawal_account_list_page.dart';
import 'page/withdrawal_account_page.dart';
import 'page/withdrawal_page.dart';
import 'page/withdrawal_password_page.dart';
import 'page/withdrawal_record_list_page.dart';
import 'page/withdrawal_result_page.dart';
class AccountRouter implements IRouterProvider{
static String accountPage = '/account';
static String accountRecordListPage = '/account/recordList';
static String addWithdrawalAccountPage = '/account/addWithdrawal';
static String bankSelectPage = '/account/bankSelect';
static String citySelectPage = '/account/citySelect';
static String withdrawalAccountListPage = '/account/withdrawalAccountList';
static String withdrawalAccountPage = '/account/withdrawalAccount';
static String withdrawalPage = '/account/withdrawal';
static String withdrawalPasswordPage = '/account/withdrawalPassword';
static String withdrawalRecordListPage = '/account/withdrawalRecordList';
static String withdrawalResultPage = '/account/withdrawalResult';
@override
void initRouter(FluroRouter router) {
router.define(accountPage, handler: Handler(handlerFunc: (_, __) => const AccountPage()));
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 ?? '0');
return BankSelectPage(type: type);
}));
router.define(citySelectPage, handler: Handler(handlerFunc: (_, __) => const CitySelectPage()));
router.define(withdrawalAccountListPage, handler: Handler(handlerFunc: (_, __) => const WithdrawalAccountListPage()));
router.define(withdrawalAccountPage, handler: Handler(handlerFunc: (_, __) => const WithdrawalAccountPage()));
router.define(withdrawalPage, handler: Handler(handlerFunc: (_, __) => const WithdrawalPage()));
router.define(withdrawalPasswordPage, handler: Handler(handlerFunc: (_, __) => const WithdrawalPasswordPage()));
router.define(withdrawalRecordListPage, handler: Handler(handlerFunc: (_, __) => const WithdrawalRecordListPage()));
router.define(withdrawalResultPage, handler: Handler(handlerFunc: (_, __) => const WithdrawalResultPage()));
}
}
================================================
FILE: lib/account/models/bank_entity.dart
================================================
import 'package:azlistview/azlistview.dart';
import 'package:flutter_deer/generated/json/bank_entity.g.dart';
import 'package:flutter_deer/generated/json/base/json_field.dart';
@JsonSerializable()
class BankEntity with ISuspensionBean {
BankEntity({this.id, this.bankName, this.firstLetter});
factory BankEntity.fromJson(Map json) => $BankEntityFromJson(json);
Map toJson() => $BankEntityToJson(this);
int? id;
String? bankName;
String? firstLetter;
@override
String getSuspensionTag() {
return firstLetter ?? '';
}
}
================================================
FILE: lib/account/models/city_entity.dart
================================================
import 'package:azlistview/azlistview.dart';
import 'package:flutter_deer/generated/json/base/json_field.dart';
import 'package:flutter_deer/generated/json/city_entity.g.dart';
@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() {
return firstCharacter;
}
}
================================================
FILE: lib/account/models/withdrawal_account_model.dart
================================================
class WithdrawalAccountModel {
WithdrawalAccountModel(this.name, this.typeName, this.type, this.code);
WithdrawalAccountModel.fromJsonMap(Map map):
name = map['name'] as String,
typeName = map['typeName'] as String,
type = map['type'] as int,
code = map['code'] as String;
String name;
String typeName;
int type;
String code;
Map toJson() {
final Map data = {};
data['name'] = name;
data['typeName'] = typeName;
data['type'] = type;
data['code'] = code;
return data;
}
}
================================================
FILE: lib/account/page/account_page.dart
================================================
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/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({super.key});
@override
_AccountPageState createState() => _AccountPageState();
}
class _AccountPageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const MyAppBar(
centerTitle: '资金管理',
),
body: SingleChildScrollView(
child: Column(
children: [
Gaps.vGap5,
_buildCard(),
Gaps.vGap5,
ClickItem(
title: '提现',
onTap: () => NavigatorUtils.push(context, AccountRouter.withdrawalPage),
),
ClickItem(
title: '提现记录',
onTap: () => NavigatorUtils.push(context, AccountRouter.withdrawalRecordListPage),
),
ClickItem(
title: '提现密码',
onTap: () => NavigatorUtils.push(context, AccountRouter.withdrawalPasswordPage),
),
],
),
)
);
}
Widget _buildCard() {
return AspectRatio(
aspectRatio: 1.85,
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 6.0),
padding: const EdgeInsets.all(6.0),
decoration: BoxDecoration(
image: DecorationImage(
image: ImageUtils.getAssetImage('account/bg'),
fit: BoxFit.fill,
),
),
child: const Column(
children: [
_AccountMoney(
title: '当前余额(元)',
money: '30.12',
alignment: MainAxisAlignment.end,
moneyTextStyle: TextStyle(color: Colors.white, fontSize: 32.0, fontWeight: FontWeight.bold, fontFamily: 'RobotoThin'),
),
Expanded(
child: Row(
children: [
_AccountMoney(title: '累计结算金额', money: '20000'),
_AccountMoney(title: '累计发放佣金', money: '0.02'),
],
),
),
],
),
),
);
}
}
class _AccountMoney extends StatelessWidget {
const _AccountMoney({
required this.title,
required this.money,
this.alignment,
this.moneyTextStyle
});
final String title;
final String money;
final MainAxisAlignment? alignment;
final TextStyle? moneyTextStyle;
@override
Widget build(BuildContext context) {
return Expanded(
child: MergeSemantics(
child: Column(
mainAxisAlignment: alignment ?? MainAxisAlignment.center,
children: [
/// 横向撑开Column,扩大语义区域
const SizedBox(width: double.infinity),
Text(title, style: const TextStyle(color: Colours.text_disabled, fontSize: Dimens.font_sp12)),
Gaps.vGap8,
RiseNumberText(
NumUtil.getDoubleByValueStr(money) ?? 0,
style: moneyTextStyle ?? const TextStyle(
color: Colours.text_disabled,
fontSize: Dimens.font_sp14,
fontWeight: FontWeight.bold,
fontFamily: 'RobotoThin'
)
),
],
),
),
);
}
}
================================================
FILE: lib/account/page/account_record_list_page.dart
================================================
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 '../../order/page/order_page.dart';
/// design/6店铺-账户/index.html#artboard1
class AccountRecordListPage extends StatefulWidget {
const AccountRecordListPage({super.key});
@override
_AccountRecordListPageState createState() => _AccountRecordListPageState();
}
class _AccountRecordListPageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const MyAppBar(
centerTitle: '账户流水',
),
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),
child: Text('2021/06/0${index + 1}'),
)
, 34.0,
),
),
SliverList(
delegate: SliverChildBuilderDelegate((_, index) {
return _buildItem(index);
},
childCount: index + 1,
),
),
],
);
}
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: 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),
),
],
),
),
);
}
}
================================================
FILE: lib/account/page/add_withdrawal_account_page.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_deer/account/account_router.dart';
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/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_item.dart';
import 'package:flutter_deer/widgets/text_field_item.dart';
/// design/6店铺-账户/index.html#artboard29
class AddWithdrawalAccountPage extends StatefulWidget {
const AddWithdrawalAccountPage({super.key});
@override
_AddWithdrawalAccountPageState createState() => _AddWithdrawalAccountPageState();
}
class _AddWithdrawalAccountPageState extends State {
bool _isWechat = false;
String _accountType = '银行卡(对私账户)';
String _city = '';
String _bank = '';
String _bank1 = '';
@override
Widget build(BuildContext context) {
final TextStyle? style = Theme.of(context).textTheme.titleSmall?.copyWith(fontSize: Dimens.font_sp14);
final List children = [
Gaps.vGap5,
SelectedItem(
title: '账号类型',
content: _accountType,
onTap: () => _showSelectAccountTypeDialog(),
),
Visibility(
maintainState: true, /// 是为了保留填写信息,其实就是Offstage,这里只是展示另一种方法。
visible: !_isWechat,
child: Column(
children: [
const TextFieldItem(
title: '持 卡 人',
hintText: '填写您的真实姓名',
),
const TextFieldItem(
title: '银行卡号',
keyboardType: TextInputType.number,
hintText: '填写银行卡号',
),
SelectedItem(
title: '开 户 地',
content: _city.isEmpty ? '选择开户城市' : _city,
style: _city.isEmpty ? style : null,
onTap: () {
NavigatorUtils.pushResult(context, AccountRouter.citySelectPage, (Object result) {
setState(() {
final CityEntity model = result as CityEntity;
_city = model.name;
});
});
},
),
SelectedItem(
title: '银行名称',
content: _bank.isEmpty ? '选择开户银行' : _bank,
style: _bank.isEmpty ? style : null,
onTap: () {
NavigatorUtils.pushResult(context, '${AccountRouter.bankSelectPage}?type=0', (Object result) {
setState(() {
final BankEntity model = result as BankEntity;
_bank = model.bankName.nullSafe;
});
});
},
),
SelectedItem(
title: '支行名称',
content: _bank1.isEmpty ? '选择开户支行' : _bank1,
style: _bank1.isEmpty ? style : null,
onTap: () {
NavigatorUtils.pushResult(context, '${AccountRouter.bankSelectPage}?type=1', (Object result) {
setState(() {
final BankEntity model = result as BankEntity;
_bank1 = model.bankName.nullSafe;
});
});
},
),
],
),
),
Padding(
padding: const EdgeInsets.only(top: 8.0, left: 16.0),
child: Text(
_isWechat ? '绑定本机当前登录的微信号' : '绑定持卡人本人的银行卡',
style: Theme.of(context).textTheme.titleSmall,
),
),
];
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: const MyAppBar(
title: '添加账号',
),
body: MyScrollView(
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
),
);
}
void _dialogSelect(bool flag) {
setState(() {
_isWechat = flag;
});
NavigatorUtils.goBack(context);
}
/// design/6店铺-账户/index.html#artboard30
void _showSelectAccountTypeDialog() {
/// 关闭输入法,避免弹出
FocusManager.instance.primaryFocus?.unfocus();
showElasticDialog(
context: context,
builder: (BuildContext context) {
const OutlinedBorder buttonShape = RoundedRectangleBorder();
final Widget content = Column(
children: [
const Text(
'账号类型',
style: TextStyles.textBold18,
),
Gaps.vGap16,
Gaps.line,
Expanded(
child: TextButton(
child: const Text('微信'),
onPressed: () {
_accountType = '微信';
_dialogSelect(true);
},
),
),
Gaps.line,
Expanded(
child: TextButton(
child: const Text('银行卡(对私账户)'),
onPressed: () {
_accountType = '银行卡(对私账户)';
_dialogSelect(false);
},
),
),
Gaps.line,
Expanded(
child: TextButton(
child: const Text('银行卡(对公账户)'),
onPressed: () {
_accountType = '银行卡(对公账户)';
_dialogSelect(false);
},
),
),
],
);
final Widget decoration = Container(
decoration: BoxDecoration(
color: context.dialogBackgroundColor,
borderRadius: BorderRadius.circular(8.0),
),
width: 270.0,
height: 190.0,
padding: const EdgeInsets.only(top: 24.0),
child: TextButtonTheme(
data: TextButtonThemeData(
style: TextButton.styleFrom(
// 文字颜色
foregroundColor: Theme.of(context).primaryColor,
// 按钮大小
minimumSize: Size.infinite,
// 修改默认圆角
shape: buttonShape,
),
),
child: content,
),
);
return Material(
type: MaterialType.transparency,
child: Center(
child: decoration,
),
);
},
);
}
}
================================================
FILE: lib/account/page/bank_select_page.dart
================================================
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/load_image.dart';
import 'package:flutter_deer/widgets/my_app_bar.dart';
/// design/6店铺-账户/index.html#artboard33
class BankSelectPage extends StatefulWidget {
const BankSelectPage({super.key, this.type = 0});
final int type;
@override
_BankSelectPageState createState() => _BankSelectPageState();
}
class _BankSelectPageState extends State {
final List _bankList = [];
final List _bankNameList = [
'工商银行', '建设银行', '中国银行', '农业银行',
'招商银行', '交通银行', '中信银行', '民生银行',
'兴业银行', '浦发银行'
];
final List _bankLogoList = [
'gongshang', 'jianhang', 'zhonghang', 'nonghang',
'zhaohang', 'jiaohang', 'zhongxin', 'minsheng',
'xingye', 'pufa'
];
List _indexBarData = [];
@override
void initState() {
super.initState();
_loadData();
}
void _loadData() {
// 获取城市列表
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(_addBank);
SuspensionUtil.sortListBySuspensionTag(_bankList);
SuspensionUtil.setShowSuspensionStatus(_bankList);
_indexBarData = _bankList.map((BankEntity e) {
if (e.isShowSuspension) {
return e.firstLetter.nullSafe;
} else {
return '';
}
}).where((String element) => element.isNotEmpty).toList();
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) {
return Scaffold(
appBar: MyAppBar(
title: widget.type == 0 ? '开户银行' : '选择支行',
),
body: SafeArea(
child: AzListView(
data: _bankList,
itemCount: _bankList.length,
itemBuilder: (_, int index) {
if (index == 0 && widget.type == 0) {
return _buildHeader();
}
return _buildListItem(index);
},
indexBarItemHeight: 25,
indexBarData: _indexBarData,
indexBarOptions: IndexBarOptions(
needRebuild: true,
indexHintWidth: 96,
indexHintHeight: 96,
indexHintTextStyle: const TextStyle(fontSize: 26.0, color: Colors.white),
textStyle: Theme.of(context).textTheme.titleSmall!,
downTextStyle: context.isDark ? TextStyles.textSize12 : const TextStyle(fontSize: 12.0, color: Colors.black),
),
),
),
);
}
Widget _buildHeader() {
return SizedBox(
height: 430,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 5.0, bottom: 5.0, left: 16.0),
child: Text('常用', style: Theme.of(context).textTheme.titleSmall),
),
Expanded(
child: ListView.builder(
physics: const NeverScrollableScrollPhysics(),
itemExtent: 40.0,
itemCount: _bankNameList.length,
itemBuilder: (_, int index) {
return InkWell(
onTap: () => NavigatorUtils.goBackWithParams(context, BankEntity(id: 0, bankName: _bankNameList[index], firstLetter: '')),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Row(
children: [
LoadAssetImage('account/${_bankLogoList[index]}',width: 24.0),
Gaps.hGap8,
Text(_bankNameList[index]),
],
),
),
);
}
),
)
],
),
);
}
Widget _buildListItem(int index) {
final BankEntity model = _bankList[index];
return InkWell(
onTap: () => NavigatorUtils.goBackWithParams(context, model),
child: Container(
padding: const EdgeInsets.only(left: 16.0, right: 34.0),
height: 40.0,
child: Container(
decoration: BoxDecoration(
border: (model.isShowSuspension && model.id != 17749) ? Border(
top: Divider.createBorderSide(context, width: 0.6),
) : null
),
child: Row(
children: [
Opacity(
opacity: model.isShowSuspension ? 1 : 0,
child: SizedBox(
width: 28.0,
child: Text(model.firstLetter.nullSafe),
)
),
Expanded(
child: Text(model.bankName.nullSafe),
)
],
),
),
),
);
}
}
================================================
FILE: lib/account/page/city_select_page.dart
================================================
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/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';
/// design/6店铺-账户/index.html#artboard34
class CitySelectPage extends StatefulWidget {
const CitySelectPage({super.key});
@override
_CitySelectPageState createState() => _CitySelectPageState();
}
class _CitySelectPageState extends State {
final List _cityList = [];
List _indexBarData = [];
@override
void initState() {
super.initState();
_loadData();
}
Future _loadData() async {
// 获取城市列表
// loadString源码中有对json大小进行判断,json过大会使用compute处理,集成测试无法使用compute,所以这里修改源码单独判断处理。
String jsonStr;
if (Constant.isDriverTest) {
jsonStr = await _loadString('assets/data/city.json');
} else {
jsonStr = await rootBundle.loadString('assets/data/city.json');
}
final List list = json.decode(jsonStr) as List;
list.forEach(_addCity);
SuspensionUtil.setShowSuspensionStatus(_cityList);
_indexBarData = _cityList.map((CityEntity e) {
if (e.isShowSuspension) {
return e.firstCharacter;
} else {
return '';
}
}).where((String element) => element.isNotEmpty).toList();
setState(() {
});
}
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);
return utf8.decode(data.buffer.asUint8List());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const MyAppBar(
title: '开户地点',
),
body: SafeArea(
child: AzListView(
data: _cityList,
itemCount: _cityList.length,
itemBuilder: (_, int index) => _buildListItem(index),
indexBarItemHeight: 18,
indexBarData: _indexBarData,
indexBarOptions: IndexBarOptions(
needRebuild: true,
indexHintWidth: 96,
indexHintHeight: 96,
indexHintTextStyle: const TextStyle(fontSize: 26.0, color: Colors.white),
textStyle: Theme.of(context).textTheme.titleSmall!,
downTextStyle: context.isDark ? TextStyles.textSize12 : const TextStyle(fontSize: 12.0, color: Colors.black),
),
),
),
);
}
Widget _buildListItem(int index) {
final CityEntity model = _cityList[index];
return InkWell(
onTap: () => NavigatorUtils.goBackWithParams(context, model),
child: Container(
padding: const EdgeInsets.only(left: 16.0, right: 34.0),
height: 40.0,
child: Container(
decoration: BoxDecoration(
border: (model.isShowSuspension && model.cityCode != '0483') ? Border(
top: Divider.createBorderSide(context, width: 0.6),
) : null
),
child: Row(
children: [
Opacity(
opacity: model.isShowSuspension ? 1 : 0,
child: SizedBox(
width: 28.0,
child: Text(model.firstCharacter),
)
),
Expanded(
child: Text(model.name),
)
],
),
),
),
);
}
}
================================================
FILE: lib/account/page/withdrawal_account_list_page.dart
================================================
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/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({super.key});
@override
_WithdrawalAccountListPageState createState() => _WithdrawalAccountListPageState();
}
class _WithdrawalAccountListPageState extends State {
final int _selectIndex = 0;
final List _list = [];
@override
void initState() {
super.initState();
_list.clear();
_list.add(WithdrawalAccountModel('尾号5236 李艺', '工商银行', 0, '123'));
_list.add(WithdrawalAccountModel('唯鹿', '微信', 1, ''));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(
centerTitle: '选择账号',
actionName: '添加',
onPressed: () => NavigatorUtils.push(context, AccountRouter.addWithdrawalAccountPage)
),
body: ListView.separated(
itemCount: _list.length,
separatorBuilder: (_, index) => const Divider(height: 0.6),
itemBuilder: (_, index) => _buildItem(index),
),
);
}
Widget _buildItem(int index) {
return InkWell(
onTap: () => NavigatorUtils.goBackWithParams(context, _list[index]),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
width: double.infinity,
height: 74.0,
alignment: Alignment.center,
child: Row(
children: [
LoadAssetImage(_list[index].type == 0 ? 'account/yhk' : 'account/wechat', width: 24.0),
Gaps.hGap16,
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_list[index].typeName),
Gaps.vGap8,
Text(_list[index].name, style: TextStyles.textSize12),
],
),
),
Visibility(
visible: _selectIndex == index,
child: const LoadAssetImage(
'account/selected',
height: 24.0,
width: 24.0,
),
)
],
),
),
);
}
}
================================================
FILE: lib/account/page/withdrawal_account_page.dart
================================================
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';
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/state_layout.dart';
import '../account_router.dart';
/// design/6店铺-账户/index.html#artboard26
class WithdrawalAccountPage extends StatefulWidget {
const WithdrawalAccountPage({super.key});
@override
_WithdrawalAccountPageState createState() => _WithdrawalAccountPageState();
}
class _WithdrawalAccountPageState extends State {
final List _list = [];
final GlobalKey _listKey = GlobalKey();
final Duration _kDuration = const Duration(milliseconds: 300);
@override
void initState() {
super.initState();
_list.clear();
_list.add(WithdrawalAccountModel('唯鹿', '微信', 1, ''));
_list.add(WithdrawalAccountModel('李*', '工商银行', 0, '**** **** **** 5236'));
_list.add(WithdrawalAccountModel('李*', '工商银行', 0, '**** **** **** 2165'));
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: MyAppBar(
centerTitle: '提现账号',
actionName: '添加',
onPressed: () {
NavigatorUtils.pushResult(context, AccountRouter.addWithdrawalAccountPage, (result) {
_insertItem(0);
});
}
),
body: _list.isEmpty ? const StateLayout(type: StateType.account) :
AnimatedList(
key: _listKey,
padding: const EdgeInsets.only(top: 8.0),
initialItemCount: _list.length,
itemBuilder: (_, index, animation) => sizeItem(_list[index], index, animation),
),
);
}
Widget sizeItem(WithdrawalAccountModel data, int index, Animation animation) {
/// item插入、移除动画
return SizeTransition(
axisAlignment: 1.0,
sizeFactor: animation,
child: WithdrawalAccountItem(
key: ObjectKey(data), /// 这里注意必须添加key,原因见: https://weilu.blog.csdn.net/article/details/104745624
data: data,
onLongPress: () => _showDeleteBottomSheet(index),
),
);
}
void _removeItem(int index) {
/// 先移除数据
final WithdrawalAccountModel item = _list.removeAt(index);
_listKey.currentState?.removeItem(
index, (_, animation) => sizeItem(item, 0, animation), /// 构建移除Widget
duration: _kDuration,
);
if (_list.isEmpty) {
Future.delayed(_kDuration, () {
if (mounted) {
setState(() {
});
}
});
}
}
void _insertItem(int index) {
final WithdrawalAccountModel item = WithdrawalAccountModel('weilu_deer', '微信', 1, '');
_list.insert(index, item);
if (_list.length == 1) {
setState(() {
});
} else {
_listKey.currentState?.insertItem(
index,
duration: _kDuration,
);
}
}
void _showDeleteBottomSheet(int index) {
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return Material(
child: SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(
height: 52.0,
child: Center(
child: Text(
'是否确认解绑,防止错误操作',
style: TextStyles.textSize16,
),
),
),
Gaps.line,
MyButton(
minHeight: 54.0,
textColor: Theme.of(context).colorScheme.error,
text: '确认解绑',
backgroundColor: Colors.transparent,
onPressed: () {
_removeItem(index);
NavigatorUtils.goBack(context);
},
),
Gaps.line,
MyButton(
minHeight: 54.0,
textColor: Colours.text_gray,
text: '取消',
backgroundColor: Colors.transparent,
onPressed: () {
NavigatorUtils.goBack(context);
},
),
],
),
),
);
},
);
}
}
================================================
FILE: lib/account/page/withdrawal_page.dart
================================================
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/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 '../account_router.dart';
/// design/6店铺-账户/index.html#artboard3
class WithdrawalPage extends StatefulWidget {
const WithdrawalPage({super.key});
@override
_WithdrawalPageState createState() => _WithdrawalPageState();
}
class _WithdrawalPageState extends State {
final TextEditingController _controller = TextEditingController();
int _withdrawalType = 0;
bool _clickable = false;
WithdrawalAccountModel _data = WithdrawalAccountModel('尾号5236 李艺', '工商银行', 0, '123');
@override
void initState() {
super.initState();
_controller.addListener(_verify);
}
@override
void dispose() {
_controller.removeListener(_verify);
_controller.dispose();
super.dispose();
}
void _verify() {
final price = _controller.text;
if (price.isEmpty || double.parse(price) < 1) {
setState(() {
_clickable = false;
});
return;
}
setState(() {
_clickable = true;
});
}
@override
Widget build(BuildContext context) {
return PopScope(
onPopInvoked: (_) {
/// 拦截返回,关闭键盘,否则会造成上一页面短暂的组件溢出
FocusManager.instance.primaryFocus?.unfocus();
},
child: Scaffold(
appBar: const MyAppBar(
title: '提现',
),
body: MyScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
children: [
Gaps.vGap5,
InkWell(
onTap: () {
NavigatorUtils.pushResult(context, AccountRouter.withdrawalAccountListPage, (result) {
setState(() {
_data = result as WithdrawalAccountModel;
});
});
},
child: Container(
width: double.infinity,
height: 74.0,
alignment: Alignment.center,
child: Row(
children: [
LoadAssetImage(_data.type == 0 ? 'account/yhk' : 'account/wechat', width: 24.0),
Gaps.hGap16,
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_data.typeName),
Gaps.vGap8,
Text(_data.name, style: Theme.of(context).textTheme.titleSmall),
],
),
),
Images.arrowRight
],
),
),
),
Gaps.vGap16,
const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('提现金额', style: TextStyles.textBold14),
Text('单笔2万,单日2万', style: TextStyle(fontSize: Dimens.font_sp12, color: Colours.orange))
],
),
Gaps.vGap8,
Row(
children: [
Container(
width: 15.0,
height: 40.0,
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: LoadAssetImage('account/rmb', color: ThemeUtils.getIconColor(context),),
),
Gaps.hGap8,
Expanded(
child: TextField(
maxLength: 10,
controller: _controller,
keyboardType: const TextInputType.numberWithOptions(decimal: true),
inputFormatters: [UsNumberTextInputFormatter()],
style: const TextStyle(
fontSize: 32.0,
fontWeight: FontWeight.bold,
),
decoration: const InputDecoration(
contentPadding: EdgeInsets.only(bottom: 8.0),
hintStyle: TextStyle(
fontSize: Dimens.font_sp14,
fontWeight: FontWeight.normal,
color: Colours.text_gray_c,
),
hintText: '不能少于1元',
counterText: '',
border: InputBorder.none,
),
),
),
],
),
Gaps.line,
Gaps.vGap8,
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('最多可提现70元', style: Theme.of(context).textTheme.titleSmall),
GestureDetector(
onTap: () {
_controller.text = '70';
},
child: SizedBox(
height: 48.0,
child: Text('全部提现', style: TextStyle(
fontSize: Dimens.font_sp12,
color: Theme.of(context).primaryColor,
)),
)
)
],
),
const Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('转出方式', style: TextStyles.textBold14),
LoadAssetImage('account/sm', width: 16.0)
],
),
_buildWithdrawalType(0),
Gaps.line,
_buildWithdrawalType(1),
Gaps.vGap24,
MyButton(
key: const Key('提现'),
onPressed: _clickable ? () {
NavigatorUtils.push(context, AccountRouter.withdrawalResultPage);
} : null,
text: '提现',
),
],
),
),
);
}
Widget _buildWithdrawalType(int type) {
return InkWell(
onTap: () {
setState(() {
_withdrawalType = type;
});
},
child: SizedBox(
width: double.infinity,
height: 74.0,
child: Stack(
children: [
Positioned(
top: 18.0,
left: 0.0,
child: LoadAssetImage(_withdrawalType == type ? 'account/txxz' : 'account/txwxz', width: 16.0),
),
Positioned(
top: 16.0,
left: 24.0,
right: 0.0,
child: Text(type == 0 ? '快速到账' : '普通到账'),
),
Positioned(
bottom: 16.0,
left: 24.0,
right: 0.0,
child: RichText(
text: type == 0 ? TextSpan(
text: '手续费按',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontSize: Dimens.font_sp12),
children: const [
TextSpan(text: '0.3%', style: TextStyle(color: Colours.orange)),
TextSpan(text: '收取'),
],
) : TextSpan(
text: '预计',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(fontSize: Dimens.font_sp12),
children: const [
TextSpan(text: 'T+1天到账(免手续费,T为工作日)', style: TextStyle(color: Colours.orange)),
],
),
)
),
],
),
),
);
}
}
================================================
FILE: lib/account/page/withdrawal_password_page.dart
================================================
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/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({super.key});
@override
_WithdrawalPasswordPageState createState() => _WithdrawalPasswordPageState();
}
class _WithdrawalPasswordPageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const MyAppBar(
centerTitle: '提现密码',
),
body: Column(
children: [
Gaps.vGap5,
ClickItem(
title: '修改密码',
onTap: () {
showModalBottomSheet(
context: context,
/// 禁止拖动关闭
enableDrag: false,
/// 使用true则高度不受16分之9的最高限制
isScrollControlled: true,
builder: (_) => const WithdrawalPasswordSetting()
);
}
),
ClickItem(
title: '忘记密码',
onTap: () => _showHintDialog()
),
],
),
);
}
void _showHintDialog() {
showElasticDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return BaseDialog(
hiddenTitle: true,
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: Text('为了您的账户安全需先进行短信验证并设置提现密码。', textAlign: TextAlign.center),
),
onPressed: () {
NavigatorUtils.goBack(context);
_showVerifyDialog();
},
);
},
);
}
void _showVerifyDialog() {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => const SMSVerifyDialog()
);
}
}
================================================
FILE: lib/account/page/withdrawal_record_list_page.dart
================================================
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 '../../order/page/order_page.dart';
/// design/6店铺-账户/index.html#artboard19
class WithdrawalRecordListPage extends StatefulWidget {
const WithdrawalRecordListPage({super.key});
@override
_WithdrawalRecordListPageState createState() => _WithdrawalRecordListPageState();
}
class _WithdrawalRecordListPageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const MyAppBar(
title: '提现记录',
),
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),
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),
decoration: BoxDecoration(
border: Border(
bottom: Divider.createBorderSide(context, width: 0.8),
),
),
child: content,
),
);
}
}
================================================
FILE: lib/account/page/withdrawal_result_page.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/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({super.key});
@override
_WithdrawalResultPageState createState() => _WithdrawalResultPageState();
}
class _WithdrawalResultPageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const MyAppBar(
title: '提现结果',
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Gaps.vGap50,
const LoadAssetImage('account/sqsb',
width: 80.0,
height: 80.0,
),
Gaps.vGap12,
const Text(
'提现申请提交失败,请重新提交',
style: TextStyles.textSize16,
),
Gaps.vGap8,
Text(
'2021-02-21 15:20:10',
style: Theme.of(context).textTheme.titleSmall,
),
Gaps.vGap8,
Text(
'5秒后返回提现页面',
style: Theme.of(context).textTheme.titleSmall,
),
Gaps.vGap24,
MyButton(
onPressed: () => NavigatorUtils.goBack(context),
text: '返回',
)
],
),
),
);
}
}
================================================
FILE: lib/account/widgets/rise_number_text.dart
================================================
import 'package:flutter/material.dart';
// 简易实现数字滚动效果
class RiseNumberText extends StatefulWidget {
const RiseNumberText(this.number,{
super.key,
this.style,
this.duration = 1200
});
final num number;
final TextStyle? style;
final int duration;
@override
_RiseNumberTextState createState() => _RiseNumberTextState();
}
class _RiseNumberTextState extends State with SingleTickerProviderStateMixin {
late Animation _animation;
late AnimationController _controller;
num _fromNumber = 0;
@override
void initState() {
super.initState();
_controller = AnimationController(duration: Duration(milliseconds: widget.duration), vsync: this);
final curve = CurvedAnimation(parent: _controller, curve: Curves.linear);
_animation = Tween(begin: 0, end: 1).animate(curve);
_controller.forward(from: 0);
}
@override
void didUpdateWidget(RiseNumberText oldWidget) {
super.didUpdateWidget(oldWidget);
// 数据变化时执行动画
if (oldWidget.number != widget.number) {
_fromNumber = oldWidget.number;
_controller.forward(from: 0);
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _animation,
builder: (_, __) {
// 数字默认从0增长。数据变化时,由之前数字为基础变化。
return Text(
(_fromNumber + (_animation.value * (widget.number - _fromNumber))).toStringAsFixed(2),
style: widget.style,
);
},
);
}
}
================================================
FILE: lib/account/widgets/sms_verify_dialog.dart
================================================
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.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/screen_utils.dart';
import 'package:flutter_deer/util/theme_utils.dart';
import 'package:flutter_deer/util/toast_utils.dart';
import 'package:flutter_deer/widgets/load_image.dart';
import 'package:flutter_deer/widgets/my_button.dart';
/// design/6店铺-账户/index.html#artboard23
/// 骚操作:借腹生子
class SMSVerifyDialog extends StatefulWidget {
const SMSVerifyDialog({super.key});
@override
_SMSVerifyDialogState createState() => _SMSVerifyDialogState();
}
class _SMSVerifyDialogState extends State {
/// 倒计时秒数
final int _second = 60;
/// 当前秒数
late int _currentSecond;
StreamSubscription? _subscription;
bool _clickable = true;
final FocusNode _focusNode = FocusNode();
final TextEditingController _controller = TextEditingController();
final List _codeList = ['', '', '', '', '', ''];
@override
void initState() {
super.initState();
// _controller.addListener(() {
// if (_controller.text.isEmpty) {
// return;
// }
// // 点击EditableText将光标放置在后端
// _controller.value = TextEditingValue(
// text: _controller.text,
// selection: TextSelection.collapsed(offset: _controller.text.length),
// );
// });
}
@override
void dispose() {
_subscription?.cancel();
_focusNode.dispose();
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final Color textColor = Theme.of(context).primaryColor;
final Widget child = Column(
children: [
Stack(
children: [
Container(
width: double.infinity,
height: 56.0,
alignment: Alignment.center,
padding: const EdgeInsets.only(top: 22.0),
child: const Text(
'短信验证',
style: TextStyles.textBold18,
),
),
Positioned(
top: 0.0,
right: 0.0,
child: Semantics(
label: '关闭',
child: GestureDetector(
onTap: () => NavigatorUtils.goBack(context),
child: const Padding(
padding: EdgeInsets.only(top: 16.0, right: 16.0),
child: LoadAssetImage('goods/icon_dialog_close', width: 16.0, key: Key('dialog_close'),),
),
),
),
)
],
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: Text('本次操作需短信验证,验证码会发送至您的注册手机 15000000000', textAlign: TextAlign.center),
),
Gaps.vGap16,
Expanded(
child: Stack(
children: [
EditableText(
controller: _controller,
focusNode: _focusNode,
keyboardType: TextInputType.number,
/// 指定键盘外观,仅iOS有效
keyboardAppearance: Brightness.dark,
/// 只能为数字、6位
inputFormatters: [FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(6)],
// 隐藏光标与字体颜色,达到隐藏输入框的目的
cursorColor: Colors.transparent,
cursorWidth: 0,
textAlign: TextAlign.center,
backgroundCursorColor: Colors.transparent,
style: const TextStyle(color: Colors.transparent, fontSize: Dimens.font_sp18),
onChanged: (v) {
for (var i = 0; i < _codeList.length; i ++) {
if (i < v.length) {
_codeList[i] = v.substring(i, i + 1);
} else {
_codeList[i] = '';
}
}
if (v.length == _codeList.length) {
Toast.show('验证码:${_controller.text}');
for (var i = 0; i < _codeList.length; i ++) {
_codeList[i] = '';
}
/// https://github.com/flutter/flutter/issues/47191
/// https://github.com/flutter/flutter/pull/57264
/// 1.19.0已修复,小于此版本需添加addPostFrameCallback处理,否则会错误触发onChanged。
SchedulerBinding.instance.addPostFrameCallback((_) {
_controller.clear();
});
}
setState(() {});
},
),
Semantics(
label: '点击输入',
child: GestureDetector(
onTap: () {
/// 一直怼,会有概率造成键盘抖动,加一个键盘时候弹出判断
if (MediaQuery.of(context).viewInsets.bottom < 10) {
final focusScope = FocusScope.of(context);
focusScope.unfocus();
Future.delayed(Duration.zero, () => focusScope.requestFocus(_focusNode));
}
},
child: Container(
key: const Key('vcode'),
color: Colors.transparent,
padding: const EdgeInsets.symmetric(horizontal: 24.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: List.generate(_codeList.length, (i) => _buildInputWidget(i, textColor)),
),
),
),
),
],
),
),
Gaps.vGap16,
Gaps.line,
MyButton(
text: _clickable ? '获取验证码' : '已发送($_currentSecond s)',
textColor: textColor,
disabledTextColor: Colours.text_gray,
backgroundColor: Colors.transparent,
disabledBackgroundColor: Colors.transparent,
onPressed: _clickable ? () {
setState(() {
_currentSecond = _second;
_clickable = false;
});
_subscription = Stream.periodic(const Duration(seconds: 1), (i) => i).take(_second).listen((i) {
setState(() {
_currentSecond = _second - i - 1;
_clickable = _currentSecond < 1;
});
});
}: null,
),
],
);
Widget body = Container(
decoration: BoxDecoration(
color: context.dialogBackgroundColor,
borderRadius: BorderRadius.circular(8.0),
),
width: 280.0,
height: 210.0,
child: child,
);
/// 判断原因见BaseDialog注释
if (Device.getAndroidSdkInt() >= 30) {
body = Container(
alignment: Alignment.center,
height: context.height - MediaQuery.of(context).viewInsets.bottom,
child: body,
);
} else {
body = AnimatedContainer(
alignment: Alignment.center,
height: context.height - MediaQuery.of(context).viewInsets.bottom,
duration: const Duration(milliseconds: 120),
curve: Curves.easeInCubic,
child: body,
);
}
return Scaffold(//创建透明层
backgroundColor: Colors.transparent,//透明类型
body: body,
);
}
Widget _buildInputWidget(int p, Color textColor) {
return Container(
height: 32.0,
width: 32.0,
alignment: Alignment.center,
decoration: BoxDecoration(
border: Border.all(width: 0.6, color: _codeList[p].isNotEmpty ? textColor : Colours.text_gray_c),
borderRadius: BorderRadius.circular(4.0),
),
child: Text(_codeList[p], style: const TextStyle(fontSize: Dimens.font_sp18),)
);
}
}
================================================
FILE: lib/account/widgets/withdrawal_account_item.dart
================================================
import 'dart:math';
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/util/theme_utils.dart';
import 'package:flutter_deer/util/toast_utils.dart';
import 'package:flutter_deer/widgets/load_image.dart';
class WithdrawalAccountItem extends StatefulWidget {
const WithdrawalAccountItem({
super.key,
required this.data,
required this.onLongPress,
});
final WithdrawalAccountModel data;
final GestureLongPressCallback onLongPress;
@override
_WithdrawalAccountItemState createState() => _WithdrawalAccountItemState();
}
/// 3D翻转动画 https://medium.com/flutterpub/flutter-flip-card-animation-with-3d-effect-4284af04f5a
class _WithdrawalAccountItemState extends State with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation _animation;
AnimationStatus _animationStatus = AnimationStatus.dismissed;
@override
void initState() {
super.initState();
_animationController = AnimationController(vsync: this, duration: const Duration(seconds: 1));
_animation = Tween(end: 1.0, begin: 0).animate(_animationController)
..addStatusListener((status) {
_animationStatus = status;
});
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final Widget front = Stack(
children: [
Positioned(
top: 25.0,
left: 24.0,
child: Container(
height: 40.0,
width: 40.0,
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20.0),
),
child: LoadAssetImage(widget.data.type == 1 ? 'account/wechat' : 'account/yhk'),
),
),
Positioned(
top: 22.0,
left: 72.0,
child: Text(widget.data.typeName, style: const TextStyle(color: Colors.white, fontSize: Dimens.font_sp18)),
),
Positioned(
top: 48.0,
left: 72.0,
child: Text(widget.data.name, style: const TextStyle(color: Colors.white, fontSize: Dimens.font_sp12)),
),
Positioned(
bottom: 24.0,
left: 72.0,
child: Text(widget.data.code, style: const TextStyle(color: Colors.white, fontSize: Dimens.font_sp18, letterSpacing: 1.0)),
),
],
);
final Widget back = Center(
child: GestureDetector(
onTap: () => Toast.show('提现'),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 5.0),
decoration: BoxDecoration(
border: Border.all(width: 0.6, color: Colors.white),
borderRadius: BorderRadius.circular(4.0),
),
child: Transform(
// 文字翻转,保证文字的方向
alignment: FractionalOffset.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.002)
..rotateX(pi),
child: const Text('提现',
style: TextStyle(color: Colors.white, fontSize: Dimens.font_sp16)
),
),
),
),
);
return Container(
height: 152.0,
padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 22.0),
child: AnimatedBuilder(
animation: _animation,
builder: (_, child) {
return Transform(
alignment: FractionalOffset.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.002)
..rotateX(pi * _animation.value),
child: AccountCard(
type: widget.data.type,
child: InkWell(
// 长按删除账号
onLongPress: () => widget.onLongPress(),
onTap: () {
/// 避免动画中重复执行
if (_animationStatus == AnimationStatus.dismissed) {
_animationController.forward();
}
if (_animationStatus == AnimationStatus.completed) {
_animationController.reverse();
}
},
child: _animation.value <= 0.5 ? front : back,
),
),
);
},
),
);
}
}
class AccountCard extends StatefulWidget {
const AccountCard({
super.key,
required this.child,
required this.type
});
final Widget child;
final int type;
@override
_AccountCardState createState() => _AccountCardState();
}
class _AccountCardState extends State {
@override
Widget build(BuildContext context) {
/// 添加RepaintBoundary原因见docs/Web问题汇总.md
return RepaintBoundary(
child: DecoratedBox(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
boxShadow: context.isDark ? null : [
BoxShadow(
color: widget.type == 1 ? const Color(0x804EE07A) : Colours.shadow_blue,
offset: const Offset(0.0, 2.0),
blurRadius: 8.0,
),
],
gradient: LinearGradient(
colors: widget.type == 1 ?
const [Color(0xFF40E6AE), Color(0xFF2DE062)] :
const [Color(0xFF57C4FA), Colours.app_main],
),
),
child: widget.child,
),
);
}
}
================================================
FILE: lib/account/widgets/withdrawal_password_setting.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/device_utils.dart';
import 'package:flutter_deer/util/screen_utils.dart';
import 'package:flutter_deer/util/theme_utils.dart';
import 'package:flutter_deer/util/toast_utils.dart';
import 'package:flutter_deer/widgets/load_image.dart';
import 'package:vibration/vibration.dart';
/// design/6店铺-账户/index.html#artboard13
class WithdrawalPasswordSetting extends StatefulWidget {
const WithdrawalPasswordSetting({super.key});
@override
_WithdrawalPasswordSettingState createState() => _WithdrawalPasswordSettingState();
}
class _WithdrawalPasswordSettingState extends State {
int _index = 0;
final _list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0];
final List _codeList = ['', '', '', '', '', ''];
@override
Widget build(BuildContext context) {
return Container(
color: context.dialogBackgroundColor,
height: context.height * 7 / 10.0,
child: Column(
children: [
Stack(
children: [
Container(
width: double.infinity,
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: const Text(
'设置提现密码',
style: TextStyles.textBold18,
),
),
Positioned(
right: 16.0,
top: 16.0,
bottom: 16.0,
child: Semantics(
label: '关闭',
child: GestureDetector(
onTap: () => NavigatorUtils.goBack(context),
child: const LoadAssetImage(
'goods/icon_dialog_close',
key: Key('close'),
width: 16.0,
height: 16.0,
),
),
),
),
],
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 45.0,
margin: const EdgeInsets.only(left: 16.0, right: 16.0),
decoration: BoxDecoration(
border: Border.all(width: 0.6, color: Colours.text_gray_c),
borderRadius: BorderRadius.circular(4.0),
),
child: Row(
children: List.generate(_codeList.length, (i) => _buildInputWidget(i))
),
),
Gaps.vGap10,
Text('提现密码不可为连续、重复的数字。', style: Theme.of(context).textTheme.titleSmall),
],
),
),
Gaps.line,
Container(
color: Theme.of(context).dividerTheme.color,
child: GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 1.953,
mainAxisSpacing: 0.6,
crossAxisSpacing: 0.6,
),
itemCount: 12,
itemBuilder: (_, index) => _buildButton(index)
),
),
],
)
);
}
Widget _buildButton(int index) {
final color = context.isDark ? Colours.dark_bg_gray : Colours.dark_button_text;
return Material(
color: (index == 9 || index == 11) ? color : null,
child: InkWell(
child: Center(
child: index == 11 ? Semantics(
label: '删除',
child: const LoadAssetImage('account/del', width: 32.0),
) : index == 9 ? Semantics(
label: '无效',
child: Gaps.empty,
) : Text(
_list[index].toString(),
style: const TextStyle(fontSize: 26.0),
),
),
onTap: () async {
if (index == 9) {
return;
}
if (index == 11) {
if (_index == 0) {
return;
}
_codeList[_index - 1] = '';
_index--;
setState(() {
});
return;
}
_codeList[_index] = _list[index].toString();
_index++;
if (_index == _codeList.length) {
var code = '';
for (var i = 0; i < _codeList.length; i ++) {
code = code + _codeList[i];
}
Toast.show('密码:$code');
_index = 0;
for (var i = 0; i < _codeList.length; i ++) {
_codeList[i] = '';
}
}
setState(() {
});
/// 点击时给予振动反馈
if (!Device.isDesktop && await Vibration.hasVibrator()) {
Vibration.vibrate(duration: 10);
}
},
),
);
}
Widget _buildInputWidget(int p) {
return Expanded(
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
border: p != 5 ? Border(
right: Divider.createBorderSide(context, color: Colours.text_gray_c, width: 0.6),
) : null,
),
child: Text(_codeList[p].isEmpty ? '' : '●', style: TextStyles.textSize12,),
),
);
}
}
================================================
FILE: lib/demo/demo_page.dart
================================================
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/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';
import 'package:flutter_deer/widgets/click_item.dart';
import 'package:flutter_deer/widgets/my_app_bar.dart';
class DemoPage extends StatefulWidget {
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(
appBar: const MyAppBar(
centerTitle: 'Demo',
),
body: Column(
children: [
Gaps.vGap5,
ClickItem(
title: 'Overlay',
onTap: () => AppNavigator.push(context, OverlayDemo()),
),
ClickItem(
title: 'Focus',
onTap: () => AppNavigator.push(context, const FocusDemoPage(title: 'Focus Demo')),
),
ClickItem(
title: 'RipplesAnimation',
onTap: () => AppNavigator.push(context, const RipplesAnimationPage()),
),
ClickItem(
title: 'Navigator 2.0',
onTap: () => AppNavigator.push(context, const NestedRouterDemo()),
),
ClickItem(
title: 'ScratchCard',
onTap: () => AppNavigator.push(context, const ScratchCardDemoPage()),
),
ClickItem(
title: 'Lottie',
onTap: () => AppNavigator.push(context, const LottieDemo()),
),
],
),
);
}
}
================================================
FILE: lib/demo/focus/focus_demo_page.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_deer/res/resources.dart';
import 'package:flutter_deer/util/theme_utils.dart';
/// 博客:https://weilu.blog.csdn.net/article/details/107132031
class FocusDemoPage extends StatefulWidget {
const FocusDemoPage({super.key, required this.title});
final String title;
@override
_FocusDemoPageState createState() => _FocusDemoPageState();
}
class _FocusDemoPageState extends State {
final FocusNode _focusNode = FocusNode();
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
debugPrint('${widget.title} build');
return Scaffold(
appBar: AppBar(
backgroundColor: context.isDark ? Colours.dark_bg_color : Colors.blue,
title: Text(widget.title),
),
body: Column(
children: [
TextField(
focusNode: _focusNode,
),
OutlinedButton(
child: const Text('打印FocusTree'),
onPressed: () {
// 关闭软键盘四种方式
// SystemChannels.textInput.invokeMethod('TextInput.hide');
// FocusScope.of(context).requestFocus(FocusNode());
// FocusScope.of(context).unfocus();
// _focusNode.unfocus();
// FocusManager.instance.primaryFocus.unfocus();
WidgetsBinding.instance.addPostFrameCallback((_) {
debugDumpFocusTree();
});
},
),
ElevatedButton(
child: const Text('Push TestPage'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => const FocusDemoPage(title: 'Test Page'),
),
);
},
),
],
),
);
}
}
================================================
FILE: lib/demo/lottie/bunny.dart
================================================
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
}
================================================
FILE: lib/demo/lottie/lottie_demo.dart
================================================
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,
),
),
);
}
}
================================================
FILE: lib/demo/navigator/book_entity.dart
================================================
class Book {
Book(this.title, this.author);
final String title;
final String author;
}
// Routes
abstract class BookRoutePath {}
class BooksListPath extends BookRoutePath {}
class BooksSettingsPath extends BookRoutePath {}
class BooksDetailsPath extends BookRoutePath {
BooksDetailsPath(this.id);
final int id;
}
================================================
FILE: lib/demo/navigator/books_app_state.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_deer/demo/navigator/book_entity.dart';
class BooksAppState extends ChangeNotifier {
BooksAppState() : _selectedIndex = 0;
late int _selectedIndex;
Book? _selectedBook;
final List books = [
Book('Stranger in a Strange Land', 'Robert A. Heinlein'),
Book('Foundation', 'Isaac Asimov'),
Book('Fahrenheit 451', 'Ray Bradbury'),
];
int get selectedIndex => _selectedIndex;
set selectedIndex(int idx) {
_selectedIndex = idx;
if (_selectedIndex == 1) {
// Remove this line if you want to keep the selected book when navigating
// between "settings" and "home" which book was selected when Settings is
// tapped.
selectedBook = null;
}
notifyListeners();
}
Book? get selectedBook => _selectedBook;
set selectedBook(Book? book) {
_selectedBook = book;
notifyListeners();
}
int getSelectedBookById() {
if (!books.contains(_selectedBook)) {
return 0;
}
return books.indexOf(_selectedBook!);
}
void setSelectedBookById(int id) {
if (id < 0 || id > books.length - 1) {
return;
}
_selectedBook = books[id];
notifyListeners();
}
}
================================================
FILE: lib/demo/navigator/books_main.dart
================================================
import 'package:flutter/material.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({super.key});
@override
_NestedRouterDemoState createState() => _NestedRouterDemoState();
}
class _NestedRouterDemoState extends State {
final BookRouterDelegate _routerDelegate = BookRouterDelegate();
final BookRouteInformationParser _routeInformationParser =
BookRouteInformationParser();
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Books App',
routerDelegate: _routerDelegate,
routeInformationParser: _routeInformationParser,
);
}
}
================================================
FILE: lib/demo/navigator/delegate/inner_router_delegate.dart
================================================
import 'package:flutter/material.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/book_details_screen.dart';
import 'package:flutter_deer/demo/navigator/screen/books_list_screen.dart';
import 'package:flutter_deer/demo/navigator/screen/setting_screen.dart';
class InnerRouterDelegate extends RouterDelegate
with ChangeNotifier, PopNavigatorRouterDelegateMixin {
InnerRouterDelegate(this._appState);
@override
final GlobalKey navigatorKey = GlobalKey();
BooksAppState get appState => _appState;
BooksAppState _appState;
set appState(BooksAppState value) {
if (value == _appState) {
return;
}
_appState = value;
notifyListeners();
}
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
pages: [
if (appState.selectedIndex == 0) ...[
FadeAnimationPage(
child: BooksListScreen(
books: appState.books,
onTapped: _handleBookTapped,
),
key: const ValueKey('BooksListPage'),
),
if (appState.selectedBook != null)
MaterialPage(
key: ValueKey(appState.selectedBook),
child: BookDetailsScreen(book: appState.selectedBook!),
),
] else
const FadeAnimationPage(
child: SettingsScreen(),
key: ValueKey('SettingsPage'),
),
],
onPopPage: (route, dynamic result) {
appState.selectedBook = null;
notifyListeners();
return route.didPop(result);
},
);
}
@override
Future setNewRoutePath(BookRoutePath configuration) async {
// This is not required for inner router delegate because it does not
// parse route
assert(false);
}
void _handleBookTapped(Book book) {
appState.selectedBook = book;
notifyListeners();
}
}
class FadeAnimationPage extends Page {
const FadeAnimationPage({super.key, required this.child});
final Widget child;
@override
Route createRoute(BuildContext context) {
return PageRouteBuilder(
settings: this,
pageBuilder: (context, animation, animation2) {
final curveTween = CurveTween(curve: Curves.easeIn);
return FadeTransition(
opacity: animation.drive(curveTween),
child: child,
);
},
);
}
}
================================================
FILE: lib/demo/navigator/delegate/router_delegate.dart
================================================
import 'package:flutter/material.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 {
BookRouterDelegate() : navigatorKey = GlobalKey() {
appState.addListener(notifyListeners);
}
@override
final GlobalKey navigatorKey;
BooksAppState appState = BooksAppState();
@override
BookRoutePath get currentConfiguration {
if (appState.selectedIndex == 1) {
return BooksSettingsPath();
} else {
if (appState.selectedBook == null) {
return BooksListPath();
} else {
return BooksDetailsPath(appState.getSelectedBookById());
}
}
}
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
pages: [
MaterialPage(
child: AppShell(appState: appState),
),
],
onPopPage: (route, dynamic result) {
if (!route.didPop(result)) {
return false;
}
if (appState.selectedBook != null) {
appState.selectedBook = null;
}
notifyListeners();
return true;
},
);
}
@override
Future setNewRoutePath(BookRoutePath configuration) async {
if (configuration is BooksListPath) {
appState.selectedIndex = 0;
appState.selectedBook = null;
} else if (configuration is BooksSettingsPath) {
appState.selectedIndex = 1;
} else if (configuration is BooksDetailsPath) {
appState.setSelectedBookById(configuration.id);
}
}
}
================================================
FILE: lib/demo/navigator/parser/route_information_parser.dart
================================================
import 'package:flutter/material.dart';
import '../book_entity.dart';
class BookRouteInformationParser extends RouteInformationParser {
@override
Future parseRouteInformation(
RouteInformation routeInformation) async {
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 BooksListPath();
}
}
@override
RouteInformation? restoreRouteInformation(BookRoutePath configuration) {
if (configuration is BooksListPath) {
return RouteInformation(uri: Uri.parse('/home'));
}
if (configuration is BooksSettingsPath) {
return RouteInformation(uri: Uri.parse('/settings'));
}
if (configuration is BooksDetailsPath) {
return RouteInformation(uri: Uri.parse('/book/${configuration.id}'));
}
return null;
}
}
================================================
FILE: lib/demo/navigator/screen/app_shell.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_deer/demo/navigator/books_app_state.dart';
import 'package:flutter_deer/demo/navigator/delegate/inner_router_delegate.dart';
// Widget that contains the AdaptiveNavigationScaffold
class AppShell extends StatefulWidget {
const AppShell({
super.key,
required this.appState,
});
final BooksAppState appState;
@override
_AppShellState createState() => _AppShellState();
}
class _AppShellState extends State {
late InnerRouterDelegate _routerDelegate;
late ChildBackButtonDispatcher _backButtonDispatcher;
@override
void initState() {
super.initState();
_routerDelegate = InnerRouterDelegate(widget.appState);
}
@override
void didUpdateWidget(covariant AppShell oldWidget) {
super.didUpdateWidget(oldWidget);
_routerDelegate.appState = widget.appState;
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
// Defer back button dispatching to the child router
_backButtonDispatcher = Router.of(context)
.backButtonDispatcher!
.createChildBackButtonDispatcher();
}
@override
Widget build(BuildContext context) {
final appState = widget.appState;
// Claim priority, If there are parallel sub router, you will need
// to pick which one should take priority;
_backButtonDispatcher.takePriority();
return Scaffold(
appBar: AppBar(
title: const Text('Books App'),
),
body: Router(
routerDelegate: _routerDelegate,
backButtonDispatcher: _backButtonDispatcher,
),
bottomNavigationBar: BottomNavigationBar(
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(
icon: Icon(Icons.settings), label: 'Settings'),
],
currentIndex: appState.selectedIndex,
onTap: (newIndex) {
appState.selectedIndex = newIndex;
},
),
);
}
}
================================================
FILE: lib/demo/navigator/screen/book_details_screen.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_deer/demo/navigator/book_entity.dart';
class BookDetailsScreen extends StatelessWidget {
const BookDetailsScreen({
super.key,
required this.book,
});
final Book book;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('Back'),
),
Text(book.title, style: Theme.of(context).textTheme.titleLarge),
Text(book.author, style: Theme.of(context).textTheme.titleMedium),
],
),
),
);
}
}
================================================
FILE: lib/demo/navigator/screen/books_list_screen.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_deer/demo/navigator/book_entity.dart';
class BooksListScreen extends StatelessWidget {
const BooksListScreen({
super.key,
required this.books,
required this.onTapped,
});
final List books;
final ValueChanged onTapped;
@override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
for (final book in books)
ListTile(
title: Text(book.title),
subtitle: Text(book.author),
onTap: () => onTapped(book),
)
],
),
);
}
}
================================================
FILE: lib/demo/navigator/screen/setting_screen.dart
================================================
import 'package:flutter/material.dart';
class SettingsScreen extends StatelessWidget {
const SettingsScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text('Settings screen'),
),
);
}
}
================================================
FILE: lib/demo/overlay/bottom_navigation/my_bottom_navigation_bar.dart
================================================
import 'package:flutter/material.dart';
class MyBottomNavigationBar extends StatefulWidget {
const MyBottomNavigationBar({
super.key,
this.selectedPosition = 0,
this.isShowIndicator = true,
required this.selectedCallback,
});
/// 选中下标
final int selectedPosition;
final bool isShowIndicator;
final void Function(int selectedPosition) selectedCallback;
@override
_MyBottomNavigationBarState createState() => _MyBottomNavigationBarState();
}
class _MyBottomNavigationBarState extends State with TickerProviderStateMixin {
/// BottomNavigationBar高度
double barHeight = 56.0;
/// 指示器高度
double indicatorHeight = 44.0;
/// 选中图标颜色
Color selectedIconColor = Colors.blue;
/// 默认图标颜色
Color normalIconColor = Colors.grey;
/// 选中下标
int selectedPosition = 0;
/// 记录上一次的选中下标
int previousSelectedPosition = 0;
/// 选中图标高度
double selectedIconHeight = 38.0;
/// 默认图标高度
double normalIconHeight = 32.0;
/// 图标
List iconList = [Icons.image, Icons.add, Icons.access_alarms, Icons.settings];
double itemWidth = 0;
late AnimationController controller;
late Animation animation;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
itemWidth = (context.size!.width - barHeight) / 3;
setState(() {});
});
/// 设置动画时长
controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 333));
if (widget.isShowIndicator) {
selectedPosition = widget.selectedPosition;
previousSelectedPosition = widget.selectedPosition;
}
animation = Tween(begin: selectedPosition.toDouble(), end: selectedPosition.toDouble())
.animate(CurvedAnimation(parent: controller, curve: Curves.linear));
}
@override
Widget build(BuildContext context) {
final children = [];
/// 背景
final background = Container(
height: barHeight,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(barHeight / 2),
boxShadow: const [
BoxShadow(color: Colors.grey, offset: Offset(0.0, 1.0), blurRadius: 4.0),
],
),
);
children.add(background);
if (itemWidth == 0) {
return Stack(children: children,);
}
if (widget.isShowIndicator) {
/// 指示器
children.add(Positioned(
left: 6.0 + animation.value * itemWidth,
top: (barHeight - indicatorHeight) / 2,
child: Container(
width: indicatorHeight,
height: indicatorHeight,
decoration: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
boxShadow: [
BoxShadow(color: Colors.grey, blurRadius: 1.0),
],
),
),
));
}
for (var i = 0; i < iconList.length; i++) {
/// 图标中心点计算
final rect = Rect.fromCenter(
center: Offset(28.0 + (i * itemWidth), 28.0),
width: (i == selectedPosition && widget.isShowIndicator) ? selectedIconHeight : normalIconHeight,
height: (i == selectedPosition && widget.isShowIndicator) ? selectedIconHeight : normalIconHeight,
);
children.add(Positioned.fromRect(
rect: rect,
child: GestureDetector(
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: (i == selectedPosition && widget.isShowIndicator) ? selectedIconColor : normalIconColor,
),
child: Icon(iconList[i], color: Colors.white,),
),
onTap: () {
_selectedPosition(i);
},
),
));
}
return Stack(children: children,);
}
void _selectedPosition(int position) {
if (!widget.isShowIndicator) {
previousSelectedPosition = position;
} else {
previousSelectedPosition = selectedPosition;
}
selectedPosition = position;
/// 执行动画
animation = Tween(begin: previousSelectedPosition.toDouble(), end: selectedPosition.toDouble())
.animate(CurvedAnimation(parent: controller, curve: Curves.linear));
animation.addListener(() {
setState(() {});
});
controller.forward(from: 0.0);
widget.selectedCallback(selectedPosition);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
================================================
FILE: lib/demo/overlay/overlay_main.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_deer/demo/overlay/page/overlay_demo_page.dart';
import 'package:flutter_deer/demo/overlay/route/application.dart';
import 'package:flutter_deer/demo/overlay/route/my_navigator_observer.dart';
class OverlayDemo extends StatelessWidget {
OverlayDemo({super.key}) {
Application.navigatorObserver = MyNavigatorObserver();
}
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const OverlayDemoPage(),
navigatorObservers: [
Application.navigatorObserver
],
);
}
}
================================================
FILE: lib/demo/overlay/page/overlay_demo_page.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_deer/demo/overlay/bottom_navigation/my_bottom_navigation_bar.dart';
import 'package:flutter_deer/demo/overlay/page/test_page.dart';
import 'package:flutter_deer/demo/overlay/route/application.dart';
/// 需求说明: 底部固定悬浮BottomNavigationBar,点击切换时有移动动画。
/// 进入二级页面图标全灰,返回一级页面返回之前状态。
/// 二级页面内点击按钮,直接返回一级页面。
///
/// 本例包含自定义BottomNavigationBar,路由监听及Overlay悬浮用法。
class OverlayDemoPage extends StatefulWidget {
const OverlayDemoPage({super.key});
@override
_OverlayDemoPageState createState() => _OverlayDemoPageState();
}
class _OverlayDemoPageState extends State {
OverlayEntry? _overlayEntry;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
_overlayEntry = OverlayEntry(
builder: (context) => _buildBottomNavigation(context),
);
/// 添加悬浮
Overlay.of(context).insert(_overlayEntry!);
});
}
@override
void dispose() {
/// 移除悬浮
_overlayEntry?.remove();
_overlayEntry = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Overlay Demo'),
),
body: ColoredBox(
color: Colors.amber,
child: Center(
child: GestureDetector(
child: const Padding(
padding: EdgeInsets.symmetric(horizontal: 26.0),
child: Text('功能说明:\n1.底部固定悬浮BottomNavigationBar点击切换时有移动动画。\n2.进入二级页面图标全灰,返回一级页面返回之前状态。\n3.二级页面内点击按钮,直接返回一级页面。\n\n点击文字进入下一页->',
style: TextStyle(fontSize: 15.0),
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => const TestPage(),
),
);
},
)
),
)
);
}
Widget _buildBottomNavigation(BuildContext context) {
final double width = MediaQuery.of(context).size.width;
return Positioned(
left: width * 0.2,
right: width * 0.2,
bottom: 20.0,
child: SafeArea(
child: MyBottomNavigationBar(
/// 是否显示指示器
isShowIndicator: Application.navigatorObserver.list.isEmpty,
selectedCallback: (position) {
/// 返回主页
void removeRoute(Route route) {
Navigator.removeRoute(context, route);
}
Application.navigatorObserver.list.forEach(removeRoute);
/// 手动清空
Application.navigatorObserver.list = [];
},
),
),
);
}
}
================================================
FILE: lib/demo/overlay/page/test_page.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_deer/demo/widgets/neumorphic.dart';
class TestPage extends StatefulWidget {
const TestPage({super.key});
@override
_TestPageState createState() => _TestPageState();
}
class _TestPageState extends State {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Test Page'),
),
backgroundColor: Colors.blueGrey.shade200,
body: Center(
child: NeumorphicContainer(
child: GestureDetector(
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,
MaterialPageRoute(
builder: (BuildContext context) => const TestPage(),
),
);
},
),
),
)
);
}
}
================================================
FILE: lib/demo/overlay/route/application.dart
================================================
import 'package:flutter_deer/demo/overlay/route/my_navigator_observer.dart';
class Application {
static late MyNavigatorObserver navigatorObserver;
}
================================================
FILE: lib/demo/overlay/route/my_navigator_observer.dart
================================================
import 'package:flutter/material.dart';
/// 记录路由,便于清空路由栈
class MyNavigatorObserver extends NavigatorObserver {
List> list = [];
@override
void didPush(Route route, Route? previousRoute) {
/// 首页不添加
if (route.settings.name != '/') {
list.add(route);
debugPrint(list.length.toString());
}
}
@override
void didPop(Route route, Route? previousRoute) {
list.remove(route);
debugPrint(list.length.toString());
}
}
================================================
FILE: lib/demo/ripple/ripples_animation_page.dart
================================================
import 'dart:math' as math show sin, pi, sqrt;
import 'package:flutter/material.dart';
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 RipplesAnimationPage extends StatefulWidget {
const RipplesAnimationPage({
super.key,
this.size = 80.0,
this.color = Colors.red,
});
final double size;
final Color color;
@override
_RipplesAnimationState createState() => _RipplesAnimationState();
}
class _RipplesAnimationState extends State with TickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
)..repeat();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
Widget _button() {
return Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(widget.size),
child: DecoratedBox(
decoration: BoxDecoration(
gradient: RadialGradient(
colors: [
widget.color,
Color.lerp(widget.color, Colors.black, .05)!
],
),
),
child: ScaleTransition(
scale: Tween(begin: 0.95, end: 1.0).animate(
CurvedAnimation(
parent: _controller,
curve: const PulsateCurve(),
),
),
child: const Icon(Icons.speaker_phone, size: 44, color: Colors.white,),
),
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: context.isDark ? Colours.dark_bg_color : Colors.blue,
title: const Text('Ripple Demo'),
),
body: Center(
child: CustomPaint(
painter: CirclePainter(
_controller,
color: widget.color,
),
child: SizedBox(
width: widget.size * 4.125,
height: widget.size * 4.125,
child: _button(),
),
),
),
);
}
}
class CirclePainter extends CustomPainter {
CirclePainter(this._animation, {
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);
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.withOpacity(opacity);
canvas.drawCircle(rect.center, radius, paint);
}
@override
void paint(Canvas canvas, Size size) {
final Rect rect = Rect.fromLTRB(0.0, 0.0, size.width, size.height);
for (int wave = 3; wave >= 0; wave--) {
circle(canvas, rect, wave + _animation.value);
}
}
@override
bool shouldRepaint(CirclePainter oldDelegate) => true;
}
class PulsateCurve extends Curve {
const PulsateCurve();
@override
double transform(double t) {
if (t == 0 || t == 1) {
return 0.01;
}
return math.sin(t * math.pi);
}
}
================================================
FILE: lib/demo/scratcher/scratch_card_demo_page.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter_deer/res/colors.dart';
import 'package:flutter_deer/res/gaps.dart';
import 'package:flutter_deer/util/theme_utils.dart';
import 'package:flutter_deer/widgets/load_image.dart';
import 'package:scratcher/scratcher.dart';
class ScratchCardDemoPage extends StatefulWidget {
const ScratchCardDemoPage({super.key});
@override
_ScratchCardDemoPageState createState() => _ScratchCardDemoPageState();
}
class _ScratchCardDemoPageState extends State {
final GlobalKey scratchKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: context.isDark ? Colours.dark_bg_color : Colors.blue,
title: const Text('ScratchCard Demo'),
),
body: Column(
children: [
Gaps.vGap16,
Scratcher(
key: scratchKey,
brushSize: 20,
threshold: 50,
color: Colors.grey,
onChange: (value) => debugPrint('Scratch progress: ${value.toStringAsFixed(2)}%'),
onThreshold: () {
/// 这里设置刮开50%,就揭开所有。
debugPrint('Threshold reached!');
scratchKey.currentState!.reveal(
duration: const Duration(milliseconds: 1000),
);
},
child: Container(
padding: const EdgeInsets.symmetric(vertical: 20),
color: Colors.white,
height: 200,
width: 300,
child: const LoadAssetImage('logo',),
),
),
Gaps.vGap50,
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
OutlinedButton(
child: const Text('Reset'),
onPressed: () {
scratchKey.currentState!.reset(
duration: const Duration(milliseconds: 2000),
);
},
),
ElevatedButton(
child: const Text('Reveal'),
onPressed: () {
scratchKey.currentState!.reveal(
duration: const Duration(milliseconds: 2000),
);
},
),
],
),
],
),
);
}
}
================================================
FILE: lib/demo/widgets/neumorphic.dart
================================================
import 'package:flutter/material.dart';
/// https://medium.com/flutter-community/neumorphic-designs-in-flutter-eab9a4de2059
class NeumorphicContainer extends StatefulWidget {
NeumorphicContainer({
super.key,
required this.child,
this.bevel = 10.0,
this.color,
}) : blurOffset = Offset(bevel / 2, bevel / 2);
final Widget child;
final double bevel;
final Offset blurOffset;
final Color? color;
@override
_NeumorphicContainerState createState() => _NeumorphicContainerState();
}
class _NeumorphicContainerState extends State {
bool _isPressed = false;
void _onPointerDown(PointerDownEvent event) {
setState(() {
_isPressed = true;
});
}
void _onPointerUp(PointerUpEvent event) {
setState(() {
_isPressed = false;
});
}
@override
Widget build(BuildContext context) {
final Color color = widget.color ?? Colors.blueGrey.shade200;
return Listener(
onPointerDown: _onPointerDown,
onPointerUp: _onPointerUp,
child: AnimatedContainer(
duration: const Duration(milliseconds: 150),
padding: const EdgeInsets.all(24.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(widget.bevel * 10),
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
if (_isPressed) color else color.mix(Colors.black, .1),
if (_isPressed) color.mix(Colors.black, .05) else color,
if (_isPressed) color.mix(Colors.black, .05) else color,
color.mix(Colors.white, _isPressed ? .2 : .5),
],
stops: const [0.0, 0.3, 0.6, 1.0],
),
boxShadow: _isPressed ? null : [
BoxShadow(
blurRadius: widget.bevel,
offset: -widget.blurOffset,
color: color.mix(Colors.white, .6),
),
BoxShadow(
blurRadius: widget.bevel,
offset: widget.blurOffset,
color: color.mix(Colors.black, .3),
),
],
),
child: widget.child,
),
);
}
}
extension ColorUtils on Color {
Color mix(Color another, double amount) {
return Color.lerp(this, another, amount)!;
}
}
================================================
FILE: lib/generated/json/bank_entity.g.dart
================================================
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;
}
================================================
FILE: lib/generated/json/base/json_convert_content.dart
================================================
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: camel_case_types
// ignore_for_file: prefer_single_quotes
// 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.g.dart';
import 'package:flutter_deer/account/models/city_entity.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.g.dart';
import 'package:flutter_deer/shop/models/user_entity.dart';
import 'package:flutter_deer/generated/json/user_entity.g.dart';
JsonConvert jsonConvert = JsonConvert();
class JsonConvert {
T? convert(dynamic value) {
if (value == null) {
return null;
}
return asT(value);
}
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 [];
}
}
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);
}
}
}
================================================
FILE: lib/generated/json/base/json_field.dart
================================================
// ignore_for_file: non_constant_identifier_names
// ignore_for_file: camel_case_types
// ignore_for_file: prefer_single_quotes
// 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;
//Whether to participate in toJson
final bool? serialize;
//Whether to participate in fromMap
final bool? deserialize;
const JSONField({this.name, this.serialize, this.deserialize});
}
================================================
FILE: lib/generated/json/city_entity.g.dart
================================================
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;
}
================================================
FILE: lib/generated/json/goods_sort_entity.g.dart
================================================
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;
}
================================================
FILE: lib/generated/json/search_entity.g.dart
================================================
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