├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ └── bug_report.md
└── workflows
│ └── flutter-ci.yml
├── .gitignore
├── .metadata
├── .vscode
└── launch.json
├── CHANGELOG.md
├── LICENSE
├── README.md
├── analysis_options.yaml
├── codecov.yml
├── doc
├── img
│ └── pal-showcase.jpg
├── pal-anchored.gif
├── pal-anchored.mp4
├── pal-anchored2.gif
└── pal-anchored2.mp4
├── example
├── .gitignore
├── .metadata
├── README.md
├── analysis_options.yaml
├── android
│ ├── .gitignore
│ ├── app
│ │ ├── build.gradle
│ │ └── src
│ │ │ ├── debug
│ │ │ └── AndroidManifest.xml
│ │ │ ├── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin
│ │ │ │ └── com
│ │ │ │ │ └── example
│ │ │ │ │ └── example
│ │ │ │ │ └── MainActivity.kt
│ │ │ └── res
│ │ │ │ ├── drawable-v21
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── drawable
│ │ │ │ └── launch_background.xml
│ │ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ │ ├── values-night
│ │ │ │ └── styles.xml
│ │ │ │ └── values
│ │ │ │ └── styles.xml
│ │ │ └── profile
│ │ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ └── settings.gradle
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── 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
│ ├── Runner
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ │ ├── AppIcon.appiconset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ │ ├── Icon-App-20x20@1x.png
│ │ │ │ ├── Icon-App-20x20@2x.png
│ │ │ │ ├── Icon-App-20x20@3x.png
│ │ │ │ ├── Icon-App-29x29@1x.png
│ │ │ │ ├── Icon-App-29x29@2x.png
│ │ │ │ ├── Icon-App-29x29@3x.png
│ │ │ │ ├── Icon-App-40x40@1x.png
│ │ │ │ ├── Icon-App-40x40@2x.png
│ │ │ │ ├── Icon-App-40x40@3x.png
│ │ │ │ ├── Icon-App-60x60@2x.png
│ │ │ │ ├── Icon-App-60x60@3x.png
│ │ │ │ ├── Icon-App-76x76@1x.png
│ │ │ │ ├── Icon-App-76x76@2x.png
│ │ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ │ └── LaunchImage.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ ├── LaunchImage.png
│ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ └── README.md
│ │ ├── Base.lproj
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
│ └── RunnerTests
│ │ └── RunnerTests.swift
├── lib
│ └── main.dart
├── pubspec.lock
└── pubspec.yaml
├── lib
├── anchored
│ ├── anchor_model.dart
│ ├── anchored_helper_widget.dart
│ └── painters
│ │ ├── anchor_painter.dart
│ │ ├── anchored_circle_painter.dart
│ │ ├── anchored_rect_painter.dart
│ │ ├── animated_anchor.dart
│ │ ├── animated_circle.dart
│ │ └── animated_rect.dart
├── animations
│ └── pop_anim.dart
├── helper_orchestrator.dart
├── pal_widgets.dart
└── services
│ ├── element_finder.dart
│ └── overlay_helper.dart
├── pal-widgets.code-workspace
├── pubspec.lock
├── pubspec.yaml
└── test
├── anchored
├── anchored_circle_widget_test.dart
├── anchored_rect_widget_test.dart
└── widgets
│ ├── anchored_circle_no_buttons.dart
│ ├── anchored_circle_page_.dart
│ ├── anchored_rect_no_buttons.dart
│ └── anchored_rect_page.dart
├── screen_variants.dart
└── services
└── element_finder_test.dart
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Every request must be reviewed and accepted by:
2 |
3 | * @g-apparence
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Go to '...'
13 | 2. Click on '....'
14 | 3. Scroll down to '....'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Logs **
24 | Run `flutter analyze` and attach any output of that command below.
25 | If there are any analysis errors, try resolving them before filing this issue.
26 |
27 | Paste the output of running `flutter doctor -v` here.
28 |
29 | **Additional context**
30 | Add any other context about the problem here.
--------------------------------------------------------------------------------
/.github/workflows/flutter-ci.yml:
--------------------------------------------------------------------------------
1 | name: pal-widgets
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - uses: subosito/flutter-action@v1
11 | with:
12 | channel: 'stable'
13 | - name: Install Dependencies
14 | run: flutter pub get
15 | - name: Format
16 | run: dart format --set-exit-if-changed lib test
17 | - name: Analyze
18 | run: flutter analyze lib test
19 | - name: Run tests
20 | run: flutter test --no-pub --coverage --test-randomize-ordering-seed random
21 | - name: Check Code Coverage
22 | uses: ChicagoFlutter/lcov-cop@v1.0.0
23 | with:
24 | path: packages/alfreed/coverage/lcov.info
25 | - name: Upload coverage to Codecov
26 | uses: codecov/codecov-action@v1
27 | with:
28 | token: ${{ secrets.CODECOV_TOKEN }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | build/
32 |
33 | # Android related
34 | **/android/**/gradle-wrapper.jar
35 | **/android/.gradle
36 | **/android/captures/
37 | **/android/gradlew
38 | **/android/gradlew.bat
39 | **/android/local.properties
40 | **/android/**/GeneratedPluginRegistrant.java
41 |
42 | # iOS/XCode related
43 | **/ios/**/*.mode1v3
44 | **/ios/**/*.mode2v3
45 | **/ios/**/*.moved-aside
46 | **/ios/**/*.pbxuser
47 | **/ios/**/*.perspectivev3
48 | **/ios/**/*sync/
49 | **/ios/**/.sconsign.dblite
50 | **/ios/**/.tags*
51 | **/ios/**/.vagrant/
52 | **/ios/**/DerivedData/
53 | **/ios/**/Icon?
54 | **/ios/**/Pods/
55 | **/ios/**/.symlinks/
56 | **/ios/**/profile
57 | **/ios/**/xcuserdata
58 | **/ios/.generated/
59 | **/ios/Flutter/App.framework
60 | **/ios/Flutter/Flutter.framework
61 | **/ios/Flutter/Flutter.podspec
62 | **/ios/Flutter/Generated.xcconfig
63 | **/ios/Flutter/ephemeral
64 | **/ios/Flutter/app.flx
65 | **/ios/Flutter/app.zip
66 | **/ios/Flutter/flutter_assets/
67 | **/ios/Flutter/flutter_export_environment.sh
68 | **/ios/ServiceDefinitions.json
69 | **/ios/Runner/GeneratedPluginRegistrant.*
70 |
71 | # Exceptions to above rules.
72 | !**/ios/**/default.mode1v3
73 | !**/ios/**/default.mode2v3
74 | !**/ios/**/default.pbxuser
75 | !**/ios/**/default.perspectivev3
76 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: 18116933e77adc82f80866c928266a5b4f1ed645
8 | channel: stable
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "example",
9 | "cwd": "example",
10 | "request": "launch",
11 | "type": "dart"
12 | },
13 | {
14 | "name": "example (profile mode)",
15 | "cwd": "example",
16 | "request": "launch",
17 | "type": "dart",
18 | "flutterMode": "profile"
19 | }
20 | ]
21 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.0.2
2 | - update readme
3 |
4 | ## 0.2.0
5 | - AnchoredHelper has 2 factories to create the background animation : AnchoredCircleHoleHelper and AnchoredRectHoleHelper
6 | - negative button is now left button
7 | - right button is now right button
8 |
9 | ## 0.0.1
10 | - initial version
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2021 Apparence.io
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | # Pal widgets
14 | A flutter package for better onboarding.
15 |
16 | A set of amazing onboarding widgets for your flutter applications.
17 |
18 | ## Install package
19 | add in your pubspec.yaml dependencies
20 |
21 | ```pal-widgets: 0.3.0```
22 |
23 | ----
24 |
25 | ## List of helpers
26 |
27 | | Name | description | status |
28 | |----------------|----------------------------------------------|-----------|
29 | | AnchoredHelper | An helper do describe another widget of your page. This creates an overlay above your page | :white_check_mark: |
30 |
31 | ## Anchored helper
32 | This widgets is highly inspired by google onboardings. This shows a circle around any widgets you want and add an overlay were you can add
33 | a message.
34 |
35 | First embbed you page or your app with a **HelperOrchestrator** widget.
36 | ```dart
37 | HelperOrchestrator(
38 | child: Scaffold(
39 | appBar: AppBar(
40 | title: Text(widget.title),
41 | ),
42 | body: ...
43 | ),
44 | )
45 | ```
46 |
47 | You now have to get a key *text1* for the widget you want to reference using
48 | ```dart
49 | Text(
50 | '$_counter',
51 | key: HelperOrchestrator.of(context).generateKey('text1'),
52 | ),
53 | ```
54 |
55 | ### Example
56 | You can now show an anchored helper using
57 | ```dart
58 | final helper = AnchoredHelper(
59 | title: const Text('Title lorem pitume'),
60 | description: const Text('Lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum'),,
61 | bgColor: Colors.blue,
62 | leftBtnText: const Text('cancel'),
63 | rightBtnText: const Text('Ok, understood'),
64 | onError: () => print("widget not found"),
65 | positivBtnStyle: helperOutlineBtnStyle,
66 | negativeBtnStyle: helperOutlineBtnStyle,
67 | onLeftBtnTap: () => HelperOrchestrator.of(context).hideHelper(),
68 | onRightTap: () => HelperOrchestrator.of(context).hideHelper(),
69 | onTapAnchor: () => HelperOrchestrator.of(context).hideHelper(),
70 | );
71 |
72 | // this will show an overlayed anchored widget using the key text1
73 | HelperOrchestrator.of(context).showAnchoredHelper('text1', helper);
74 | ```
75 |
76 | ### properties
77 |
78 | | Property | description | Required |
79 | |--------------------|----------------------------------------------|-------------|
80 | | title | A Text widget to show as title | |
81 | | description | A Text widget to show as description | |
82 | | bgColor | A Color as Overlayed background | :white_check_mark: |
83 | | leftBtnText | A [Text] widget to show within the left button | |
84 | | rightBtnText | A [Text] widget to show within the right button | |
85 | | onLeftBtnTap | Action on tap left button | |
86 | | onRightTap | Action on tap right button | |
87 | | onError | Functions to call when widgets encounters any errors | |
88 | | leftBtnStyle | Material style to put on negativ button | |
89 | | rightBtnStyle | Material style to put on positiv button | |
90 | | onTapAnchor | Action to call when user tap on anchor | |
91 | | widgetFactory | The Widget factory to create the anchored helper. Default to AnchoredCircleHoleHelper.anchorFactory which creates a full overlay with an animated hole | |
92 |
93 | There is two Hole Anchor widget factories :
94 | - ```AnchoredCircleHoleHelper.anchorFactory```: creates a circle hole (default value)
95 | - ```AnchoredRectHoleHelper.anchorFactory``` : creates a rectangle hole on strict widget bounds
96 |
97 | ## Any question?
98 | Contact us on [our twitter](https://twitter.com/PalFlutter)
99 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:flutter_lints/flutter.yaml
2 |
3 | # Additional information about this file can be found at
4 | # https://dart.dev/guides/language/analysis-options
5 |
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | ignore:
2 | - "lib/anchored/painters/**"
--------------------------------------------------------------------------------
/doc/img/pal-showcase.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/doc/img/pal-showcase.jpg
--------------------------------------------------------------------------------
/doc/pal-anchored.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/doc/pal-anchored.gif
--------------------------------------------------------------------------------
/doc/pal-anchored.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/doc/pal-anchored.mp4
--------------------------------------------------------------------------------
/doc/pal-anchored2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/doc/pal-anchored2.gif
--------------------------------------------------------------------------------
/doc/pal-anchored2.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/doc/pal-anchored2.mp4
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: "abb292a07e20d696c4568099f918f6c5f330e6b0"
8 | channel: "stable"
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
17 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
18 | - platform: android
19 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
20 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
21 | - platform: ios
22 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
23 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
24 | - platform: linux
25 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
26 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
27 | - platform: macos
28 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
29 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
30 | - platform: web
31 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
32 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
33 | - platform: windows
34 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
35 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0
36 |
37 | # User provided section
38 |
39 | # List of Local paths (relative to this file) that should be
40 | # ignored by the migrate tool.
41 | #
42 | # Files that are not part of the templates will be ignored by default.
43 | unmanaged_files:
44 | - 'lib/main.dart'
45 | - 'ios/Runner.xcodeproj/project.pbxproj'
46 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # pal_widgets_example
2 |
3 | A new Flutter project.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "kotlin-android"
4 | id "dev.flutter.flutter-gradle-plugin"
5 | }
6 |
7 | def localProperties = new Properties()
8 | def localPropertiesFile = rootProject.file('local.properties')
9 | if (localPropertiesFile.exists()) {
10 | localPropertiesFile.withReader('UTF-8') { reader ->
11 | localProperties.load(reader)
12 | }
13 | }
14 |
15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
16 | if (flutterVersionCode == null) {
17 | flutterVersionCode = '1'
18 | }
19 |
20 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
21 | if (flutterVersionName == null) {
22 | flutterVersionName = '1.0'
23 | }
24 |
25 | android {
26 | namespace "com.example.example"
27 | compileSdk flutter.compileSdkVersion
28 | ndkVersion flutter.ndkVersion
29 |
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_8
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = '1.8'
37 | }
38 |
39 | sourceSets {
40 | main.java.srcDirs += 'src/main/kotlin'
41 | }
42 |
43 | defaultConfig {
44 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
45 | applicationId "com.example.example"
46 | // You can update the following values to match your application needs.
47 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
48 | minSdkVersion flutter.minSdkVersion
49 | targetSdkVersion flutter.targetSdkVersion
50 | versionCode flutterVersionCode.toInteger()
51 | versionName flutterVersionName
52 | }
53 |
54 | buildTypes {
55 | release {
56 | // TODO: Add your own signing config for the release build.
57 | // Signing with the debug keys for now, so `flutter run --release` works.
58 | signingConfig signingConfigs.debug
59 | }
60 | }
61 | }
62 |
63 | flutter {
64 | source '../..'
65 | }
66 |
67 | dependencies {}
68 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
14 |
18 |
22 |
23 |
24 |
25 |
26 |
27 |
29 |
32 |
33 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity()
6 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | rootProject.buildDir = '../build'
9 | subprojects {
10 | project.buildDir = "${rootProject.buildDir}/${project.name}"
11 | }
12 | subprojects {
13 | project.evaluationDependsOn(':app')
14 | }
15 |
16 | tasks.register("clean", Delete) {
17 | delete rootProject.buildDir
18 | }
19 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx4G
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
6 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }
9 | settings.ext.flutterSdkPath = flutterSdkPath()
10 |
11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
12 |
13 | repositories {
14 | google()
15 | mavenCentral()
16 | gradlePluginPortal()
17 | }
18 | }
19 |
20 | plugins {
21 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
22 | id "com.android.application" version "7.3.0" apply false
23 | id "org.jetbrains.kotlin.android" version "1.7.10" apply false
24 | }
25 |
26 | include ":app"
27 |
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 54;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXContainerItemProxy section */
20 | 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
21 | isa = PBXContainerItemProxy;
22 | containerPortal = 97C146E61CF9000F007C117D /* Project object */;
23 | proxyType = 1;
24 | remoteGlobalIDString = 97C146ED1CF9000F007C117D;
25 | remoteInfo = Runner;
26 | };
27 | /* End PBXContainerItemProxy section */
28 |
29 | /* Begin PBXCopyFilesBuildPhase section */
30 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
31 | isa = PBXCopyFilesBuildPhase;
32 | buildActionMask = 2147483647;
33 | dstPath = "";
34 | dstSubfolderSpec = 10;
35 | files = (
36 | );
37 | name = "Embed Frameworks";
38 | runOnlyForDeploymentPostprocessing = 0;
39 | };
40 | /* End PBXCopyFilesBuildPhase section */
41 |
42 | /* Begin PBXFileReference section */
43 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
44 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
45 | 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
46 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
47 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
48 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
49 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
50 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
51 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
52 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
53 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
54 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
55 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
56 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
57 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
58 | /* End PBXFileReference section */
59 |
60 | /* Begin PBXFrameworksBuildPhase section */
61 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
62 | isa = PBXFrameworksBuildPhase;
63 | buildActionMask = 2147483647;
64 | files = (
65 | );
66 | runOnlyForDeploymentPostprocessing = 0;
67 | };
68 | /* End PBXFrameworksBuildPhase section */
69 |
70 | /* Begin PBXGroup section */
71 | 331C8082294A63A400263BE5 /* RunnerTests */ = {
72 | isa = PBXGroup;
73 | children = (
74 | 331C807B294A618700263BE5 /* RunnerTests.swift */,
75 | );
76 | path = RunnerTests;
77 | sourceTree = "";
78 | };
79 | 9740EEB11CF90186004384FC /* Flutter */ = {
80 | isa = PBXGroup;
81 | children = (
82 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
83 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
84 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
85 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
86 | );
87 | name = Flutter;
88 | sourceTree = "";
89 | };
90 | 97C146E51CF9000F007C117D = {
91 | isa = PBXGroup;
92 | children = (
93 | 9740EEB11CF90186004384FC /* Flutter */,
94 | 97C146F01CF9000F007C117D /* Runner */,
95 | 97C146EF1CF9000F007C117D /* Products */,
96 | 331C8082294A63A400263BE5 /* RunnerTests */,
97 | );
98 | sourceTree = "";
99 | };
100 | 97C146EF1CF9000F007C117D /* Products */ = {
101 | isa = PBXGroup;
102 | children = (
103 | 97C146EE1CF9000F007C117D /* Runner.app */,
104 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
105 | );
106 | name = Products;
107 | sourceTree = "";
108 | };
109 | 97C146F01CF9000F007C117D /* Runner */ = {
110 | isa = PBXGroup;
111 | children = (
112 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
113 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
114 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
115 | 97C147021CF9000F007C117D /* Info.plist */,
116 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
117 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
118 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
119 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
120 | );
121 | path = Runner;
122 | sourceTree = "";
123 | };
124 | /* End PBXGroup section */
125 |
126 | /* Begin PBXNativeTarget section */
127 | 331C8080294A63A400263BE5 /* RunnerTests */ = {
128 | isa = PBXNativeTarget;
129 | buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
130 | buildPhases = (
131 | 331C807D294A63A400263BE5 /* Sources */,
132 | 331C807F294A63A400263BE5 /* Resources */,
133 | );
134 | buildRules = (
135 | );
136 | dependencies = (
137 | 331C8086294A63A400263BE5 /* PBXTargetDependency */,
138 | );
139 | name = RunnerTests;
140 | productName = RunnerTests;
141 | productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
142 | productType = "com.apple.product-type.bundle.unit-test";
143 | };
144 | 97C146ED1CF9000F007C117D /* Runner */ = {
145 | isa = PBXNativeTarget;
146 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
147 | buildPhases = (
148 | 9740EEB61CF901F6004384FC /* Run Script */,
149 | 97C146EA1CF9000F007C117D /* Sources */,
150 | 97C146EB1CF9000F007C117D /* Frameworks */,
151 | 97C146EC1CF9000F007C117D /* Resources */,
152 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
153 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
154 | );
155 | buildRules = (
156 | );
157 | dependencies = (
158 | );
159 | name = Runner;
160 | productName = Runner;
161 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
162 | productType = "com.apple.product-type.application";
163 | };
164 | /* End PBXNativeTarget section */
165 |
166 | /* Begin PBXProject section */
167 | 97C146E61CF9000F007C117D /* Project object */ = {
168 | isa = PBXProject;
169 | attributes = {
170 | BuildIndependentTargetsInParallel = YES;
171 | LastUpgradeCheck = 1510;
172 | ORGANIZATIONNAME = "";
173 | TargetAttributes = {
174 | 331C8080294A63A400263BE5 = {
175 | CreatedOnToolsVersion = 14.0;
176 | TestTargetID = 97C146ED1CF9000F007C117D;
177 | };
178 | 97C146ED1CF9000F007C117D = {
179 | CreatedOnToolsVersion = 7.3.1;
180 | LastSwiftMigration = 1100;
181 | };
182 | };
183 | };
184 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
185 | compatibilityVersion = "Xcode 9.3";
186 | developmentRegion = en;
187 | hasScannedForEncodings = 0;
188 | knownRegions = (
189 | en,
190 | Base,
191 | );
192 | mainGroup = 97C146E51CF9000F007C117D;
193 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
194 | projectDirPath = "";
195 | projectRoot = "";
196 | targets = (
197 | 97C146ED1CF9000F007C117D /* Runner */,
198 | 331C8080294A63A400263BE5 /* RunnerTests */,
199 | );
200 | };
201 | /* End PBXProject section */
202 |
203 | /* Begin PBXResourcesBuildPhase section */
204 | 331C807F294A63A400263BE5 /* Resources */ = {
205 | isa = PBXResourcesBuildPhase;
206 | buildActionMask = 2147483647;
207 | files = (
208 | );
209 | runOnlyForDeploymentPostprocessing = 0;
210 | };
211 | 97C146EC1CF9000F007C117D /* Resources */ = {
212 | isa = PBXResourcesBuildPhase;
213 | buildActionMask = 2147483647;
214 | files = (
215 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
216 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
217 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
218 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
219 | );
220 | runOnlyForDeploymentPostprocessing = 0;
221 | };
222 | /* End PBXResourcesBuildPhase section */
223 |
224 | /* Begin PBXShellScriptBuildPhase section */
225 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
226 | isa = PBXShellScriptBuildPhase;
227 | alwaysOutOfDate = 1;
228 | buildActionMask = 2147483647;
229 | files = (
230 | );
231 | inputPaths = (
232 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
233 | );
234 | name = "Thin Binary";
235 | outputPaths = (
236 | );
237 | runOnlyForDeploymentPostprocessing = 0;
238 | shellPath = /bin/sh;
239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
240 | };
241 | 9740EEB61CF901F6004384FC /* Run Script */ = {
242 | isa = PBXShellScriptBuildPhase;
243 | alwaysOutOfDate = 1;
244 | buildActionMask = 2147483647;
245 | files = (
246 | );
247 | inputPaths = (
248 | );
249 | name = "Run Script";
250 | outputPaths = (
251 | );
252 | runOnlyForDeploymentPostprocessing = 0;
253 | shellPath = /bin/sh;
254 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
255 | };
256 | /* End PBXShellScriptBuildPhase section */
257 |
258 | /* Begin PBXSourcesBuildPhase section */
259 | 331C807D294A63A400263BE5 /* Sources */ = {
260 | isa = PBXSourcesBuildPhase;
261 | buildActionMask = 2147483647;
262 | files = (
263 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
264 | );
265 | runOnlyForDeploymentPostprocessing = 0;
266 | };
267 | 97C146EA1CF9000F007C117D /* Sources */ = {
268 | isa = PBXSourcesBuildPhase;
269 | buildActionMask = 2147483647;
270 | files = (
271 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
272 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
273 | );
274 | runOnlyForDeploymentPostprocessing = 0;
275 | };
276 | /* End PBXSourcesBuildPhase section */
277 |
278 | /* Begin PBXTargetDependency section */
279 | 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
280 | isa = PBXTargetDependency;
281 | target = 97C146ED1CF9000F007C117D /* Runner */;
282 | targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
283 | };
284 | /* End PBXTargetDependency section */
285 |
286 | /* Begin PBXVariantGroup section */
287 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
288 | isa = PBXVariantGroup;
289 | children = (
290 | 97C146FB1CF9000F007C117D /* Base */,
291 | );
292 | name = Main.storyboard;
293 | sourceTree = "";
294 | };
295 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
296 | isa = PBXVariantGroup;
297 | children = (
298 | 97C147001CF9000F007C117D /* Base */,
299 | );
300 | name = LaunchScreen.storyboard;
301 | sourceTree = "";
302 | };
303 | /* End PBXVariantGroup section */
304 |
305 | /* Begin XCBuildConfiguration section */
306 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ALWAYS_SEARCH_USER_PATHS = NO;
310 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
311 | CLANG_ANALYZER_NONNULL = YES;
312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
313 | CLANG_CXX_LIBRARY = "libc++";
314 | CLANG_ENABLE_MODULES = YES;
315 | CLANG_ENABLE_OBJC_ARC = YES;
316 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
317 | CLANG_WARN_BOOL_CONVERSION = YES;
318 | CLANG_WARN_COMMA = YES;
319 | CLANG_WARN_CONSTANT_CONVERSION = YES;
320 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
321 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
322 | CLANG_WARN_EMPTY_BODY = YES;
323 | CLANG_WARN_ENUM_CONVERSION = YES;
324 | CLANG_WARN_INFINITE_RECURSION = YES;
325 | CLANG_WARN_INT_CONVERSION = YES;
326 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
327 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
328 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
329 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
330 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
331 | CLANG_WARN_STRICT_PROTOTYPES = YES;
332 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
333 | CLANG_WARN_UNREACHABLE_CODE = YES;
334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
335 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
336 | COPY_PHASE_STRIP = NO;
337 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
338 | ENABLE_NS_ASSERTIONS = NO;
339 | ENABLE_STRICT_OBJC_MSGSEND = YES;
340 | ENABLE_USER_SCRIPT_SANDBOXING = NO;
341 | GCC_C_LANGUAGE_STANDARD = gnu99;
342 | GCC_NO_COMMON_BLOCKS = YES;
343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
345 | GCC_WARN_UNDECLARED_SELECTOR = YES;
346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
347 | GCC_WARN_UNUSED_FUNCTION = YES;
348 | GCC_WARN_UNUSED_VARIABLE = YES;
349 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
350 | MTL_ENABLE_DEBUG_INFO = NO;
351 | SDKROOT = iphoneos;
352 | SUPPORTED_PLATFORMS = iphoneos;
353 | TARGETED_DEVICE_FAMILY = "1,2";
354 | VALIDATE_PRODUCT = YES;
355 | };
356 | name = Profile;
357 | };
358 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
359 | isa = XCBuildConfiguration;
360 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
361 | buildSettings = {
362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
363 | CLANG_ENABLE_MODULES = YES;
364 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
365 | DEVELOPMENT_TEAM = 3P7AMLT6Q9;
366 | ENABLE_BITCODE = NO;
367 | INFOPLIST_FILE = Runner/Info.plist;
368 | LD_RUNPATH_SEARCH_PATHS = (
369 | "$(inherited)",
370 | "@executable_path/Frameworks",
371 | );
372 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
373 | PRODUCT_NAME = "$(TARGET_NAME)";
374 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
375 | SWIFT_VERSION = 5.0;
376 | VERSIONING_SYSTEM = "apple-generic";
377 | };
378 | name = Profile;
379 | };
380 | 331C8088294A63A400263BE5 /* Debug */ = {
381 | isa = XCBuildConfiguration;
382 | buildSettings = {
383 | BUNDLE_LOADER = "$(TEST_HOST)";
384 | CODE_SIGN_STYLE = Automatic;
385 | CURRENT_PROJECT_VERSION = 1;
386 | GENERATE_INFOPLIST_FILE = YES;
387 | MARKETING_VERSION = 1.0;
388 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
389 | PRODUCT_NAME = "$(TARGET_NAME)";
390 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
391 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
392 | SWIFT_VERSION = 5.0;
393 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
394 | };
395 | name = Debug;
396 | };
397 | 331C8089294A63A400263BE5 /* Release */ = {
398 | isa = XCBuildConfiguration;
399 | buildSettings = {
400 | BUNDLE_LOADER = "$(TEST_HOST)";
401 | CODE_SIGN_STYLE = Automatic;
402 | CURRENT_PROJECT_VERSION = 1;
403 | GENERATE_INFOPLIST_FILE = YES;
404 | MARKETING_VERSION = 1.0;
405 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
406 | PRODUCT_NAME = "$(TARGET_NAME)";
407 | SWIFT_VERSION = 5.0;
408 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
409 | };
410 | name = Release;
411 | };
412 | 331C808A294A63A400263BE5 /* Profile */ = {
413 | isa = XCBuildConfiguration;
414 | buildSettings = {
415 | BUNDLE_LOADER = "$(TEST_HOST)";
416 | CODE_SIGN_STYLE = Automatic;
417 | CURRENT_PROJECT_VERSION = 1;
418 | GENERATE_INFOPLIST_FILE = YES;
419 | MARKETING_VERSION = 1.0;
420 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
421 | PRODUCT_NAME = "$(TARGET_NAME)";
422 | SWIFT_VERSION = 5.0;
423 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
424 | };
425 | name = Profile;
426 | };
427 | 97C147031CF9000F007C117D /* Debug */ = {
428 | isa = XCBuildConfiguration;
429 | buildSettings = {
430 | ALWAYS_SEARCH_USER_PATHS = NO;
431 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
432 | CLANG_ANALYZER_NONNULL = YES;
433 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
434 | CLANG_CXX_LIBRARY = "libc++";
435 | CLANG_ENABLE_MODULES = YES;
436 | CLANG_ENABLE_OBJC_ARC = YES;
437 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
438 | CLANG_WARN_BOOL_CONVERSION = YES;
439 | CLANG_WARN_COMMA = YES;
440 | CLANG_WARN_CONSTANT_CONVERSION = YES;
441 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
442 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
443 | CLANG_WARN_EMPTY_BODY = YES;
444 | CLANG_WARN_ENUM_CONVERSION = YES;
445 | CLANG_WARN_INFINITE_RECURSION = YES;
446 | CLANG_WARN_INT_CONVERSION = YES;
447 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
448 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
449 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
450 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
451 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
452 | CLANG_WARN_STRICT_PROTOTYPES = YES;
453 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
454 | CLANG_WARN_UNREACHABLE_CODE = YES;
455 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
456 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
457 | COPY_PHASE_STRIP = NO;
458 | DEBUG_INFORMATION_FORMAT = dwarf;
459 | ENABLE_STRICT_OBJC_MSGSEND = YES;
460 | ENABLE_TESTABILITY = YES;
461 | ENABLE_USER_SCRIPT_SANDBOXING = NO;
462 | GCC_C_LANGUAGE_STANDARD = gnu99;
463 | GCC_DYNAMIC_NO_PIC = NO;
464 | GCC_NO_COMMON_BLOCKS = YES;
465 | GCC_OPTIMIZATION_LEVEL = 0;
466 | GCC_PREPROCESSOR_DEFINITIONS = (
467 | "DEBUG=1",
468 | "$(inherited)",
469 | );
470 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
471 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
472 | GCC_WARN_UNDECLARED_SELECTOR = YES;
473 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
474 | GCC_WARN_UNUSED_FUNCTION = YES;
475 | GCC_WARN_UNUSED_VARIABLE = YES;
476 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
477 | MTL_ENABLE_DEBUG_INFO = YES;
478 | ONLY_ACTIVE_ARCH = YES;
479 | SDKROOT = iphoneos;
480 | TARGETED_DEVICE_FAMILY = "1,2";
481 | };
482 | name = Debug;
483 | };
484 | 97C147041CF9000F007C117D /* Release */ = {
485 | isa = XCBuildConfiguration;
486 | buildSettings = {
487 | ALWAYS_SEARCH_USER_PATHS = NO;
488 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
489 | CLANG_ANALYZER_NONNULL = YES;
490 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
491 | CLANG_CXX_LIBRARY = "libc++";
492 | CLANG_ENABLE_MODULES = YES;
493 | CLANG_ENABLE_OBJC_ARC = YES;
494 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
495 | CLANG_WARN_BOOL_CONVERSION = YES;
496 | CLANG_WARN_COMMA = YES;
497 | CLANG_WARN_CONSTANT_CONVERSION = YES;
498 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
499 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
500 | CLANG_WARN_EMPTY_BODY = YES;
501 | CLANG_WARN_ENUM_CONVERSION = YES;
502 | CLANG_WARN_INFINITE_RECURSION = YES;
503 | CLANG_WARN_INT_CONVERSION = YES;
504 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
505 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
506 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
507 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
508 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
509 | CLANG_WARN_STRICT_PROTOTYPES = YES;
510 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
511 | CLANG_WARN_UNREACHABLE_CODE = YES;
512 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
513 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
514 | COPY_PHASE_STRIP = NO;
515 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
516 | ENABLE_NS_ASSERTIONS = NO;
517 | ENABLE_STRICT_OBJC_MSGSEND = YES;
518 | ENABLE_USER_SCRIPT_SANDBOXING = NO;
519 | GCC_C_LANGUAGE_STANDARD = gnu99;
520 | GCC_NO_COMMON_BLOCKS = YES;
521 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
522 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
523 | GCC_WARN_UNDECLARED_SELECTOR = YES;
524 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
525 | GCC_WARN_UNUSED_FUNCTION = YES;
526 | GCC_WARN_UNUSED_VARIABLE = YES;
527 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
528 | MTL_ENABLE_DEBUG_INFO = NO;
529 | SDKROOT = iphoneos;
530 | SUPPORTED_PLATFORMS = iphoneos;
531 | SWIFT_COMPILATION_MODE = wholemodule;
532 | SWIFT_OPTIMIZATION_LEVEL = "-O";
533 | TARGETED_DEVICE_FAMILY = "1,2";
534 | VALIDATE_PRODUCT = YES;
535 | };
536 | name = Release;
537 | };
538 | 97C147061CF9000F007C117D /* Debug */ = {
539 | isa = XCBuildConfiguration;
540 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
541 | buildSettings = {
542 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
543 | CLANG_ENABLE_MODULES = YES;
544 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
545 | DEVELOPMENT_TEAM = 3P7AMLT6Q9;
546 | ENABLE_BITCODE = NO;
547 | INFOPLIST_FILE = Runner/Info.plist;
548 | LD_RUNPATH_SEARCH_PATHS = (
549 | "$(inherited)",
550 | "@executable_path/Frameworks",
551 | );
552 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
553 | PRODUCT_NAME = "$(TARGET_NAME)";
554 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
555 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
556 | SWIFT_VERSION = 5.0;
557 | VERSIONING_SYSTEM = "apple-generic";
558 | };
559 | name = Debug;
560 | };
561 | 97C147071CF9000F007C117D /* Release */ = {
562 | isa = XCBuildConfiguration;
563 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
564 | buildSettings = {
565 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
566 | CLANG_ENABLE_MODULES = YES;
567 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
568 | DEVELOPMENT_TEAM = 3P7AMLT6Q9;
569 | ENABLE_BITCODE = NO;
570 | INFOPLIST_FILE = Runner/Info.plist;
571 | LD_RUNPATH_SEARCH_PATHS = (
572 | "$(inherited)",
573 | "@executable_path/Frameworks",
574 | );
575 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
576 | PRODUCT_NAME = "$(TARGET_NAME)";
577 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
578 | SWIFT_VERSION = 5.0;
579 | VERSIONING_SYSTEM = "apple-generic";
580 | };
581 | name = Release;
582 | };
583 | /* End XCBuildConfiguration section */
584 |
585 | /* Begin XCConfigurationList section */
586 | 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
587 | isa = XCConfigurationList;
588 | buildConfigurations = (
589 | 331C8088294A63A400263BE5 /* Debug */,
590 | 331C8089294A63A400263BE5 /* Release */,
591 | 331C808A294A63A400263BE5 /* Profile */,
592 | );
593 | defaultConfigurationIsVisible = 0;
594 | defaultConfigurationName = Release;
595 | };
596 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
597 | isa = XCConfigurationList;
598 | buildConfigurations = (
599 | 97C147031CF9000F007C117D /* Debug */,
600 | 97C147041CF9000F007C117D /* Release */,
601 | 249021D3217E4FDB00AE95B9 /* Profile */,
602 | );
603 | defaultConfigurationIsVisible = 0;
604 | defaultConfigurationName = Release;
605 | };
606 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
607 | isa = XCConfigurationList;
608 | buildConfigurations = (
609 | 97C147061CF9000F007C117D /* Debug */,
610 | 97C147071CF9000F007C117D /* Release */,
611 | 249021D4217E4FDB00AE95B9 /* Profile */,
612 | );
613 | defaultConfigurationIsVisible = 0;
614 | defaultConfigurationName = Release;
615 | };
616 | /* End XCConfigurationList section */
617 | };
618 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
619 | }
620 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
65 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Apparence-io/pal-widgets/a9aba9be1dad29c09ab88ae1100302db36cca64b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | 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.
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleDisplayName
8 | Example
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | example
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(FLUTTER_BUILD_NUMBER)
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | CADisableMinimumFrameDurationOnPhone
45 |
46 | UIApplicationSupportsIndirectInputEvents
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/example/ios/RunnerTests/RunnerTests.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 | import XCTest
4 |
5 | class RunnerTests: XCTestCase {
6 |
7 | func testExample() {
8 | // If you add code to the Runner application, consider adding tests here.
9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:pal_widgets/pal_widgets.dart';
3 |
4 | void main() {
5 | runApp(const MyApp());
6 | }
7 |
8 | class MyApp extends StatelessWidget {
9 | const MyApp({Key? key}) : super(key: key);
10 |
11 | // This widget is the root of your application.
12 | @override
13 | Widget build(BuildContext context) {
14 | return MaterialApp(
15 | title: 'Flutter Demo',
16 | theme: ThemeData(
17 | primarySwatch: Colors.blue,
18 | splashFactory: InkSplash.splashFactory,
19 | ),
20 | home: const HelperOrchestrator(
21 | child: MyHomePage(title: 'Flutter Demo Home Page'),
22 | ),
23 | );
24 | }
25 | }
26 |
27 | class MyHomePage extends StatefulWidget {
28 | const MyHomePage({Key? key, required this.title}) : super(key: key);
29 |
30 | final String title;
31 |
32 | @override
33 | State createState() => _MyHomePageState();
34 | }
35 |
36 | class _MyHomePageState extends State {
37 | int _counter = 0;
38 |
39 | void _incrementCounter() {
40 | setState(() {
41 | _counter++;
42 | });
43 | }
44 |
45 | @override
46 | void initState() {
47 | super.initState();
48 | Future.delayed(const Duration(milliseconds: 500), () {
49 | HelperOrchestrator.of(context).showAnchoredHelper('btn', helper);
50 | });
51 | }
52 |
53 | ButtonStyle get helperOutlineBtnStyle => OutlinedButton.styleFrom(
54 | padding: const EdgeInsets.all(12),
55 | textStyle: const TextStyle(
56 | color: Colors.white,
57 | fontSize: 18,
58 | ),
59 | side: const BorderSide(
60 | width: 1.0,
61 | style: BorderStyle.solid,
62 | color: Colors.white,
63 | ),
64 | shape: RoundedRectangleBorder(
65 | borderRadius: BorderRadius.circular(8.0),
66 | ),
67 | );
68 |
69 | @override
70 | Widget build(BuildContext context) {
71 | return Scaffold(
72 | appBar: AppBar(
73 | title: Text(widget.title),
74 | ),
75 | body: Center(
76 | child: Column(
77 | mainAxisAlignment: MainAxisAlignment.center,
78 | children: [
79 | const Text(
80 | 'You have pushed the button this many times:',
81 | ),
82 | Text(
83 | '$_counter',
84 | style: const TextStyle(color: Colors.blue, fontSize: 32),
85 | ),
86 | Text(
87 | 'test widget helper',
88 | key: HelperOrchestrator.of(context).generateKey('text2'),
89 | ),
90 | const SizedBox(height: 21),
91 | OutlinedButton(
92 | onPressed: () {
93 | HelperOrchestrator.of(context)
94 | .showAnchoredHelper('btn', helper);
95 | },
96 | child: const Text('push me 2'),
97 | key: HelperOrchestrator.of(context).generateKey('btn'),
98 | ),
99 | ],
100 | ),
101 | ),
102 | floatingActionButton: FloatingActionButton(
103 | // key: HelperOrchestrator.of(context).generateKey('btn'),
104 | onPressed: _incrementCounter,
105 | tooltip: 'Increment',
106 | child: const Icon(Icons.add),
107 | ),
108 | );
109 | }
110 |
111 | AnchoredHelper get helper => AnchoredHelper(
112 | title: const Text(
113 | 'Push to increment',
114 | textAlign: TextAlign.center,
115 | style: TextStyle(
116 | color: Colors.white,
117 | fontSize: 32,
118 | ),
119 | ),
120 | description: const Text(
121 | 'Tap on this button to increment the flutter demo counter',
122 | textAlign: TextAlign.center,
123 | style: TextStyle(
124 | color: Colors.white,
125 | fontSize: 21,
126 | ),
127 | ),
128 | bgColor: Colors.blue,
129 | // leftBtnText: const Text('cancel'),
130 | // rightBtnText: const Text('Ok, understood'),
131 | onError: () {
132 | // do as you want if error happens
133 | },
134 | rightBtnStyle: helperOutlineBtnStyle,
135 | leftBtnStyle: helperOutlineBtnStyle,
136 | // onRightTap: () => HelperOrchestrator.of(context).hideHelper(),
137 | // onLeftBtnTap: () => HelperOrchestrator.of(context).hideHelper(),
138 | onTapAnchor: () => HelperOrchestrator.of(context).hideHelper(),
139 | widgetFactory: AnchoredCircleHoleHelper.anchorFactory,
140 | );
141 | }
142 |
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "2.11.0"
12 | boolean_selector:
13 | dependency: transitive
14 | description:
15 | name: boolean_selector
16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.1.1"
20 | characters:
21 | dependency: transitive
22 | description:
23 | name: characters
24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "1.3.0"
28 | clock:
29 | dependency: transitive
30 | description:
31 | name: clock
32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.1.1"
36 | collection:
37 | dependency: transitive
38 | description:
39 | name: collection
40 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.18.0"
44 | cupertino_icons:
45 | dependency: "direct main"
46 | description:
47 | name: cupertino_icons
48 | sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.0.8"
52 | fake_async:
53 | dependency: transitive
54 | description:
55 | name: fake_async
56 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
57 | url: "https://pub.dev"
58 | source: hosted
59 | version: "1.3.1"
60 | flutter:
61 | dependency: "direct main"
62 | description: flutter
63 | source: sdk
64 | version: "0.0.0"
65 | flutter_lints:
66 | dependency: "direct dev"
67 | description:
68 | name: flutter_lints
69 | sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493
70 | url: "https://pub.dev"
71 | source: hosted
72 | version: "1.0.4"
73 | flutter_test:
74 | dependency: "direct dev"
75 | description: flutter
76 | source: sdk
77 | version: "0.0.0"
78 | leak_tracker:
79 | dependency: transitive
80 | description:
81 | name: leak_tracker
82 | sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
83 | url: "https://pub.dev"
84 | source: hosted
85 | version: "10.0.4"
86 | leak_tracker_flutter_testing:
87 | dependency: transitive
88 | description:
89 | name: leak_tracker_flutter_testing
90 | sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
91 | url: "https://pub.dev"
92 | source: hosted
93 | version: "3.0.3"
94 | leak_tracker_testing:
95 | dependency: transitive
96 | description:
97 | name: leak_tracker_testing
98 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
99 | url: "https://pub.dev"
100 | source: hosted
101 | version: "3.0.1"
102 | lints:
103 | dependency: transitive
104 | description:
105 | name: lints
106 | sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c
107 | url: "https://pub.dev"
108 | source: hosted
109 | version: "1.0.1"
110 | matcher:
111 | dependency: transitive
112 | description:
113 | name: matcher
114 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
115 | url: "https://pub.dev"
116 | source: hosted
117 | version: "0.12.16+1"
118 | material_color_utilities:
119 | dependency: transitive
120 | description:
121 | name: material_color_utilities
122 | sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
123 | url: "https://pub.dev"
124 | source: hosted
125 | version: "0.8.0"
126 | meta:
127 | dependency: transitive
128 | description:
129 | name: meta
130 | sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
131 | url: "https://pub.dev"
132 | source: hosted
133 | version: "1.12.0"
134 | pal_widgets:
135 | dependency: "direct main"
136 | description:
137 | path: ".."
138 | relative: true
139 | source: path
140 | version: "0.2.0"
141 | path:
142 | dependency: transitive
143 | description:
144 | name: path
145 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
146 | url: "https://pub.dev"
147 | source: hosted
148 | version: "1.9.0"
149 | sky_engine:
150 | dependency: transitive
151 | description: flutter
152 | source: sdk
153 | version: "0.0.99"
154 | source_span:
155 | dependency: transitive
156 | description:
157 | name: source_span
158 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
159 | url: "https://pub.dev"
160 | source: hosted
161 | version: "1.10.0"
162 | stack_trace:
163 | dependency: transitive
164 | description:
165 | name: stack_trace
166 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
167 | url: "https://pub.dev"
168 | source: hosted
169 | version: "1.11.1"
170 | stream_channel:
171 | dependency: transitive
172 | description:
173 | name: stream_channel
174 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
175 | url: "https://pub.dev"
176 | source: hosted
177 | version: "2.1.2"
178 | string_scanner:
179 | dependency: transitive
180 | description:
181 | name: string_scanner
182 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
183 | url: "https://pub.dev"
184 | source: hosted
185 | version: "1.2.0"
186 | term_glyph:
187 | dependency: transitive
188 | description:
189 | name: term_glyph
190 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
191 | url: "https://pub.dev"
192 | source: hosted
193 | version: "1.2.1"
194 | test_api:
195 | dependency: transitive
196 | description:
197 | name: test_api
198 | sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
199 | url: "https://pub.dev"
200 | source: hosted
201 | version: "0.7.0"
202 | vector_math:
203 | dependency: transitive
204 | description:
205 | name: vector_math
206 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
207 | url: "https://pub.dev"
208 | source: hosted
209 | version: "2.1.4"
210 | vm_service:
211 | dependency: transitive
212 | description:
213 | name: vm_service
214 | sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
215 | url: "https://pub.dev"
216 | source: hosted
217 | version: "14.2.1"
218 | sdks:
219 | dart: ">=3.3.0 <4.0.0"
220 | flutter: ">=3.18.0-18.0.pre.54"
221 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: pal_widgets_example
2 | description: A new Flutter project.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `flutter pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 1.0.0+1
19 |
20 | environment:
21 | sdk: ">=2.12.0 <3.0.0"
22 |
23 | # Dependencies specify other packages that your package needs in order to work.
24 | # To automatically upgrade your package dependencies to the latest versions
25 | # consider running `flutter pub upgrade --major-versions`. Alternatively,
26 | # dependencies can be manually updated by changing the version numbers below to
27 | # the latest version available on pub.dev. To see which dependencies have newer
28 | # versions available, run `flutter pub outdated`.
29 | dependencies:
30 | flutter:
31 | sdk: flutter
32 | pal_widgets:
33 | path: ../
34 |
35 |
36 | # The following adds the Cupertino Icons font to your application.
37 | # Use with the CupertinoIcons class for iOS style icons.
38 | cupertino_icons: ^1.0.2
39 |
40 | dev_dependencies:
41 | flutter_test:
42 | sdk: flutter
43 |
44 | # The "flutter_lints" package below contains a set of recommended lints to
45 | # encourage good coding practices. The lint set provided by the package is
46 | # activated in the `analysis_options.yaml` file located at the root of your
47 | # package. See that file for information about deactivating specific lint
48 | # rules and activating additional ones.
49 | flutter_lints: ^1.0.0
50 |
51 | # For information on the generic Dart part of this file, see the
52 | # following page: https://dart.dev/tools/pub/pubspec
53 |
54 | # The following section is specific to Flutter.
55 | flutter:
56 |
57 | # The following line ensures that the Material Icons font is
58 | # included with your application, so that you can use the icons in
59 | # the material Icons class.
60 | uses-material-design: true
61 |
62 | # To add assets to your application, add an assets section, like this:
63 | # assets:
64 | # - images/a_dot_burr.jpeg
65 | # - images/a_dot_ham.jpeg
66 |
67 | # An image asset can refer to one or more resolution-specific "variants", see
68 | # https://flutter.dev/assets-and-images/#resolution-aware.
69 |
70 | # For details regarding adding assets from package dependencies, see
71 | # https://flutter.dev/assets-and-images/#from-packages
72 |
73 | # To add custom fonts to your application, add a fonts section here,
74 | # in this "flutter" section. Each entry in this list should have a
75 | # "family" key with the font family name, and a "fonts" key with a
76 | # list giving the asset and other descriptors for the font. For
77 | # example:
78 | # fonts:
79 | # - family: Schyler
80 | # fonts:
81 | # - asset: fonts/Schyler-Regular.ttf
82 | # - asset: fonts/Schyler-Italic.ttf
83 | # style: italic
84 | # - family: Trajan Pro
85 | # fonts:
86 | # - asset: fonts/TrajanPro.ttf
87 | # - asset: fonts/TrajanPro_Bold.ttf
88 | # weight: 700
89 | #
90 | # For details regarding fonts from package dependencies,
91 | # see https://flutter.dev/custom-fonts/#from-packages
92 |
--------------------------------------------------------------------------------
/lib/anchored/anchor_model.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | class Anchor {
4 | final Size size;
5 | final Offset offset;
6 | final Rect rect;
7 |
8 | const Anchor({
9 | required this.size,
10 | required this.offset,
11 | required this.rect,
12 | });
13 | }
14 |
--------------------------------------------------------------------------------
/lib/anchored/anchored_helper_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:pal_widgets/animations/pop_anim.dart';
4 |
5 | import 'anchor_model.dart';
6 | import 'painters/anchor_painter.dart';
7 | import 'painters/animated_circle.dart';
8 |
9 | class AnchorHelperWrapper extends InheritedWidget {
10 | final Anchor anchor;
11 |
12 | const AnchorHelperWrapper({
13 | Key? key,
14 | required this.anchor,
15 | required Widget child,
16 | }) : super(key: key, child: child);
17 |
18 | static AnchorHelperWrapper? of(BuildContext context) =>
19 | context.dependOnInheritedWidgetOfExactType();
20 |
21 | @override
22 | bool updateShouldNotify(covariant InheritedWidget oldWidget) => true;
23 | }
24 |
25 | /// This widget is an helper based on the position of a widget
26 | /// you must have an ancestor of type [HelperOrchestrator]
27 | ///
28 | /// to create a
29 | /// You can change [widgetFactory] to create your own anchored widget or use one of
30 | /// - [AnchoredHoleHelper.anchorFactory]
31 | ///
32 | /// You are free to add a positiv button / negativ button or an onTap function
33 | /// on the anchor.
34 | class AnchoredHelper extends StatefulWidget {
35 | /// A [Text] widget to show as title
36 | final Text? title;
37 |
38 | /// A [Text] widget to show as description
39 | final Text? description;
40 |
41 | /// A Color as Overlayed background
42 | final Color bgColor;
43 |
44 | /// A [Text] widget to show within the left button
45 | final Text? leftBtnText;
46 |
47 | /// A [Text] widget to show within the right button
48 | final Text? rightBtnText;
49 |
50 | /// Functions to call when user tap on left or right button
51 | final Function? onLeftBtnTap, onRightTap, onTapBackground;
52 |
53 | /// Functions to call when widgets encounters any errors
54 | final Function? onError;
55 |
56 | /// Buttons material style
57 | final ButtonStyle? leftBtnStyle, rightBtnStyle;
58 |
59 | /// If you want to use a custom position. Else we will use the [HelperOrchestrator]
60 | /// to get this using the [anchorKeyId]
61 | final Anchor? anchor;
62 |
63 | /// function called when user type on anchor position
64 | final Function? onTapAnchor;
65 |
66 | /// factory to create the all background with the hole
67 | /// Use one of these
68 | /// - [AnchoredCircleHoleHelper.anchorFactory]
69 | /// - [AnchoredRectHoleHelper.anchorFactory]
70 | /// or create yours
71 | final AnchorWidgetFactory widgetFactory;
72 |
73 | const AnchoredHelper({
74 | this.onLeftBtnTap,
75 | this.onRightTap,
76 | this.rightBtnText,
77 | this.leftBtnText,
78 | this.title,
79 | this.description,
80 | this.onError,
81 | this.leftBtnStyle,
82 | this.rightBtnStyle,
83 | this.onTapAnchor,
84 | this.onTapBackground,
85 | Key? key,
86 | required this.bgColor,
87 | this.anchor,
88 | this.widgetFactory = AnchoredCircleHoleHelper.anchorFactory,
89 | }) : super(key: key);
90 |
91 | @override
92 | _AnchoredHelperState createState() => _AnchoredHelperState();
93 | }
94 |
95 | class _AnchoredHelperState extends State
96 | with TickerProviderStateMixin {
97 | late AnimationController anchorAnimationController, fadeAnimController;
98 | late Animation backgroundAnimation;
99 |
100 | Animation? titleOpacityAnimation, titleSizeAnimation;
101 | Animation? descriptionOpacityAnimation, descriptionSizeAnimation;
102 | Animation? btnOpacityAnimation, btnSizeAnimation;
103 |
104 | Anchor get anchor => widget.anchor ?? AnchorHelperWrapper.of(context)!.anchor;
105 |
106 | @override
107 | void initState() {
108 | super.initState();
109 | anchorAnimationController = AnimationController(
110 | vsync: this, duration: const Duration(milliseconds: 650))
111 | ..repeat(reverse: true);
112 | fadeAnimController = AnimationController(
113 | vsync: this, duration: const Duration(milliseconds: 1500));
114 | backgroundAnimation = CurvedAnimation(
115 | parent: fadeAnimController,
116 | curve: const Interval(0, .2, curve: Curves.easeIn),
117 | );
118 | titleOpacityAnimation = CurvedAnimation(
119 | parent: fadeAnimController,
120 | curve: const Interval(.4, .5, curve: Curves.easeIn),
121 | );
122 | titleSizeAnimation = CurvedAnimation(
123 | parent: fadeAnimController,
124 | curve: const Interval(.4, .6, curve: Curves.easeInOutBack),
125 | );
126 | descriptionOpacityAnimation = CurvedAnimation(
127 | parent: fadeAnimController,
128 | curve: const Interval(.5, .6, curve: Curves.easeIn),
129 | );
130 | descriptionSizeAnimation = CurvedAnimation(
131 | parent: fadeAnimController,
132 | curve: const Interval(.5, .7, curve: Curves.easeInOutBack),
133 | );
134 | btnOpacityAnimation = CurvedAnimation(
135 | parent: fadeAnimController,
136 | curve: const Interval(.8, .9, curve: Curves.easeIn),
137 | );
138 | btnSizeAnimation = CurvedAnimation(
139 | parent: fadeAnimController,
140 | curve: const Interval(.8, 1, curve: Curves.easeInOutBack),
141 | );
142 | fadeAnimController.forward();
143 | }
144 |
145 | @override
146 | void dispose() {
147 | anchorAnimationController.stop();
148 | fadeAnimController.stop();
149 | anchorAnimationController.dispose();
150 | fadeAnimController.dispose();
151 | super.dispose();
152 | }
153 |
154 | @override
155 | Widget build(BuildContext context) {
156 | // print("anchor: ${anchor.offset} ${anchor.size}: ${anchor.rect}");
157 | return GestureDetector(
158 | onTap: () async {
159 | if (widget.onTapBackground != null) {
160 | HapticFeedback.selectionClick();
161 | await fadeAnimController.reverse();
162 | widget.onTapBackground!.call();
163 | }
164 | },
165 | child: Material(
166 | color: Colors.transparent,
167 | child: Stack(
168 | children: [
169 | Positioned.fill(
170 | child: FadeTransition(
171 | opacity: backgroundAnimation,
172 | child: widget.widgetFactory.create(
173 | currentPos: anchor.offset,
174 | anchorSize: anchor.size,
175 | bgColor: widget.bgColor,
176 | listenable: anchorAnimationController,
177 | onTap: () async {
178 | if (widget.onTapAnchor != null) {
179 | HapticFeedback.selectionClick();
180 | await fadeAnimController.reverse();
181 | widget.onTapAnchor!();
182 | }
183 | },
184 | ),
185 | ),
186 | ),
187 | Positioned.fromRect(
188 | rect: anchor.rect,
189 | child: LayoutBuilder(
190 | builder: (context, constraints) => Padding(
191 | padding: const EdgeInsets.symmetric(
192 | horizontal: 16.0, vertical: 8.0),
193 | child: ConstrainedBox(
194 | constraints: BoxConstraints(
195 | minHeight: constraints.maxHeight,
196 | ),
197 | child: Column(
198 | mainAxisAlignment: MainAxisAlignment.center,
199 | crossAxisAlignment: CrossAxisAlignment.center,
200 | mainAxisSize: MainAxisSize.min,
201 | children: [
202 | if (widget.title != null)
203 | Padding(
204 | padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
205 | child: PopAnimation(
206 | animation: fadeAnimController,
207 | opacityAnim: titleOpacityAnimation,
208 | sizeAnim: titleSizeAnimation,
209 | child: widget.title!,
210 | ),
211 | ),
212 | if (widget.description != null)
213 | Padding(
214 | padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
215 | child: PopAnimation(
216 | animation: fadeAnimController,
217 | opacityAnim: descriptionOpacityAnimation,
218 | sizeAnim: descriptionSizeAnimation,
219 | child: widget.description!,
220 | ),
221 | ),
222 | const SizedBox(height: 24),
223 | Row(
224 | mainAxisAlignment: MainAxisAlignment.center,
225 | crossAxisAlignment: CrossAxisAlignment.center,
226 | children: [
227 | if (widget.leftBtnText != null &&
228 | widget.onRightTap != null)
229 | Expanded(
230 | child: PopAnimation(
231 | animation: fadeAnimController,
232 | opacityAnim: btnOpacityAnimation,
233 | sizeAnim: btnSizeAnimation,
234 | child: _buildEditableBordered(
235 | widget.leftBtnText!,
236 | widget.onRightTap!,
237 | widget.leftBtnStyle,
238 | ),
239 | ),
240 | ),
241 | const SizedBox(width: 16),
242 | if (widget.rightBtnText != null &&
243 | widget.onLeftBtnTap != null)
244 | Expanded(
245 | child: PopAnimation(
246 | animation: fadeAnimController,
247 | opacityAnim: btnOpacityAnimation,
248 | sizeAnim: btnSizeAnimation,
249 | child: _buildEditableBordered(
250 | widget.rightBtnText!,
251 | widget.onLeftBtnTap!,
252 | widget.rightBtnStyle,
253 | ),
254 | ),
255 | ),
256 | ],
257 | ),
258 | ],
259 | ),
260 | ),
261 | ),
262 | ),
263 | ),
264 | ],
265 | ),
266 | ),
267 | );
268 | }
269 |
270 | Widget _buildEditableBordered(
271 | Text text,
272 | Function onTap,
273 | ButtonStyle? outlineButtonStyle,
274 | ) {
275 | return OutlinedButton(
276 | onPressed: () async {
277 | HapticFeedback.selectionClick();
278 | await fadeAnimController.reverse();
279 | onTap();
280 | },
281 | style: outlineButtonStyle,
282 | child: text,
283 | );
284 | }
285 | }
286 |
--------------------------------------------------------------------------------
/lib/anchored/painters/anchor_painter.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// Used by [AnchoredWidget] to paint the background
4 | abstract class AnchorWidgetFactory {
5 | const AnchorWidgetFactory();
6 |
7 | // @factory
8 | Widget create({
9 | final Offset? currentPos,
10 | final Size? anchorSize,
11 | final Color? bgColor,
12 | final Function? onTap,
13 | final Listenable? listenable,
14 | });
15 | }
16 |
--------------------------------------------------------------------------------
/lib/anchored/painters/anchored_circle_painter.dart:
--------------------------------------------------------------------------------
1 | // coverage:ignore-file
2 | import 'dart:math';
3 |
4 | import 'package:flutter/material.dart';
5 |
6 | class AnchoredCirclePainter extends CustomPainter {
7 | final Offset? currentPos;
8 |
9 | final double padding;
10 |
11 | final Size? anchorSize;
12 |
13 | final Color? bgColor;
14 |
15 | double? radius;
16 | Offset? center;
17 |
18 | double circle1Width, circle2Width;
19 |
20 | AnchoredCirclePainter({
21 | this.currentPos,
22 | this.anchorSize,
23 | this.padding = 0,
24 | this.bgColor,
25 | this.circle1Width = 64,
26 | this.circle2Width = 100,
27 | });
28 |
29 | @override
30 | void paint(Canvas canvas, Size size) {
31 | Paint clearPainter = Paint()
32 | ..blendMode = BlendMode.clear
33 | ..isAntiAlias = true;
34 | Paint bgPainter = Paint()
35 | ..color = bgColor!
36 | ..style = PaintingStyle.fill
37 | ..isAntiAlias = true;
38 | Paint circle1Painter = Paint()
39 | ..color = Colors.white.withOpacity(.3)
40 | ..style = PaintingStyle.stroke
41 | ..strokeWidth = circle1Width
42 | ..isAntiAlias = true;
43 | Paint circle2Painter = Paint()
44 | ..color = Colors.white.withOpacity(.4)
45 | ..style = PaintingStyle.stroke
46 | ..strokeWidth = circle2Width
47 | ..isAntiAlias = true;
48 | canvas.saveLayer(Offset.zero & size, Paint());
49 | canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), bgPainter);
50 | // canvas.drawCircle(currentPos, radius, clearPainter);
51 | // canvas.drawRect(currentPos & anchorSize, clearPainter);
52 | radius = radiusCalc;
53 | center = centerCalc;
54 | canvas.drawCircle(center!, radius! + padding, circle1Painter);
55 | canvas.drawCircle(center!, radius! + padding, circle2Painter);
56 | canvas.drawCircle(center!, radius! + padding, clearPainter);
57 | canvas.restore();
58 | }
59 |
60 | @pragma('vm:prefer-inline')
61 | double get radiusCalc =>
62 | sqrt(pow(anchorSize!.width, 2) + pow(anchorSize!.height, 2)) / 2;
63 |
64 | @pragma('vm:prefer-inline')
65 | Offset get centerCalc => currentPos!.translate(
66 | anchorSize!.width / 2,
67 | anchorSize!.height / 2,
68 | );
69 |
70 | @override
71 | bool shouldRepaint(AnchoredCirclePainter oldDelegate) {
72 | return oldDelegate.currentPos != currentPos ||
73 | oldDelegate.circle1Width != circle1Width ||
74 | oldDelegate.circle2Width != circle2Width ||
75 | oldDelegate.bgColor != bgColor;
76 | }
77 |
78 | @override
79 | bool hitTest(Offset position) {
80 | if (currentPos == null) return false;
81 | var distance = (position - (center ?? centerCalc)).distance;
82 | if (distance <= (radius ?? radiusCalc)) {
83 | return true;
84 | }
85 | return false;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/lib/anchored/painters/anchored_rect_painter.dart:
--------------------------------------------------------------------------------
1 | // coverage:ignore-file
2 | import 'package:flutter/material.dart';
3 |
4 | extension AddSize on Size {
5 | withPadding(double padding) {
6 | return Size(width + padding, height + padding);
7 | }
8 | }
9 |
10 | class AnchoredRectPainter extends CustomPainter {
11 | final Offset? currentPos;
12 |
13 | final double padding;
14 |
15 | final Size? anchorSize;
16 |
17 | final Color? bgColor;
18 |
19 | double? radius;
20 | Offset? center;
21 |
22 | double rect1Width, rect2Width;
23 |
24 | AnchoredRectPainter({
25 | this.currentPos,
26 | this.anchorSize,
27 | this.padding = 0,
28 | this.bgColor,
29 | this.rect1Width = 32,
30 | this.rect2Width = 64,
31 | });
32 |
33 | @override
34 | void paint(Canvas canvas, Size size) {
35 | Paint clearPainter = Paint()
36 | ..blendMode = BlendMode.clear
37 | ..isAntiAlias = true;
38 | Paint bgPainter = Paint()
39 | ..color = bgColor!
40 | ..style = PaintingStyle.fill
41 | ..isAntiAlias = true;
42 | Paint rect1Painter = Paint()
43 | ..color = Colors.white.withOpacity(.3)
44 | ..style = PaintingStyle.stroke
45 | ..strokeWidth = rect1Width
46 | ..isAntiAlias = true;
47 | Paint rect2Painter = Paint()
48 | ..color = Colors.white.withOpacity(.4)
49 | ..style = PaintingStyle.stroke
50 | ..strokeWidth = rect2Width
51 | ..isAntiAlias = true;
52 | canvas.saveLayer(Offset.zero & size, Paint());
53 | canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), bgPainter);
54 |
55 | center = centerCalc;
56 | final rect = Rect.fromLTWH(
57 | currentPos!.dx,
58 | currentPos!.dy,
59 | anchorSize!.width,
60 | anchorSize!.height,
61 | );
62 | canvas.drawRect(rect, rect1Painter);
63 | canvas.drawRect(rect, rect2Painter);
64 | canvas.drawRect(currentPos! & anchorSize!, clearPainter);
65 | canvas.restore();
66 | }
67 |
68 | @pragma('vm:prefer-inline')
69 | Offset get centerCalc => currentPos!.translate(
70 | anchorSize!.width / 2,
71 | anchorSize!.height / 2,
72 | );
73 |
74 | @override
75 | bool shouldRepaint(AnchoredRectPainter oldDelegate) {
76 | return oldDelegate.currentPos != currentPos ||
77 | oldDelegate.rect1Width != rect1Width ||
78 | oldDelegate.rect2Width != rect2Width ||
79 | oldDelegate.bgColor != bgColor;
80 | }
81 |
82 | @override
83 | bool hitTest(Offset position) {
84 | if (currentPos == null) return false;
85 | return (position.dx >= currentPos!.dx &&
86 | position.dx <= (currentPos!.dx + anchorSize!.width) &&
87 | position.dy >= currentPos!.dy &&
88 | position.dy <= (currentPos!.dy + anchorSize!.height));
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/lib/anchored/painters/animated_anchor.dart:
--------------------------------------------------------------------------------
1 | // coverage:ignore-file
2 | import 'package:flutter/material.dart';
3 |
4 | /// Helper explaining a widget
5 | /// this creates a full background widget with a hole of the size of the aimed
6 | /// widget.
7 | abstract class BasicAnchoredHoleHelper extends AnimatedWidget {
8 | final Offset? currentPos;
9 | final double padding;
10 | final Size? anchorSize;
11 | final Color? bgColor;
12 | final Function? onTap;
13 |
14 | final Animation _stroke1Animation, _stroke2Animation;
15 |
16 | CustomPainter get customPainter;
17 |
18 | Animation get stroke1Animation => _stroke1Animation;
19 |
20 | Animation get stroke2Animation => _stroke2Animation;
21 |
22 | BasicAnchoredHoleHelper({
23 | Key? key,
24 | required this.currentPos,
25 | required this.padding,
26 | required this.bgColor,
27 | required this.anchorSize,
28 | required Listenable listenable,
29 | this.onTap,
30 | }) : _stroke1Animation = CurvedAnimation(
31 | parent: listenable as Animation,
32 | curve: Curves.ease,
33 | ),
34 | _stroke2Animation = CurvedAnimation(
35 | parent: listenable,
36 | curve: const Interval(0, .8, curve: Curves.ease),
37 | ),
38 | super(key: key, listenable: listenable);
39 |
40 | @override
41 | Widget build(BuildContext context) {
42 | return GestureDetector(
43 | onTap: () {
44 | if (onTap != null) {
45 | onTap!();
46 | }
47 | },
48 | child: SizedBox(
49 | child: CustomPaint(
50 | painter: customPainter,
51 | ),
52 | ),
53 | );
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/lib/anchored/painters/animated_circle.dart:
--------------------------------------------------------------------------------
1 | // coverage:ignore-file
2 | import 'package:flutter/material.dart';
3 |
4 | import 'anchor_painter.dart';
5 | import 'anchored_circle_painter.dart';
6 | import 'animated_anchor.dart';
7 |
8 | class _AnchoredHoleHelperFactory extends AnchorWidgetFactory {
9 | const _AnchoredHoleHelperFactory();
10 |
11 | @override
12 | Widget create({
13 | Offset? currentPos,
14 | Size? anchorSize,
15 | Color? bgColor,
16 | Function? onTap,
17 | Listenable? listenable,
18 | }) {
19 | return AnchoredCircleHoleHelper(
20 | currentPos: currentPos,
21 | padding: 8,
22 | bgColor: bgColor,
23 | anchorSize: anchorSize,
24 | listenable: listenable!,
25 | onTap: onTap,
26 | );
27 | }
28 | }
29 |
30 | const _circle1Width = 88;
31 | const _circle2Width = 140;
32 |
33 | /// Helper explaining a widget
34 | /// this creates a full background widget with a hole of the size of the aimed
35 | /// widget.
36 | class AnchoredCircleHoleHelper extends BasicAnchoredHoleHelper {
37 | static const AnchorWidgetFactory anchorFactory = _AnchoredHoleHelperFactory();
38 |
39 | AnchoredCircleHoleHelper({
40 | Key? key,
41 | required Offset? currentPos,
42 | required double padding,
43 | required Color? bgColor,
44 | required Size? anchorSize,
45 | required Listenable listenable,
46 | Function? onTap,
47 | }) : super(
48 | key: key,
49 | listenable: listenable,
50 | currentPos: currentPos,
51 | padding: padding,
52 | bgColor: bgColor,
53 | anchorSize: anchorSize,
54 | onTap: onTap,
55 | );
56 |
57 | @override
58 | CustomPainter get customPainter => AnchoredCirclePainter(
59 | currentPos: currentPos,
60 | anchorSize: anchorSize,
61 | padding: padding,
62 | bgColor: bgColor,
63 | circle1Width: stroke1Animation.value * _circle1Width,
64 | circle2Width: stroke2Animation.value * _circle2Width,
65 | );
66 | }
67 |
--------------------------------------------------------------------------------
/lib/anchored/painters/animated_rect.dart:
--------------------------------------------------------------------------------
1 | // coverage:ignore-file
2 | import 'package:flutter/material.dart';
3 |
4 | import 'anchor_painter.dart';
5 | import 'anchored_rect_painter.dart';
6 | import 'animated_anchor.dart';
7 |
8 | class _AnchoredRectHoleHelperFactory extends AnchorWidgetFactory {
9 | const _AnchoredRectHoleHelperFactory();
10 |
11 | @override
12 | Widget create({
13 | Offset? currentPos,
14 | Size? anchorSize,
15 | Color? bgColor,
16 | Function? onTap,
17 | Listenable? listenable,
18 | }) {
19 | return AnchoredRectHoleHelper(
20 | currentPos: currentPos,
21 | padding: 8,
22 | bgColor: bgColor,
23 | anchorSize: anchorSize,
24 | listenable: listenable!,
25 | onTap: onTap,
26 | );
27 | }
28 | }
29 |
30 | const _rect1Width = 16;
31 | const _rect2Width = 32;
32 |
33 | /// Helper explaining a widget
34 | /// this creates a full background widget with a hole of the size of the aimed
35 | /// widget.
36 | class AnchoredRectHoleHelper extends BasicAnchoredHoleHelper {
37 | static const AnchorWidgetFactory anchorFactory =
38 | _AnchoredRectHoleHelperFactory();
39 |
40 | AnchoredRectHoleHelper({
41 | Key? key,
42 | required Offset? currentPos,
43 | required double padding,
44 | required Color? bgColor,
45 | required Size? anchorSize,
46 | required Listenable listenable,
47 | Function? onTap,
48 | }) : super(
49 | key: key,
50 | listenable: listenable,
51 | currentPos: currentPos,
52 | padding: padding,
53 | bgColor: bgColor,
54 | anchorSize: anchorSize,
55 | onTap: onTap,
56 | );
57 |
58 | @override
59 | CustomPainter get customPainter => AnchoredRectPainter(
60 | currentPos: currentPos,
61 | anchorSize: anchorSize,
62 | padding: padding,
63 | bgColor: bgColor,
64 | rect1Width: stroke1Animation.value * _rect1Width,
65 | rect2Width: stroke2Animation.value * _rect2Width,
66 | );
67 | }
68 |
--------------------------------------------------------------------------------
/lib/animations/pop_anim.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class PopAnimation extends StatelessWidget {
4 | final Animation? sizeAnim;
5 | final Animation? opacityAnim;
6 | final Widget child;
7 | final Listenable animation;
8 |
9 | const PopAnimation({
10 | Key? key,
11 | this.sizeAnim,
12 | this.opacityAnim,
13 | required this.child,
14 | required this.animation,
15 | }) : super(key: key);
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return AnimatedBuilder(
20 | animation: animation,
21 | builder: (context, child) => Transform.translate(
22 | offset: Offset(0, -40 + ((sizeAnim?.value ?? 0) * 40)),
23 | child: Transform.scale(
24 | scale: sizeAnim?.value ?? 0,
25 | child: Opacity(
26 | opacity: opacityAnim?.value ?? 0,
27 | child: child,
28 | ),
29 | ),
30 | ),
31 | child: child,
32 | );
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/lib/helper_orchestrator.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:pal_widgets/services/element_finder.dart';
3 |
4 | import 'anchored/anchor_model.dart';
5 | import 'anchored/anchored_helper_widget.dart';
6 | import 'services/overlay_helper.dart';
7 |
8 | /// used to align the helper to the top, bottom, left or right of the anchor
9 | enum HelperAlignment { top, bottom, left, right }
10 |
11 | class _HelperOrchestratorScope extends InheritedWidget {
12 | final HelperOrchestratorState _helperOrchestratorState;
13 |
14 | const _HelperOrchestratorScope({
15 | Key? key,
16 | required Widget child,
17 | required HelperOrchestratorState helperOrchestratorState,
18 | }) : _helperOrchestratorState = helperOrchestratorState,
19 | super(key: key, child: child);
20 |
21 | @override
22 | bool updateShouldNotify(_HelperOrchestratorScope old) =>
23 | _helperOrchestratorState != old._helperOrchestratorState;
24 | }
25 |
26 | /// Manages Pal widgets to display an [Overlay] over your pages
27 | /// this will allow you to register widgets and show [AnchoredHelper] and others
28 | /// Pal onboarding widgets
29 | class HelperOrchestrator extends StatefulWidget {
30 | final Widget child;
31 | final GlobalKey? navigatorKey;
32 |
33 | const HelperOrchestrator({
34 | Key? key,
35 | required this.child,
36 | this.navigatorKey,
37 | }) : super(key: key);
38 |
39 | /// If there is no [HelperOrchestrator] in scope
40 | ///
41 | /// See also:
42 | ///
43 | /// * [maybeOf], which is a similar function but will return null instead of
44 | /// throwing if there is no [HelperOrchestrator] ancestor.
45 | static HelperOrchestratorState of(BuildContext context) {
46 | assert(debugCheckHasScaffoldMessenger(context));
47 |
48 | final _HelperOrchestratorScope scope =
49 | context.dependOnInheritedWidgetOfExactType<_HelperOrchestratorScope>()!;
50 | return scope._helperOrchestratorState;
51 | }
52 |
53 | /// The state from the closest instance of this class that encloses the given
54 | /// context, if any.
55 | ///
56 | /// Will return null if a [HelperOrchestrator] is not found in the given context.
57 | ///
58 | /// See also:
59 | ///
60 | /// * [of], which is a similar function, except that it will throw an
61 | /// exception if a [HelperOrchestrator] is not found in the given context.
62 | static HelperOrchestratorState? maybeOf(BuildContext? context) {
63 | if (context == null) {
64 | return null;
65 | }
66 | final _HelperOrchestratorScope? scope =
67 | context.dependOnInheritedWidgetOfExactType<_HelperOrchestratorScope>();
68 | return scope?._helperOrchestratorState;
69 | }
70 |
71 | @override
72 | HelperOrchestratorState createState() => HelperOrchestratorState();
73 | }
74 |
75 | /// State for [HelperOrchestrator]
76 | ///
77 | /// Can display an helper as overlay above a child using
78 | /// the current [BuildContext] and [HelperOrchestrator.of]
79 | class HelperOrchestratorState extends State {
80 | final Map keys = {};
81 | final OverlayHelper _overlayHelper = OverlayHelper();
82 | ElementFinder? _elementFinder;
83 |
84 | @override
85 | void initState() {
86 | super.initState();
87 | }
88 |
89 | @override
90 | void didChangeDependencies() {
91 | super.didChangeDependencies();
92 | if (widget.navigatorKey != null) {
93 | _elementFinder = ElementFinder(navigatorKey: widget.navigatorKey);
94 | } else {
95 | _elementFinder = ElementFinder(buildContext: context);
96 | }
97 | }
98 |
99 | /// generate a [key] that will be registered within HelperOrchestrator
100 | /// this keyId is associated with the key and can be found again
101 | /// using [getAnchorKey]
102 | /// The key will be used to find the element position when requesting
103 | /// [showAnchoredHelper]
104 | Key generateKey(String key) {
105 | // final uniqueKey = GlobalKey(debugLabel: key);
106 | final uniqueKey = ValueKey(key);
107 | keys[key] = uniqueKey;
108 | return uniqueKey;
109 | }
110 |
111 | /// Returns the [Key] from the KeyId if found.
112 | /// this throws if not found.
113 | Key getAnchorKey(String keyId) {
114 | if (keys.containsKey(keyId)) {
115 | return keys[keyId]!;
116 | }
117 | throw 'Key not found';
118 | }
119 |
120 | /// This shows an [AnchoredHelper] above your page as overlay
121 | ///
122 | /// requires [anchorKeyId] that must have been generated within a widget using
123 | /// ```dart
124 | /// HelperOrchestrator.of(context).generateKey('myKeyId')
125 | /// ```
126 | Future showAnchoredHelper(
127 | String anchorKeyId,
128 | AnchoredHelper helper, {
129 | HelperAlignment? align,
130 | bool isInModal = false,
131 | }) async {
132 | try {
133 | //final key = getAnchorKey(anchorKeyId) as ValueKey;
134 | final anchor =
135 | await findAnchor(anchorKeyId, align: align, isInModal: isInModal);
136 | if (anchor == null) {
137 | debugPrint("anchor cannot be found. show anchored failed");
138 | return;
139 | }
140 | _overlayHelper.showHelper(
141 | context,
142 | (context) => AnchorHelperWrapper(
143 | anchor: anchor,
144 | child: helper,
145 | ),
146 | );
147 | } catch (e, s) {
148 | debugPrint("show anchored helper failed: $e $s");
149 | }
150 | }
151 |
152 | /// Returns an [Anchor] wich contains position, size and rect of the widget
153 | /// containing the key.
154 | ///
155 | /// this requires an [anchorKeyId] to search within our keys
156 | Future findAnchor(
157 | String anchorKeyId, {
158 | HelperAlignment? align,
159 | bool isInModal = false,
160 | }) async {
161 | final element = _elementFinder! //
162 | .searchChildElementByKey(getAnchorKey(anchorKeyId),
163 | isInModal: isInModal);
164 | if (element == null || element.bounds == null) {
165 | debugPrint("anchor not found");
166 | return null;
167 | }
168 | final anchorSize = element.bounds!.size;
169 | final currentPos = element.offset!;
170 | if (align != null) {
171 | return Anchor(
172 | size: anchorSize,
173 | offset: currentPos,
174 | rect: _elementFinder!.getSpaceFromAlignment(align, element),
175 | );
176 | }
177 | return Anchor(
178 | size: anchorSize,
179 | offset: currentPos,
180 | rect: _elementFinder!.getLargestAvailableSpace(element),
181 | );
182 | }
183 |
184 | /// Hide the current overlayed helper
185 | /// this do nothing if there is no helper overlayed.
186 | void hideHelper() {
187 | _overlayHelper.popHelper();
188 | }
189 |
190 | @override
191 | Widget build(BuildContext context) {
192 | return _HelperOrchestratorScope(
193 | helperOrchestratorState: this,
194 | child: widget.child,
195 | );
196 | }
197 | }
198 |
--------------------------------------------------------------------------------
/lib/pal_widgets.dart:
--------------------------------------------------------------------------------
1 | library pal_widgets;
2 |
3 | export 'helper_orchestrator.dart';
4 | export 'anchored/anchored_helper_widget.dart' show AnchoredHelper;
5 | export 'anchored/painters/animated_circle.dart';
6 | export 'anchored/painters/animated_rect.dart';
7 |
--------------------------------------------------------------------------------
/lib/services/element_finder.dart:
--------------------------------------------------------------------------------
1 | import 'package:collection/collection.dart' show IterableExtension;
2 | import 'package:flutter/material.dart';
3 | import 'package:pal_widgets/helper_orchestrator.dart';
4 |
5 | const minWritableSpace = 100;
6 |
7 | /// This class helps you find element within your app
8 | /// use [searchChildElement] with a String containing key value
9 | /// result is available in [result] and returns an [Element]
10 | class ElementFinder {
11 | // Prefer use the navigatorContext to get full context tree
12 | final BuildContext? buildContext;
13 | final GlobalKey? navigatorKey;
14 |
15 | ElementFinder({this.buildContext, this.navigatorKey});
16 |
17 | BuildContext get context {
18 | if (buildContext != null) {
19 | return buildContext!;
20 | }
21 | return navigatorKey!.currentContext!;
22 | }
23 |
24 | Size get screenSize => MediaQuery.of(context).size;
25 |
26 | // this method scan all child recursively to get all widget bounds we could select for an helper
27 | Map scan({Key? omitChildsOf, bool debugMode = false}) {
28 | Map results = {};
29 | context.visitChildElements((element) => _scanChildElement(
30 | context.findRenderObject(), element, results,
31 | omitChildsOf: omitChildsOf, debugMode: debugMode));
32 | return results;
33 | }
34 |
35 | // List all pages from this context
36 | List scanPages() {
37 | var pages = [];
38 | context.visitChildElements((element) =>
39 | _scanPageChildElement(element, pages as List));
40 | return pages as List;
41 | }
42 |
43 | // this method scan all child recursively to find a widget having a key == searchedKey
44 | ElementModel? searchChildElement(String? key) {
45 | ElementModel? result;
46 | context.visitChildElements((element) =>
47 | result = _searchChildElement(context.findRenderObject(), element, key));
48 | if (result == null) {
49 | return ElementModel.empty();
50 | }
51 | return result;
52 | }
53 |
54 | ElementModel? searchChildElementByKey(
55 | Key key, {
56 | required bool isInModal,
57 | }) {
58 | ElementModel? result;
59 | context.visitChildElements((element) => result = _searchChildElementByKey(
60 | context.findRenderObject(),
61 | element,
62 | key,
63 | isInModal: isInModal,
64 | ));
65 | if (result == null) {
66 | return ElementModel.empty();
67 | }
68 | return result;
69 | }
70 |
71 | Rect getSpaceFromAlignment(HelperAlignment align, ElementModel elementModel) {
72 | var parentObject = context.findRenderObject()!;
73 | var element = elementModel.element!;
74 | // var translation = element.renderObject!.getTransformTo(parentObject).getTranslation();
75 | var objectX = elementModel.offset!.dx;
76 | var objectEndX = objectX + element.size!.width;
77 | var objectY = elementModel.offset!.dy;
78 | var objectEndY = objectY + element.size!.height;
79 | var layerRect = parentObject.paintBounds;
80 | switch (align) {
81 | case HelperAlignment.top:
82 | return Rect.fromLTWH(0, 0, layerRect.width, objectY);
83 | case HelperAlignment.bottom:
84 | return Rect.fromLTWH(0, objectEndY, layerRect.width, layerRect.height);
85 | case HelperAlignment.left:
86 | return Rect.fromLTWH(0, 0, objectX, layerRect.height);
87 | case HelperAlignment.right:
88 | return Rect.fromLTWH(
89 | objectEndX, 0, layerRect.width - objectEndX, layerRect.height);
90 | }
91 | }
92 |
93 | /// This functions search for the maximum rect available space
94 | /// We use it for example to find the most available space to write a text in our anchored helper
95 | Rect getLargestAvailableSpace(ElementModel elementModel) {
96 | var parentObject = context.findRenderObject()!;
97 | var element = elementModel.element!;
98 | var translation =
99 | element.renderObject!.getTransformTo(parentObject).getTranslation();
100 | var objectX = translation.x;
101 | var objectEndX = objectX + element.size!.width;
102 | var objectY = translation.y;
103 | var objectEndY = objectY + element.size!.height;
104 | var layerRect = parentObject.paintBounds;
105 |
106 | Rect availableHSpace;
107 | Rect availableWSpace;
108 | if (objectY > layerRect.height - objectEndY) {
109 | availableHSpace = Rect.fromLTWH(0, 0, layerRect.width, objectY);
110 | } else {
111 | availableHSpace = Rect.fromLTWH(
112 | 0, objectEndY, layerRect.width, layerRect.height - objectEndY);
113 | }
114 | if (objectX > layerRect.width - objectEndX) {
115 | availableWSpace = Rect.fromLTWH(0, 0, objectX, layerRect.height);
116 | } else {
117 | availableWSpace = Rect.fromLTWH(
118 | objectEndX, 0, layerRect.width - objectEndX, layerRect.height);
119 | }
120 | // check area to use the largest
121 | var availableHSpaceArea =
122 | availableHSpace.size.width * availableHSpace.size.height;
123 | var availableWSpaceArea =
124 | availableWSpace.size.width * availableWSpace.size.height;
125 | if (availableWSpaceArea > availableHSpaceArea &&
126 | availableWSpace.width > minWritableSpace) {
127 | return availableWSpace;
128 | }
129 | return availableHSpace;
130 | }
131 |
132 | // -----------------------------------------------------------
133 | // private
134 | // -----------------------------------------------------------
135 |
136 | // ERROR
137 | ElementModel? _searchChildElement(
138 | RenderObject? parentObject, Element element, String? key,
139 | {int n = 0}) {
140 | if (element.widget.key != null &&
141 | element.widget.key.toString().contains(key!)) {
142 | try {
143 | // if render element has bounds lets take it
144 | var res = _createElementModel(parentObject, element, false);
145 | return res;
146 | } catch (_) {}
147 | }
148 | ElementModel? result;
149 | element.visitChildElements((visitor) {
150 | var res = _searchChildElement(parentObject, visitor, key, n: n + 1);
151 | if (res != null) result = res;
152 | });
153 | return result;
154 | }
155 |
156 | ElementModel? _searchChildElementByKey(
157 | RenderObject? parentObject,
158 | Element element,
159 | Key key, {
160 | int n = 0,
161 | required bool isInModal,
162 | }) {
163 | if (element.widget.key != null && element.widget.key == key) {
164 | try {
165 | // if render element has bounds lets take it
166 | return _createElementModel(parentObject, element, isInModal);
167 | } catch (_) {}
168 | }
169 | ElementModel? result;
170 | element.visitChildElements((visitor) {
171 | var res = _searchChildElementByKey(
172 | parentObject,
173 | visitor,
174 | key,
175 | n: n + 1,
176 | isInModal: isInModal,
177 | );
178 | if (res != null) result = res;
179 | });
180 | return result;
181 | }
182 |
183 | // omits elements with key starting with anything other than [<
184 | // flutter makes key with "[<_myKey_>]" for our keys
185 | // scan all elements in the current page tree and add their bounds to the results map
186 | _scanChildElement(RenderObject? parentObject, Element element,
187 | Map results,
188 | {int n = 0, Key? omitChildsOf, bool debugMode = true}) {
189 | if (debugMode) {
190 | var nbChilds = element.debugDescribeChildren().length;
191 | var pre = StringBuffer();
192 | for (int i = 0; i < n; i++) {
193 | pre.write(" ");
194 | }
195 | debugPrint("$pre ${element.widget.runtimeType} $n => $nbChilds ");
196 | }
197 | if (element.widget.key != null &&
198 | omitChildsOf != null &&
199 | element.widget.key.toString() == omitChildsOf.toString()) {
200 | return;
201 | }
202 | if (element.widget.key != null &&
203 | element.widget.key.toString().startsWith("[<") &&
204 | !results.containsKey(element.widget.key.toString())) {
205 | if (debugMode) {
206 | debugPrint(" added ${element.widget.key.toString()} : $n");
207 | }
208 | try {
209 | var model = _createElementModel(parentObject, element, false);
210 | if (results.values.firstWhereOrNull((element) =>
211 | element.bounds == model.bounds &&
212 | element.offset == model.offset) ==
213 | null) {
214 | results.putIfAbsent(element.widget.key.toString(), () => model);
215 | }
216 | } catch (e) {
217 | debugPrint(" error while getting element bounds:");
218 | debugPrint("$e");
219 | }
220 | }
221 | element.visitChildElements((visitor) => _scanChildElement(
222 | parentObject, visitor, results,
223 | n: n + 1, omitChildsOf: omitChildsOf, debugMode: debugMode));
224 | }
225 |
226 | // search first entries that contains our pages
227 | _scanPageChildElement(Element element, List pages) {
228 | if (element.runtimeType.toString() == "_Theatre") {
229 | element.visitChildElements((visitor) => pages.add(PageElement(element)));
230 | } else {
231 | element.visitChildElements(
232 | (visitor) => _scanPageChildElement(element, pages));
233 | }
234 | }
235 |
236 | ElementModel _createElementModel(
237 | RenderObject? parentObject,
238 | Element element,
239 | bool isInModal,
240 | ) {
241 | try {
242 | final mScreenSize = screenSize;
243 | var renderObject = element.findRenderObject()!;
244 |
245 | var bounds = renderObject.paintBounds;
246 | var translation = renderObject //
247 | .getTransformTo(parentObject)
248 | .getTranslation();
249 | // final parentBounds = parentObject!.paintBounds;
250 | if (isInModal) {
251 | final parentRenderObject = renderObject.parent!;
252 | final parentBounds = parentRenderObject.paintBounds;
253 | if (parentBounds.height < mScreenSize.height) {
254 | translation.y +=
255 | mScreenSize.height - parentBounds.height - bounds.height / 2;
256 | }
257 | }
258 | var offset = Offset(translation.x, translation.y);
259 |
260 | return ElementModel(
261 | element.widget.key.toString(),
262 | bounds,
263 | offset,
264 | element.widget.runtimeType,
265 | element: element,
266 | );
267 | } catch (e, s) {
268 | debugPrint("Error while creating element model: $e $s");
269 | rethrow;
270 | }
271 | }
272 | }
273 |
274 | class PageElement {
275 | Element element;
276 |
277 | PageElement(this.element);
278 | }
279 |
280 | class ElementModel {
281 | String? key;
282 |
283 | Rect? bounds;
284 |
285 | Offset? offset;
286 |
287 | Element? element;
288 |
289 | @override
290 | Type runtimeType;
291 |
292 | ElementModel(
293 | this.key,
294 | this.bounds,
295 | this.offset,
296 | this.runtimeType, {
297 | this.element,
298 | });
299 |
300 | factory ElementModel.empty() => ElementModel(null, null, null, ElementModel);
301 | }
302 |
--------------------------------------------------------------------------------
/lib/services/overlay_helper.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class OverlayHelper {
4 | OverlayEntry? overlayEntry;
5 |
6 | OverlayHelper();
7 |
8 | showHelper(BuildContext context, WidgetBuilder widgetBuilder) {
9 | popHelper();
10 | overlayEntry = OverlayEntry(
11 | opaque: false,
12 | builder: widgetBuilder,
13 | );
14 | final overlay = Overlay.maybeOf(context);
15 | if (overlay != null) {
16 | overlay.insert(overlayEntry!);
17 | }
18 | }
19 |
20 | bool popHelper() {
21 | if (overlayEntry != null) {
22 | overlayEntry!.remove();
23 | overlayEntry = null;
24 | return true;
25 | }
26 | return false;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/pal-widgets.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "."
5 | }
6 | ],
7 | "settings": {}
8 | }
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | async:
5 | dependency: transitive
6 | description:
7 | name: async
8 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "2.11.0"
12 | boolean_selector:
13 | dependency: transitive
14 | description:
15 | name: boolean_selector
16 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.1.1"
20 | characters:
21 | dependency: transitive
22 | description:
23 | name: characters
24 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "1.3.0"
28 | clock:
29 | dependency: transitive
30 | description:
31 | name: clock
32 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "1.1.1"
36 | collection:
37 | dependency: "direct main"
38 | description:
39 | name: collection
40 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.18.0"
44 | fake_async:
45 | dependency: transitive
46 | description:
47 | name: fake_async
48 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.3.1"
52 | flutter:
53 | dependency: "direct main"
54 | description: flutter
55 | source: sdk
56 | version: "0.0.0"
57 | flutter_lints:
58 | dependency: "direct dev"
59 | description:
60 | name: flutter_lints
61 | sha256: b543301ad291598523947dc534aaddc5aaad597b709d2426d3a0e0d44c5cb493
62 | url: "https://pub.dev"
63 | source: hosted
64 | version: "1.0.4"
65 | flutter_test:
66 | dependency: "direct dev"
67 | description: flutter
68 | source: sdk
69 | version: "0.0.0"
70 | leak_tracker:
71 | dependency: transitive
72 | description:
73 | name: leak_tracker
74 | sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
75 | url: "https://pub.dev"
76 | source: hosted
77 | version: "10.0.4"
78 | leak_tracker_flutter_testing:
79 | dependency: transitive
80 | description:
81 | name: leak_tracker_flutter_testing
82 | sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
83 | url: "https://pub.dev"
84 | source: hosted
85 | version: "3.0.3"
86 | leak_tracker_testing:
87 | dependency: transitive
88 | description:
89 | name: leak_tracker_testing
90 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
91 | url: "https://pub.dev"
92 | source: hosted
93 | version: "3.0.1"
94 | lints:
95 | dependency: transitive
96 | description:
97 | name: lints
98 | sha256: a2c3d198cb5ea2e179926622d433331d8b58374ab8f29cdda6e863bd62fd369c
99 | url: "https://pub.dev"
100 | source: hosted
101 | version: "1.0.1"
102 | matcher:
103 | dependency: transitive
104 | description:
105 | name: matcher
106 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
107 | url: "https://pub.dev"
108 | source: hosted
109 | version: "0.12.16+1"
110 | material_color_utilities:
111 | dependency: transitive
112 | description:
113 | name: material_color_utilities
114 | sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
115 | url: "https://pub.dev"
116 | source: hosted
117 | version: "0.8.0"
118 | meta:
119 | dependency: transitive
120 | description:
121 | name: meta
122 | sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
123 | url: "https://pub.dev"
124 | source: hosted
125 | version: "1.12.0"
126 | path:
127 | dependency: transitive
128 | description:
129 | name: path
130 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
131 | url: "https://pub.dev"
132 | source: hosted
133 | version: "1.9.0"
134 | sky_engine:
135 | dependency: transitive
136 | description: flutter
137 | source: sdk
138 | version: "0.0.99"
139 | source_span:
140 | dependency: transitive
141 | description:
142 | name: source_span
143 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
144 | url: "https://pub.dev"
145 | source: hosted
146 | version: "1.10.0"
147 | stack_trace:
148 | dependency: transitive
149 | description:
150 | name: stack_trace
151 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
152 | url: "https://pub.dev"
153 | source: hosted
154 | version: "1.11.1"
155 | stream_channel:
156 | dependency: transitive
157 | description:
158 | name: stream_channel
159 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
160 | url: "https://pub.dev"
161 | source: hosted
162 | version: "2.1.2"
163 | string_scanner:
164 | dependency: transitive
165 | description:
166 | name: string_scanner
167 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
168 | url: "https://pub.dev"
169 | source: hosted
170 | version: "1.2.0"
171 | term_glyph:
172 | dependency: transitive
173 | description:
174 | name: term_glyph
175 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
176 | url: "https://pub.dev"
177 | source: hosted
178 | version: "1.2.1"
179 | test_api:
180 | dependency: transitive
181 | description:
182 | name: test_api
183 | sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
184 | url: "https://pub.dev"
185 | source: hosted
186 | version: "0.7.0"
187 | vector_math:
188 | dependency: transitive
189 | description:
190 | name: vector_math
191 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
192 | url: "https://pub.dev"
193 | source: hosted
194 | version: "2.1.4"
195 | vm_service:
196 | dependency: transitive
197 | description:
198 | name: vm_service
199 | sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
200 | url: "https://pub.dev"
201 | source: hosted
202 | version: "14.2.1"
203 | sdks:
204 | dart: ">=3.3.0 <4.0.0"
205 | flutter: ">=3.18.0-18.0.pre.54"
206 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: pal_widgets
2 | description: A set of amazing onboarding widgets for your flutter applications.
3 | version: 0.4.0
4 | repository: https://github.com/Apparence-io/pal-widgets
5 |
6 | environment:
7 | sdk: ">=2.15.0 <3.0.0"
8 | flutter: ">=1.17.0"
9 |
10 | dependencies:
11 | collection: ^1.15.0
12 | flutter:
13 | sdk: flutter
14 |
15 | dev_dependencies:
16 | flutter_lints: ^1.0.0
17 | flutter_test:
18 | sdk: flutter
19 |
20 | # For information on the generic Dart part of this file, see the
21 | # following page: https://dart.dev/tools/pub/pubspec
22 | # The following section is specific to Flutter.
23 | flutter: null
24 |
25 | # To add assets to your package, add an assets section, like this:
26 | # assets:
27 | # - images/a_dot_burr.jpeg
28 | # - images/a_dot_ham.jpeg
29 | #
30 | # For details regarding assets in packages, see
31 | # https://flutter.dev/assets-and-images/#from-packages
32 | #
33 | # An image asset can refer to one or more resolution-specific "variants", see
34 | # https://flutter.dev/assets-and-images/#resolution-aware.
35 | # To add custom fonts to your package, add a fonts section here,
36 | # in this "flutter" section. Each entry in this list should have a
37 | # "family" key with the font family name, and a "fonts" key with a
38 | # list giving the asset and other descriptors for the font. For
39 | # example:
40 | # fonts:
41 | # - family: Schyler
42 | # fonts:
43 | # - asset: fonts/Schyler-Regular.ttf
44 | # - asset: fonts/Schyler-Italic.ttf
45 | # style: italic
46 | # - family: Trajan Pro
47 | # fonts:
48 | # - asset: fonts/TrajanPro.ttf
49 | # - asset: fonts/TrajanPro_Bold.ttf
50 | # weight: 700
51 | #
52 | # For details regarding fonts in packages, see
53 | # https://flutter.dev/custom-fonts/#from-packages
54 |
--------------------------------------------------------------------------------
/test/anchored/anchored_circle_widget_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 | import 'package:pal_widgets/pal_widgets.dart';
4 |
5 | import '../screen_variants.dart';
6 | import 'widgets/anchored_circle_page_.dart';
7 | import 'widgets/anchored_circle_no_buttons.dart';
8 |
9 | void main() {
10 | group('circle anchor widget with buttons', () {
11 | final screenSizeVariants = ValueVariant(basicPhones);
12 |
13 | testWidgets('click on button => shows an anchored widget overlay', (
14 | WidgetTester tester,
15 | ) async {
16 | await tester.setScreenSize(screenSizeVariants.currentValue!);
17 | //---
18 | await tester.pumpWidget(const MyAppWithCircleAnchored());
19 | expect(find.byType(AnchoredHelper), findsNothing);
20 | await tester.tap(find.byType(OutlinedButton).first);
21 | await tester.pump(const Duration(seconds: 2));
22 | expect(find.byType(AnchoredHelper), findsOneWidget);
23 | }, variant: screenSizeVariants);
24 |
25 | testWidgets(
26 | 'shows an anchored widget overlay => positiv button close helper', (
27 | WidgetTester tester,
28 | ) async {
29 | await tester.pumpWidget(const MyAppWithCircleAnchored());
30 | await tester.tap(find.byType(OutlinedButton).first);
31 | await tester.pump(const Duration(seconds: 2));
32 | // tap on positiv button
33 | final btn1 = find.byType(OutlinedButton).at(1).evaluate().first.widget
34 | as OutlinedButton;
35 | btn1.onPressed!();
36 | await tester.pump(const Duration(seconds: 2));
37 | // variants.currentValue;
38 | expect(find.byType(AnchoredHelper), findsNothing);
39 | });
40 |
41 | testWidgets(
42 | 'shows an anchored widget overlay => negative button close helper', (
43 | WidgetTester tester,
44 | ) async {
45 | await tester.pumpWidget(const MyAppWithCircleAnchored());
46 | await tester.tap(find.byType(OutlinedButton).first);
47 | await tester.pump(const Duration(seconds: 2));
48 | // tap on positiv button
49 | final btn2 = find.byType(OutlinedButton).at(2).evaluate().first.widget
50 | as OutlinedButton;
51 | btn2.onPressed!();
52 | await tester.pump(const Duration(seconds: 2));
53 |
54 | expect(find.byType(AnchoredHelper), findsNothing);
55 | });
56 |
57 | testWidgets(
58 | '''shows an anchored widget overlay, anchorAction is null, tap on anchor
59 | => nothing happens''',
60 | (
61 | WidgetTester tester,
62 | ) async {
63 | await tester.pumpWidget(const MyAppWithCircleAnchored());
64 | await tester.tap(find.byType(OutlinedButton).first);
65 | await tester.pump(const Duration(seconds: 2));
66 | // tap on anchor area button
67 | await tester.tap(find.byType(FloatingActionButton));
68 | await tester.pump(const Duration(seconds: 2));
69 |
70 | expect(find.byType(AnchoredHelper), findsOneWidget);
71 | },
72 | );
73 | });
74 |
75 | group('Circle anchor widget - no buttons', () {
76 | testWidgets('click on button => shows an anchored widget overlay', (
77 | WidgetTester tester,
78 | ) async {
79 | await tester.pumpWidget(const MyAppAnchoredNoBtn());
80 | expect(find.byType(AnchoredHelper), findsNothing);
81 | await tester.tap(find.byType(OutlinedButton).first);
82 | await tester.pump(const Duration(seconds: 2));
83 | expect(find.byType(AnchoredHelper), findsOneWidget);
84 | });
85 |
86 | testWidgets(
87 | 'shows an anchored widget overlay => tap on anchor area close helper', (
88 | WidgetTester tester,
89 | ) async {
90 | await tester.pumpWidget(const MyAppAnchoredNoBtn());
91 | await tester.tap(find.byType(OutlinedButton).first);
92 | await tester.pump(const Duration(seconds: 2));
93 | // tap on anchor area button
94 | await tester.tap(find.byType(FloatingActionButton));
95 | await tester.pump(const Duration(seconds: 2));
96 |
97 | expect(find.byType(AnchoredHelper), findsNothing);
98 | });
99 |
100 | testWidgets(
101 | '''tap on anchor area => helper is closed and counter is not incremented
102 | (still 0)''', (
103 | WidgetTester tester,
104 | ) async {
105 | await tester.pumpWidget(const MyAppAnchoredNoBtn());
106 | await tester.tap(find.byType(OutlinedButton).first);
107 | await tester.pump(const Duration(seconds: 2));
108 | // tap on anchor area button
109 | await tester.tap(find.byType(FloatingActionButton));
110 | await tester.pump(const Duration(seconds: 2));
111 |
112 | expect(find.byType(AnchoredHelper), findsNothing);
113 | expect(find.text('0'), findsOneWidget);
114 | });
115 | });
116 | }
117 |
--------------------------------------------------------------------------------
/test/anchored/anchored_rect_widget_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 | import 'package:pal_widgets/pal_widgets.dart';
4 |
5 | import 'widgets/anchored_rect_no_buttons.dart';
6 | import 'widgets/anchored_rect_page.dart';
7 |
8 | void main() {
9 | group('rect anchor widget with buttons', () {
10 | testWidgets('click on button => shows an anchored widget overlay', (
11 | WidgetTester tester,
12 | ) async {
13 | await tester.pumpWidget(const MyAppWithRectAnchored());
14 | expect(find.byType(AnchoredHelper), findsNothing);
15 | await tester.tap(find.byType(OutlinedButton).first);
16 | await tester.pump(const Duration(seconds: 2));
17 | expect(find.byType(AnchoredHelper), findsOneWidget);
18 | });
19 |
20 | testWidgets(
21 | 'shows an anchored widget overlay => positiv button close helper', (
22 | WidgetTester tester,
23 | ) async {
24 | await tester.pumpWidget(const MyAppWithRectAnchored());
25 | await tester.tap(find.byType(OutlinedButton).first);
26 | await tester.pump(const Duration(seconds: 2));
27 | // tap on positiv button
28 | final btn1 = find.byType(OutlinedButton).at(1).evaluate().first.widget
29 | as OutlinedButton;
30 | btn1.onPressed!();
31 | await tester.pump(const Duration(seconds: 2));
32 |
33 | expect(find.byType(AnchoredHelper), findsNothing);
34 | });
35 |
36 | testWidgets(
37 | 'shows an anchored widget overlay => negative button close helper', (
38 | WidgetTester tester,
39 | ) async {
40 | await tester.pumpWidget(const MyAppWithRectAnchored());
41 | await tester.tap(find.byType(OutlinedButton).first);
42 | await tester.pump(const Duration(seconds: 2));
43 | // tap on positiv button
44 | final btn2 = find.byType(OutlinedButton).at(2).evaluate().first.widget
45 | as OutlinedButton;
46 | btn2.onPressed!();
47 | await tester.pump(const Duration(seconds: 2));
48 |
49 | expect(find.byType(AnchoredHelper), findsNothing);
50 | });
51 | });
52 |
53 | group('rect anchor widget - no buttons', () {
54 | testWidgets('click on button => shows an anchored widget overlay', (
55 | WidgetTester tester,
56 | ) async {
57 | await tester.pumpWidget(const MyAppAnchoredNoBtn());
58 | expect(find.byType(AnchoredHelper), findsNothing);
59 | await tester.tap(find.byType(OutlinedButton).first);
60 | await tester.pump(const Duration(seconds: 2));
61 | expect(find.byType(AnchoredHelper), findsOneWidget);
62 | });
63 |
64 | testWidgets(
65 | 'shows an anchored widget overlay => tap on anchor area close helper', (
66 | WidgetTester tester,
67 | ) async {
68 | await tester.pumpWidget(const MyAppAnchoredNoBtn());
69 | await tester.tap(find.byType(OutlinedButton).first);
70 | await tester.pump(const Duration(seconds: 2));
71 | // tap on anchor area button
72 | await tester.tap(find.byType(FloatingActionButton));
73 | await tester.pump(const Duration(seconds: 2));
74 |
75 | expect(find.byType(AnchoredHelper), findsNothing);
76 | });
77 |
78 | testWidgets(
79 | '''tap on anchor area => helper is closed and counter is not incremented
80 | (still 0)''', (
81 | WidgetTester tester,
82 | ) async {
83 | await tester.pumpWidget(const MyAppAnchoredNoBtn());
84 | await tester.tap(find.byType(OutlinedButton).first);
85 | await tester.pump(const Duration(seconds: 2));
86 | // tap on anchor area button
87 | await tester.tap(find.byType(FloatingActionButton));
88 | await tester.pump(const Duration(seconds: 2));
89 |
90 | expect(find.byType(AnchoredHelper), findsNothing);
91 | expect(find.text('0'), findsOneWidget);
92 | });
93 | });
94 | }
95 |
--------------------------------------------------------------------------------
/test/anchored/widgets/anchored_circle_no_buttons.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:pal_widgets/pal_widgets.dart';
3 |
4 | class MyAppAnchoredNoBtn extends StatelessWidget {
5 | const MyAppAnchoredNoBtn({Key? key}) : super(key: key);
6 |
7 | // This widget is the root of your application.
8 | @override
9 | Widget build(BuildContext context) {
10 | return MaterialApp(
11 | title: 'Flutter Demo',
12 | theme: ThemeData(
13 | primarySwatch: Colors.blue,
14 | ),
15 | home: const HelperOrchestrator(
16 | child: MyHomePage2(title: 'Flutter Demo Home Page'),
17 | ),
18 | );
19 | }
20 | }
21 |
22 | class MyHomePage2 extends StatefulWidget {
23 | const MyHomePage2({Key? key, required this.title}) : super(key: key);
24 |
25 | final String title;
26 |
27 | @override
28 | State createState() => _MyHomePage2State();
29 | }
30 |
31 | class _MyHomePage2State extends State {
32 | int _counter = 0;
33 |
34 | void _incrementCounter() {
35 | setState(() {
36 | _counter++;
37 | });
38 | }
39 |
40 | ButtonStyle get helperOutlineBtnStyle => OutlinedButton.styleFrom(
41 | padding: const EdgeInsets.all(12),
42 | textStyle: const TextStyle(
43 | color: Colors.white,
44 | fontSize: 18,
45 | ),
46 | side: const BorderSide(
47 | width: 1.0,
48 | style: BorderStyle.solid,
49 | color: Colors.white,
50 | ),
51 | shape: RoundedRectangleBorder(
52 | borderRadius: BorderRadius.circular(8.0),
53 | ),
54 | );
55 |
56 | AnchoredHelper get helper => AnchoredHelper(
57 | title: const Text(
58 | 'Title lorem pitume',
59 | textAlign: TextAlign.center,
60 | style: TextStyle(
61 | color: Colors.white,
62 | fontSize: 32,
63 | ),
64 | ),
65 | description: const Text(
66 | 'Lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum',
67 | textAlign: TextAlign.center,
68 | style: TextStyle(
69 | color: Colors.white,
70 | fontSize: 21,
71 | ),
72 | ),
73 | bgColor: Colors.blue,
74 | onTapAnchor: () => HelperOrchestrator.of(context).hideHelper(),
75 | rightBtnStyle: helperOutlineBtnStyle,
76 | leftBtnStyle: helperOutlineBtnStyle,
77 | );
78 |
79 | @override
80 | Widget build(BuildContext context) {
81 | return content;
82 | }
83 |
84 | Widget get content => Scaffold(
85 | appBar: AppBar(
86 | title: Text(widget.title),
87 | ),
88 | body: Center(
89 | child: Column(
90 | mainAxisAlignment: MainAxisAlignment.center,
91 | children: [
92 | const Text(
93 | 'You have pushed the button this many times:',
94 | ),
95 | Text(
96 | '$_counter',
97 | style: const TextStyle(color: Colors.blue, fontSize: 32),
98 | ),
99 | const Text('test widget helper'),
100 | const SizedBox(height: 21),
101 | OutlinedButton(
102 | onPressed: () {
103 | HelperOrchestrator.of(context) //
104 | .showAnchoredHelper('text1', helper);
105 | },
106 | child: const Text('push me 2'),
107 | ),
108 | ],
109 | ),
110 | ),
111 | floatingActionButton: FloatingActionButton(
112 | key: HelperOrchestrator.of(context).generateKey('text1'),
113 | onPressed: _incrementCounter,
114 | tooltip: 'Increment',
115 | child: const Icon(Icons.add),
116 | ),
117 | );
118 | }
119 |
--------------------------------------------------------------------------------
/test/anchored/widgets/anchored_circle_page_.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:pal_widgets/pal_widgets.dart';
3 |
4 | class MyAppWithCircleAnchored extends StatelessWidget {
5 | const MyAppWithCircleAnchored({Key? key}) : super(key: key);
6 |
7 | // This widget is the root of your application.
8 | @override
9 | Widget build(BuildContext context) {
10 | return MaterialApp(
11 | title: 'Flutter Demo',
12 | theme: ThemeData(
13 | primarySwatch: Colors.blue,
14 | ),
15 | home: const HelperOrchestrator(
16 | child: MyHomePage(title: 'Flutter Demo Home Page'),
17 | ),
18 | );
19 | }
20 | }
21 |
22 | class MyHomePage extends StatefulWidget {
23 | const MyHomePage({Key? key, required this.title}) : super(key: key);
24 |
25 | final String title;
26 |
27 | @override
28 | State createState() => _MyHomePageState();
29 | }
30 |
31 | class _MyHomePageState extends State {
32 | int _counter = 0;
33 |
34 | void _incrementCounter() {
35 | setState(() {
36 | _counter++;
37 | });
38 | }
39 |
40 | ButtonStyle get helperOutlineBtnStyle => OutlinedButton.styleFrom(
41 | padding: const EdgeInsets.all(12),
42 | textStyle: const TextStyle(
43 | color: Colors.white,
44 | fontSize: 18,
45 | ),
46 | side: const BorderSide(
47 | width: 1.0,
48 | style: BorderStyle.solid,
49 | color: Colors.white,
50 | ),
51 | shape: RoundedRectangleBorder(
52 | borderRadius: BorderRadius.circular(8.0),
53 | ),
54 | );
55 |
56 | AnchoredHelper get helper => AnchoredHelper(
57 | title: const Text(
58 | 'Title lorem pitume',
59 | textAlign: TextAlign.center,
60 | style: TextStyle(
61 | color: Colors.white,
62 | fontSize: 32,
63 | ),
64 | ),
65 | description: const Text(
66 | 'Lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum',
67 | textAlign: TextAlign.center,
68 | style: TextStyle(
69 | color: Colors.white,
70 | fontSize: 21,
71 | ),
72 | ),
73 | bgColor: Colors.blue,
74 | leftBtnText: const Text('cancel'),
75 | rightBtnText: const Text('Ok, understood'),
76 | rightBtnStyle: helperOutlineBtnStyle,
77 | leftBtnStyle: helperOutlineBtnStyle,
78 | onRightTap: () => HelperOrchestrator.of(context).hideHelper(),
79 | onLeftBtnTap: () => HelperOrchestrator.of(context).hideHelper(),
80 | );
81 |
82 | @override
83 | Widget build(BuildContext context) {
84 | return content;
85 | }
86 |
87 | Widget get content => Scaffold(
88 | appBar: AppBar(
89 | title: Text(widget.title),
90 | ),
91 | body: Center(
92 | child: Column(
93 | mainAxisAlignment: MainAxisAlignment.center,
94 | children: [
95 | const Text(
96 | 'You have pushed the button this many times:',
97 | ),
98 | Text(
99 | '$_counter',
100 | key: HelperOrchestrator.of(context).generateKey('text1'),
101 | style: const TextStyle(color: Colors.blue, fontSize: 32),
102 | ),
103 | Text(
104 | 'test widget helper',
105 | key: HelperOrchestrator.of(context).generateKey('text2'),
106 | ),
107 | const SizedBox(height: 21),
108 | OutlinedButton(
109 | onPressed: () {
110 | HelperOrchestrator.of(context)
111 | .showAnchoredHelper('text1', helper);
112 | },
113 | child: const Text('push me 2'),
114 | ),
115 | ],
116 | ),
117 | ),
118 | floatingActionButton: FloatingActionButton(
119 | onPressed: _incrementCounter,
120 | tooltip: 'Increment',
121 | child: const Icon(Icons.add),
122 | ),
123 | );
124 | }
125 |
--------------------------------------------------------------------------------
/test/anchored/widgets/anchored_rect_no_buttons.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:pal_widgets/pal_widgets.dart';
3 |
4 | class MyAppAnchoredNoBtn extends StatelessWidget {
5 | const MyAppAnchoredNoBtn({Key? key}) : super(key: key);
6 |
7 | // This widget is the root of your application.
8 | @override
9 | Widget build(BuildContext context) {
10 | return MaterialApp(
11 | title: 'Flutter Demo',
12 | theme: ThemeData(
13 | primarySwatch: Colors.blue,
14 | ),
15 | home: const HelperOrchestrator(
16 | child: MyHomePage2(title: 'Flutter Demo Home Page'),
17 | ),
18 | );
19 | }
20 | }
21 |
22 | class MyHomePage2 extends StatefulWidget {
23 | const MyHomePage2({Key? key, required this.title}) : super(key: key);
24 |
25 | final String title;
26 |
27 | @override
28 | State createState() => _MyHomePage2State();
29 | }
30 |
31 | class _MyHomePage2State extends State {
32 | int _counter = 0;
33 |
34 | void _incrementCounter() {
35 | setState(() {
36 | _counter++;
37 | });
38 | }
39 |
40 | ButtonStyle get helperOutlineBtnStyle => OutlinedButton.styleFrom(
41 | padding: const EdgeInsets.all(12),
42 | textStyle: const TextStyle(
43 | color: Colors.white,
44 | fontSize: 18,
45 | ),
46 | side: const BorderSide(
47 | width: 1.0,
48 | style: BorderStyle.solid,
49 | color: Colors.white,
50 | ),
51 | shape: RoundedRectangleBorder(
52 | borderRadius: BorderRadius.circular(8.0),
53 | ),
54 | );
55 |
56 | AnchoredHelper get helper => AnchoredHelper(
57 | title: const Text(
58 | 'Title lorem pitume',
59 | textAlign: TextAlign.center,
60 | style: TextStyle(
61 | color: Colors.white,
62 | fontSize: 32,
63 | ),
64 | ),
65 | description: const Text(
66 | 'Lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum',
67 | textAlign: TextAlign.center,
68 | style: TextStyle(
69 | color: Colors.white,
70 | fontSize: 21,
71 | ),
72 | ),
73 | bgColor: Colors.blue,
74 | onTapAnchor: () => HelperOrchestrator.of(context).hideHelper(),
75 | rightBtnStyle: helperOutlineBtnStyle,
76 | leftBtnStyle: helperOutlineBtnStyle,
77 | widgetFactory: AnchoredRectHoleHelper.anchorFactory,
78 | );
79 |
80 | @override
81 | Widget build(BuildContext context) {
82 | return content;
83 | }
84 |
85 | Widget get content => Scaffold(
86 | appBar: AppBar(
87 | title: Text(widget.title),
88 | ),
89 | body: Center(
90 | child: Column(
91 | mainAxisAlignment: MainAxisAlignment.center,
92 | children: [
93 | const Text(
94 | 'You have pushed the button this many times:',
95 | ),
96 | Text(
97 | '$_counter',
98 | style: const TextStyle(color: Colors.blue, fontSize: 32),
99 | ),
100 | const Text('test widget helper'),
101 | const SizedBox(height: 21),
102 | OutlinedButton(
103 | onPressed: () {
104 | HelperOrchestrator.of(context) //
105 | .showAnchoredHelper('text1', helper);
106 | },
107 | child: const Text('push me 2'),
108 | ),
109 | ],
110 | ),
111 | ),
112 | floatingActionButton: FloatingActionButton(
113 | key: HelperOrchestrator.of(context).generateKey('text1'),
114 | onPressed: _incrementCounter,
115 | tooltip: 'Increment',
116 | child: const Icon(Icons.add),
117 | ),
118 | );
119 | }
120 |
--------------------------------------------------------------------------------
/test/anchored/widgets/anchored_rect_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:pal_widgets/pal_widgets.dart';
3 |
4 | class MyAppWithRectAnchored extends StatelessWidget {
5 | const MyAppWithRectAnchored({Key? key}) : super(key: key);
6 |
7 | // This widget is the root of your application.
8 | @override
9 | Widget build(BuildContext context) {
10 | return MaterialApp(
11 | title: 'Flutter Demo',
12 | theme: ThemeData(
13 | primarySwatch: Colors.blue,
14 | ),
15 | home: const HelperOrchestrator(
16 | child: MyHomePage(title: 'Flutter Demo Home Page'),
17 | ),
18 | );
19 | }
20 | }
21 |
22 | class MyHomePage extends StatefulWidget {
23 | const MyHomePage({Key? key, required this.title}) : super(key: key);
24 |
25 | final String title;
26 |
27 | @override
28 | State createState() => _MyHomePageState();
29 | }
30 |
31 | class _MyHomePageState extends State {
32 | int _counter = 0;
33 |
34 | void _incrementCounter() {
35 | setState(() {
36 | _counter++;
37 | });
38 | }
39 |
40 | ButtonStyle get helperOutlineBtnStyle => OutlinedButton.styleFrom(
41 | padding: const EdgeInsets.all(12),
42 | textStyle: const TextStyle(
43 | color: Colors.white,
44 | fontSize: 18,
45 | ),
46 | side: const BorderSide(
47 | width: 1.0,
48 | style: BorderStyle.solid,
49 | color: Colors.white,
50 | ),
51 | shape: RoundedRectangleBorder(
52 | borderRadius: BorderRadius.circular(8.0),
53 | ),
54 | );
55 |
56 | AnchoredHelper get helper => AnchoredHelper(
57 | title: const Text(
58 | 'Title lorem pitume',
59 | textAlign: TextAlign.center,
60 | style: TextStyle(
61 | color: Colors.white,
62 | fontSize: 32,
63 | ),
64 | ),
65 | description: const Text(
66 | 'Lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum',
67 | textAlign: TextAlign.center,
68 | style: TextStyle(
69 | color: Colors.white,
70 | fontSize: 21,
71 | ),
72 | ),
73 | bgColor: Colors.blue,
74 | leftBtnText: const Text('cancel'),
75 | rightBtnText: const Text('Ok, understood'),
76 | rightBtnStyle: helperOutlineBtnStyle,
77 | leftBtnStyle: helperOutlineBtnStyle,
78 | onRightTap: () => HelperOrchestrator.of(context).hideHelper(),
79 | onLeftBtnTap: () => HelperOrchestrator.of(context).hideHelper(),
80 | widgetFactory: AnchoredRectHoleHelper.anchorFactory,
81 | );
82 |
83 | @override
84 | Widget build(BuildContext context) {
85 | return content;
86 | }
87 |
88 | Widget get content => Scaffold(
89 | appBar: AppBar(
90 | title: Text(widget.title),
91 | ),
92 | body: Center(
93 | child: Column(
94 | mainAxisAlignment: MainAxisAlignment.center,
95 | children: [
96 | const Text(
97 | 'You have pushed the button this many times:',
98 | ),
99 | Text(
100 | '$_counter',
101 | key: HelperOrchestrator.of(context).generateKey('text1'),
102 | style: const TextStyle(color: Colors.blue, fontSize: 32),
103 | ),
104 | Text(
105 | 'test widget helper',
106 | key: HelperOrchestrator.of(context).generateKey('text2'),
107 | ),
108 | const SizedBox(height: 21),
109 | OutlinedButton(
110 | onPressed: () {
111 | HelperOrchestrator.of(context)
112 | .showAnchoredHelper('text1', helper);
113 | },
114 | child: const Text('push me 2'),
115 | ),
116 | ],
117 | ),
118 | ),
119 | floatingActionButton: FloatingActionButton(
120 | onPressed: _incrementCounter,
121 | tooltip: 'Increment',
122 | child: const Icon(Icons.add),
123 | ),
124 | );
125 | }
126 |
--------------------------------------------------------------------------------
/test/screen_variants.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | import 'package:flutter_test/flutter_test.dart';
4 |
5 | const iphone8Plus = ScreenSize('Iphone 8 Plus', 414, 736, 3);
6 | const iphone11Max = ScreenSize('Iphone 11 Max', 414, 896, 3);
7 | const samsungGalaxyS = ScreenSize('Android Samsung Galaxy S', 480, 800, 1);
8 |
9 | final basicPhones = {iphone8Plus, iphone11Max, samsungGalaxyS};
10 |
11 | /// this class defines a size configuration of a specific device
12 | class ScreenSize {
13 | /// name this configuration
14 | final String name;
15 |
16 | /// size configuration and pixel density
17 | final double width, height, pixelDensity;
18 |
19 | const ScreenSize(this.name, this.width, this.height, this.pixelDensity);
20 |
21 | @override
22 | String toString() => name;
23 | }
24 |
25 | extension ScreenSizeManager on WidgetTester {
26 | Future setScreenSize(ScreenSize screenSize) async {
27 | return _setScreenSize(
28 | width: screenSize.width,
29 | height: screenSize.height,
30 | pixelDensity: screenSize.pixelDensity);
31 | }
32 |
33 | Future _setScreenSize(
34 | {double width = 540,
35 | double height = 960,
36 | double pixelDensity = 1}) async {
37 | final size = Size(width, height);
38 | await binding.setSurfaceSize(size);
39 | view.physicalSize = size;
40 | view.devicePixelRatio = pixelDensity;
41 | // Old code
42 | // binding.window.physicalSizeTestValue = size;
43 | // binding.window.devicePixelRatioTestValue = pixelDensity;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/test/services/element_finder_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 | import 'package:pal_widgets/services/element_finder.dart';
4 |
5 | void main() {
6 | group('Element finder with String key', () {
7 | Widget page = Scaffold(
8 | body: Column(
9 | children: [
10 | const Text("Test Text", key: ValueKey("text1")),
11 | const Text("Test Text 2", key: ValueKey("text2")),
12 | const Text("Test Text 3", key: ValueKey("text3")),
13 | // ignore: sized_box_for_whitespace
14 | Container(key: const ValueKey("container"), height: 50, width: 150)
15 | ],
16 | ),
17 | );
18 |
19 | testWidgets('''
20 | search a widget by key as String and find location + size.
21 | this only work if this is a valueKey
22 | ''', (
23 | WidgetTester tester,
24 | ) async {
25 | BuildContext? _context;
26 | var app = MediaQuery(
27 | data: const MediaQueryData(),
28 | child: MaterialApp(home: Builder(
29 | builder: (context) {
30 | _context = context;
31 | return page;
32 | },
33 | )));
34 | await tester.pumpWidget(app);
35 | ElementFinder finder = ElementFinder(buildContext: _context);
36 | var result = finder.searchChildElement("container")!;
37 | expect(result, isNotNull);
38 | expect(result.bounds!.size, equals(const Size(150, 50)));
39 | });
40 |
41 | testWidgets('scan widgets finds all with their rect',
42 | (WidgetTester tester) async {
43 | BuildContext? _context;
44 | var app = MediaQuery(
45 | data: const MediaQueryData(),
46 | child: MaterialApp(home: Builder(
47 | builder: (context) {
48 | _context = context;
49 | return page;
50 | },
51 | )));
52 | await tester.pumpWidget(app);
53 | ElementFinder finder = ElementFinder(buildContext: _context);
54 | var elements = finder.scan();
55 | expect(elements, isNotNull);
56 | expect(elements.length, equals(6));
57 | var keys = elements.keys.toList();
58 | expect(keys[1], contains("text1"));
59 | expect(keys[2], contains("text2"));
60 | // expect(elements[keys.last].bounds.size, equals(Size(150, 50)));
61 | });
62 | });
63 |
64 | group('Element finder with key', () {
65 | final containerKey = UniqueKey();
66 |
67 | Widget page = Scaffold(
68 | body: Column(
69 | children: [
70 | const Text("Test Text", key: ValueKey("text1")),
71 | const Text("Test Text 2", key: ValueKey("text2")),
72 | const Text("Test Text 3", key: ValueKey("text3")),
73 | // ignore: sized_box_for_whitespace
74 | Container(key: containerKey, height: 50, width: 150)
75 | ],
76 | ),
77 | );
78 |
79 | testWidgets('search a widget by key and find location + size', (
80 | WidgetTester tester,
81 | ) async {
82 | BuildContext? _context;
83 | var app = MediaQuery(
84 | data: const MediaQueryData(),
85 | child: MaterialApp(home: Builder(
86 | builder: (context) {
87 | _context = context;
88 | return page;
89 | },
90 | )));
91 | await tester.pumpWidget(app);
92 | ElementFinder finder = ElementFinder(buildContext: _context);
93 | var result =
94 | finder.searchChildElementByKey(containerKey, isInModal: false)!;
95 | expect(result, isNotNull);
96 | expect(result.bounds!.size, equals(const Size(150, 50)));
97 | });
98 | });
99 | }
100 |
--------------------------------------------------------------------------------