├── example
├── ios
│ ├── Flutter
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── AppFrameworkInfo.plist
│ ├── Runner
│ │ ├── Runner-Bridging-Header.h
│ │ ├── Assets.xcassets
│ │ │ ├── LaunchImage.imageset
│ │ │ │ ├── LaunchImage.png
│ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ ├── README.md
│ │ │ │ └── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ ├── 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-1024x1024@1x.png
│ │ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ │ └── Contents.json
│ │ ├── AppDelegate.swift
│ │ ├── Base.lproj
│ │ │ ├── Main.storyboard
│ │ │ └── LaunchScreen.storyboard
│ │ └── Info.plist
│ ├── Runner.xcodeproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ ├── xcshareddata
│ │ │ └── xcschemes
│ │ │ │ └── Runner.xcscheme
│ │ └── project.pbxproj
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ └── .gitignore
├── .firebaserc
├── web
│ ├── favicon.png
│ ├── icons
│ │ ├── Icon-192.png
│ │ ├── Icon-512.png
│ │ ├── Icon-maskable-192.png
│ │ └── Icon-maskable-512.png
│ ├── manifest.json
│ └── index.html
├── android
│ ├── gradle.properties
│ ├── app
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ ├── 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
│ │ │ │ │ ├── drawable
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── drawable-v21
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── values
│ │ │ │ │ │ └── styles.xml
│ │ │ │ │ └── values-night
│ │ │ │ │ │ └── styles.xml
│ │ │ │ ├── kotlin
│ │ │ │ │ └── com
│ │ │ │ │ │ └── example
│ │ │ │ │ │ └── example
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── debug
│ │ │ │ └── AndroidManifest.xml
│ │ │ └── profile
│ │ │ │ └── AndroidManifest.xml
│ │ └── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ ├── .gitignore
│ ├── settings.gradle
│ └── build.gradle
├── macos
│ └── Flutter
│ │ ├── GeneratedPluginRegistrant.swift
│ │ └── ephemeral
│ │ ├── Flutter-Generated.xcconfig
│ │ └── flutter_export_environment.sh
├── linux
│ └── flutter
│ │ ├── generated_plugin_registrant.cc
│ │ ├── generated_plugin_registrant.h
│ │ └── generated_plugins.cmake
├── firebase.json
├── pubspec.yaml
├── README.md
├── .gitignore
├── lib
│ ├── list_example.dart
│ └── main.dart
├── .metadata
├── pubspec.lock
└── analysis_options.yaml
├── assets
└── readme
│ ├── highlight_example.png
│ ├── package_profile.webp
│ └── example_presentation.gif
├── lib
├── src
│ ├── enums
│ │ ├── arrow_direction.dart
│ │ ├── popup_click_trigger_behavior.dart
│ │ └── popup_dismiss_trigger_behavior.dart
│ ├── typedefs
│ │ ├── on_area_pressed.dart
│ │ └── on_controller_created.dart
│ ├── extensions
│ │ └── context_extensions.dart
│ ├── constants
│ │ └── popup_constants.dart
│ ├── themes
│ │ ├── high_light_theme.dart
│ │ ├── info_popup_arrow_theme.dart
│ │ └── info_popup_content_theme.dart
│ ├── painters
│ │ ├── high_lighter.dart
│ │ └── arrow_indicator_painter.dart
│ ├── controllers
│ │ └── info_popup_controller.dart
│ ├── info_popup_widget.dart
│ └── overlays
│ │ └── overlay_entry_layout.dart
└── info_popup.dart
├── .metadata
├── .github
└── workflows
│ └── stale.yml
├── .gitignore
├── pubspec.yaml
├── LICENSE
├── test
└── info_popup_test.dart
├── CHANGELOG.md
├── analysis_options.yaml
└── README.md
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/example/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "info-popup"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/example/web/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/example/web/favicon.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/example/web/icons/Icon-192.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/example/web/icons/Icon-512.png
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/assets/readme/highlight_example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/assets/readme/highlight_example.png
--------------------------------------------------------------------------------
/assets/readme/package_profile.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/assets/readme/package_profile.webp
--------------------------------------------------------------------------------
/assets/readme/example_presentation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/assets/readme/example_presentation.gif
--------------------------------------------------------------------------------
/example/web/icons/Icon-maskable-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/example/web/icons/Icon-maskable-192.png
--------------------------------------------------------------------------------
/example/web/icons/Icon-maskable-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/example/web/icons/Icon-maskable-512.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/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/SalihCanBinboga/info_popup/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/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 | }
7 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SalihCanBinboga/info_popup/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/src/enums/arrow_direction.dart:
--------------------------------------------------------------------------------
1 | /// [ArrowDirection] is used to specify the direction of the arrow.
2 | enum ArrowDirection {
3 | /// The arrow is pointing up.
4 | up,
5 |
6 | /// The arrow is pointing down.
7 | down,
8 | }
9 |
--------------------------------------------------------------------------------
/example/macos/Flutter/GeneratedPluginRegistrant.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | import FlutterMacOS
6 | import Foundation
7 |
8 |
9 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
10 | }
11 |
--------------------------------------------------------------------------------
/lib/src/typedefs/on_area_pressed.dart:
--------------------------------------------------------------------------------
1 | import 'package:info_popup/src/controllers/info_popup_controller.dart';
2 |
3 | /// [OnAreaPressed] is called when the tap area is pressed.
4 | typedef OnAreaPressed = void Function(InfoPopupController controller);
5 |
--------------------------------------------------------------------------------
/lib/src/typedefs/on_controller_created.dart:
--------------------------------------------------------------------------------
1 | import 'package:info_popup/info_popup.dart';
2 |
3 | /// [OnControllerCreated] is called when the [InfoPopupController] is created.
4 | typedef OnControllerCreated = void Function(InfoPopupController controller);
5 |
--------------------------------------------------------------------------------
/example/linux/flutter/generated_plugin_registrant.cc:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #include "generated_plugin_registrant.h"
8 |
9 |
10 | void fl_register_plugins(FlPluginRegistry* registry) {
11 | }
12 |
--------------------------------------------------------------------------------
/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.4-all.zip
6 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "hosting": {
3 | "public": "build/web",
4 | "ignore": [
5 | "firebase.json",
6 | "**/.*",
7 | "**/node_modules/**"
8 | ],
9 | "rewrites": [
10 | {
11 | "source": "**",
12 | "destination": "/index.html"
13 | }
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.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: 5464c5bac742001448fe4fc0597be939379f88ea
8 | channel: stable
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/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/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: example
2 | description: Info Popup Example
3 | publish_to: 'none'
4 | version: 1.0.0+2
5 |
6 | environment:
7 | sdk: '>=2.17.3 <3.0.0'
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 | info_popup:
13 | path: ../
14 |
15 | dev_dependencies:
16 | flutter_test:
17 | sdk: flutter
18 |
19 | flutter:
20 | uses-material-design: true
21 |
--------------------------------------------------------------------------------
/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/linux/flutter/generated_plugin_registrant.h:
--------------------------------------------------------------------------------
1 | //
2 | // Generated file. Do not edit.
3 | //
4 |
5 | // clang-format off
6 |
7 | #ifndef GENERATED_PLUGIN_REGISTRANT_
8 | #define GENERATED_PLUGIN_REGISTRANT_
9 |
10 | #include
11 |
12 | // Registers Flutter plugins.
13 | void fl_register_plugins(FlPluginRegistry* registry);
14 |
15 | #endif // GENERATED_PLUGIN_REGISTRANT_
16 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
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 |
--------------------------------------------------------------------------------
/lib/src/enums/popup_click_trigger_behavior.dart:
--------------------------------------------------------------------------------
1 | /// [PopupClickTriggerBehavior] is used to specify the trigger behavior of the info popup.
2 | enum PopupClickTriggerBehavior {
3 | /// [onTap] is used to trigger the popup when the user taps on the child widget.
4 | onTap,
5 |
6 | /// [onLongPress] is used to trigger the popup when the user long presses on the child widget.
7 | onLongPress,
8 |
9 | /// [none] is used to disable the trigger behavior of the popup.
10 | none,
11 | }
12 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/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/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 | name: Close Stale Issues
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * *'
6 |
7 | jobs:
8 | close_stale_issues:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Close Stale Issues
12 | uses: actions/stale@v9.0.0
13 | with:
14 | stale-issue-message: 'This issue has been automatically closed due to inactivity. Please feel free to reopen it if you are still experiencing this problem.'
15 | days-before-stale: 14
16 | days-before-close: 7
17 | stale-issue-label: 'stale'
--------------------------------------------------------------------------------
/example/macos/Flutter/ephemeral/Flutter-Generated.xcconfig:
--------------------------------------------------------------------------------
1 | // This is a generated file; do not edit or check into version control.
2 | FLUTTER_ROOT=/Users/salihcanbinboga/development/sdk/fvm/versions/3.13.6
3 | FLUTTER_APPLICATION_PATH=/Users/salihcanbinboga/development/flutter_projects/personal/case/info_popup/example
4 | COCOAPODS_PARALLEL_CODE_SIGN=true
5 | FLUTTER_BUILD_DIR=build
6 | FLUTTER_BUILD_NAME=1.0.0
7 | FLUTTER_BUILD_NUMBER=2
8 | DART_OBFUSCATION=false
9 | TRACK_WIDGET_CREATION=true
10 | TREE_SHAKE_ICONS=false
11 | PACKAGE_CONFIG=.dart_tool/package_config.json
12 |
--------------------------------------------------------------------------------
/lib/src/enums/popup_dismiss_trigger_behavior.dart:
--------------------------------------------------------------------------------
1 | /// [PopupDismissTriggerBehavior] is used to specify the trigger behavior of the info popup.
2 | enum PopupDismissTriggerBehavior {
3 | /// [onTapContent] is used to dismiss the popup when the content is tapped.
4 | onTapContent,
5 |
6 | /// [onTapArea] is used to dismiss the popup when the area outside the popup is tapped.
7 | onTapArea,
8 |
9 | /// [anyWhere] is used to dismiss the popup when anywhere is tapped.
10 | anyWhere,
11 |
12 | /// [manuel] is used to dismiss the popup manually.
13 | manuel,
14 | }
15 |
--------------------------------------------------------------------------------
/lib/info_popup.dart:
--------------------------------------------------------------------------------
1 | library info_popup;
2 |
3 | export 'src/constants/popup_constants.dart';
4 | export 'src/controllers/info_popup_controller.dart';
5 | export 'src/enums/arrow_direction.dart';
6 | export 'src/enums/popup_click_trigger_behavior.dart';
7 | export 'src/enums/popup_dismiss_trigger_behavior.dart';
8 | export 'src/info_popup_widget.dart';
9 | export 'src/themes/high_light_theme.dart';
10 | export 'src/themes/info_popup_arrow_theme.dart';
11 | export 'src/themes/info_popup_content_theme.dart';
12 | export 'src/typedefs/on_area_pressed.dart';
13 | export 'src/typedefs/on_controller_created.dart';
14 |
--------------------------------------------------------------------------------
/lib/src/extensions/context_extensions.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// [BuildContextExtensions] is a class that contains extensions for [BuildContext]
4 | extension BuildContextExtensions on BuildContext {
5 | /// Returns the [MediaQueryData] from the closest instance that encloses the given context.
6 | MediaQueryData get mediaQuery => MediaQuery.of(this);
7 |
8 | /// Return the [screenWidth] of the screen
9 | double get screenWidth => mediaQuery.size.width;
10 |
11 | /// Return the [screenHeight] of the screen
12 | double get screenHeight => mediaQuery.size.height;
13 | }
14 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # example
2 |
3 | Info Popup Example
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://docs.flutter.dev/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
13 |
14 | For help getting started with Flutter development, view the
15 | [online documentation](https://docs.flutter.dev/), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/.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 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
25 | /pubspec.lock
26 | **/doc/api/
27 | .dart_tool/
28 | .packages
29 | build/
30 |
--------------------------------------------------------------------------------
/example/macos/Flutter/ephemeral/flutter_export_environment.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # This is a generated file; do not edit or check into version control.
3 | export "FLUTTER_ROOT=/Users/salihcanbinboga/development/sdk/fvm/versions/3.13.6"
4 | export "FLUTTER_APPLICATION_PATH=/Users/salihcanbinboga/development/flutter_projects/personal/case/info_popup/example"
5 | export "COCOAPODS_PARALLEL_CODE_SIGN=true"
6 | export "FLUTTER_BUILD_DIR=build"
7 | export "FLUTTER_BUILD_NAME=1.0.0"
8 | export "FLUTTER_BUILD_NUMBER=2"
9 | export "DART_OBFUSCATION=false"
10 | export "TRACK_WIDGET_CREATION=true"
11 | export "TREE_SHAKE_ICONS=false"
12 | export "PACKAGE_CONFIG=.dart_tool/package_config.json"
13 |
--------------------------------------------------------------------------------
/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/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.6.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.1.2'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | mavenCentral()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: info_popup
2 | description: The simple way to show the user some information on your selected widget.
3 | version: 4.3.2
4 | maintainer: Salih Can Binboga
5 | homepage: https://github.com/salihcanbinboga/info_popup
6 | repository: https://github.com/salihcanbinboga/info_popup
7 | issue_tracker: https://github.com/salihcanbinboga/info_popup/issues
8 | screenshots:
9 | - description: "Info Popup Logo"
10 | path: assets/readme/package_profile.webp
11 | topics:
12 | - popup
13 | - modal
14 | - dialog
15 | - alert
16 | - tooltip
17 |
18 | environment:
19 | sdk: '>=2.18.4 <4.0.0'
20 | flutter: '>=2.0.0'
21 |
22 | dependencies:
23 | flutter:
24 | sdk: flutter
25 |
26 | dev_dependencies:
27 | example:
28 | path: example
29 | flutter_lints: ^1.0.0
30 | flutter_test:
31 | sdk: flutter
32 |
--------------------------------------------------------------------------------
/example/linux/flutter/generated_plugins.cmake:
--------------------------------------------------------------------------------
1 | #
2 | # Generated file, do not edit.
3 | #
4 |
5 | list(APPEND FLUTTER_PLUGIN_LIST
6 | )
7 |
8 | list(APPEND FLUTTER_FFI_PLUGIN_LIST
9 | )
10 |
11 | set(PLUGIN_BUNDLED_LIBRARIES)
12 |
13 | foreach(plugin ${FLUTTER_PLUGIN_LIST})
14 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
15 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
16 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
17 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
18 | endforeach(plugin)
19 |
20 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
21 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
22 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
23 | endforeach(ffi_plugin)
24 |
--------------------------------------------------------------------------------
/lib/src/constants/popup_constants.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | /// [PopupConstants] is some constants used in the popup.
4 | mixin PopupConstants {
5 | /// [defaultArrowSize] is used to create a default arrow size.
6 | static const Size defaultArrowSize = Size(15, 10);
7 |
8 | /// [defaultInfoTextStyle] is used to create a default info text style.
9 | static const TextStyle defaultInfoTextStyle = TextStyle(
10 | color: Colors.black,
11 | fontSize: 14.0,
12 | );
13 |
14 | /// [defaultContentBorderRadius] is used to create a default content border radius.
15 | static const BorderRadius defaultContentBorderRadius =
16 | BorderRadius.all(Radius.circular(8));
17 |
18 | /// [defaultAreaBackgroundColor] is used to create a default area background color.
19 | static const Color defaultAreaBackgroundColor = Colors.transparent;
20 | }
21 |
--------------------------------------------------------------------------------
/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 | 11.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Symbolication related
36 | app.*.symbols
37 |
38 | # Obfuscation related
39 | app.*.map.json
40 |
41 | # Android Studio will place build artifacts here
42 | /android/app/debug
43 | /android/app/profile
44 | /android/app/release
45 | /.firebase/
46 |
--------------------------------------------------------------------------------
/example/web/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example",
3 | "short_name": "example",
4 | "start_url": ".",
5 | "display": "standalone",
6 | "background_color": "#0175C2",
7 | "theme_color": "#0175C2",
8 | "description": "Info Popup Example",
9 | "orientation": "portrait-primary",
10 | "prefer_related_applications": false,
11 | "icons": [
12 | {
13 | "src": "icons/Icon-192.png",
14 | "sizes": "192x192",
15 | "type": "image/png"
16 | },
17 | {
18 | "src": "icons/Icon-512.png",
19 | "sizes": "512x512",
20 | "type": "image/png"
21 | },
22 | {
23 | "src": "icons/Icon-maskable-192.png",
24 | "sizes": "192x192",
25 | "type": "image/png",
26 | "purpose": "maskable"
27 | },
28 | {
29 | "src": "icons/Icon-maskable-512.png",
30 | "sizes": "512x512",
31 | "type": "image/png",
32 | "purpose": "maskable"
33 | }
34 | ]
35 | }
36 |
--------------------------------------------------------------------------------
/example/lib/list_example.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:info_popup/info_popup.dart';
3 |
4 | class ListExample extends StatelessWidget {
5 | const ListExample({Key? key}) : super(key: key);
6 |
7 | @override
8 | Widget build(BuildContext context) {
9 | return Scaffold(
10 | appBar: AppBar(
11 | title: const Text('List Example'),
12 | ),
13 | body: SizedBox(
14 | width: MediaQuery.of(context).size.width,
15 | child: ListView.builder(
16 | itemCount: 120,
17 | cacheExtent: 10000,
18 | itemBuilder: (_, int index) {
19 | return ListTile(
20 | title: Text('Item $index'),
21 | leading: const InfoPopupWidget(
22 | contentTitle: 'Lorem ipsum dolor sit amet',
23 | dismissTriggerBehavior:
24 | PopupDismissTriggerBehavior.onTapContent,
25 | child: Icon(Icons.info),
26 | ),
27 | );
28 | },
29 | ),
30 | ),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Salih Can Binboga
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/lib/src/themes/high_light_theme.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart'
2 | show BorderRadius, Color, Colors, EdgeInsets;
3 |
4 | /// The [HighLightTheme] class is used to customize the highlighter.
5 | class HighLightTheme {
6 | /// Creates a [HighLightTheme].
7 | HighLightTheme({
8 | required this.backgroundColor,
9 | this.padding = EdgeInsets.zero,
10 | this.radius = BorderRadius.zero,
11 | }) : assert(
12 | backgroundColor != Colors.transparent,
13 | '\n\nThe backgroundColor can not be transparent. \n'
14 | 'Description: The backgroundColor can not be transparent. '
15 | 'Please use another color. \n \n'
16 | 'For example: Colors.black.withOpacity(.5) \n',
17 | );
18 |
19 | /// Default [HighLightTheme].
20 | factory HighLightTheme.defaultTheme() => HighLightTheme(
21 | backgroundColor: Colors.black.withOpacity(.5),
22 | );
23 |
24 | /// The [radius] of the highlighter.
25 | final BorderRadius radius;
26 |
27 | /// The [padding] of the highlighter.
28 | final EdgeInsets padding;
29 |
30 | /// The [backgroundColor] of background area color.
31 | final Color backgroundColor;
32 | }
33 |
--------------------------------------------------------------------------------
/lib/src/painters/high_lighter.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: use_late_for_private_fields_and_variables
2 |
3 | part of '../controllers/info_popup_controller.dart';
4 |
5 | class _HighLighter extends CustomClipper {
6 | _HighLighter({
7 | this.area = Rect.zero,
8 | this.radius = BorderRadius.zero,
9 | this.padding = EdgeInsets.zero,
10 | });
11 |
12 | final Rect area;
13 | final BorderRadius radius;
14 | final EdgeInsets padding;
15 |
16 | @override
17 | Path getClip(Size size) {
18 | final Rect rect = Rect.fromLTRB(
19 | area.left - padding.left,
20 | area.top - padding.top,
21 | area.right + padding.right,
22 | area.bottom + padding.bottom,
23 | );
24 |
25 | return Path()
26 | ..fillType = PathFillType.evenOdd
27 | ..addRect(Offset.zero & size)
28 | ..addRRect(
29 | RRect.fromRectAndCorners(
30 | rect,
31 | topLeft: radius.topLeft,
32 | topRight: radius.topRight,
33 | bottomLeft: radius.bottomLeft,
34 | bottomRight: radius.bottomRight,
35 | ),
36 | );
37 | }
38 |
39 | @override
40 | bool shouldReclip(covariant CustomClipper oldClipper) =>
41 | oldClipper is _HighLighter &&
42 | (radius != oldClipper.radius || area != oldClipper.area);
43 | }
44 |
--------------------------------------------------------------------------------
/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.
5 |
6 | version:
7 | revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
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: ffccd96b62ee8cec7740dab303538c5fc26ac543
17 | base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
18 | - platform: android
19 | create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
20 | base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
21 | - platform: ios
22 | create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
23 | base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
24 | - platform: web
25 | create_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
26 | base_revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
27 |
28 | # User provided section
29 |
30 | # List of Local paths (relative to this file) that should be
31 | # ignored by the migrate tool.
32 | #
33 | # Files that are not part of the templates will be ignored by default.
34 | unmanaged_files:
35 | - 'lib/main.dart'
36 | - 'ios/Runner.xcodeproj/project.pbxproj'
37 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/lib/src/painters/arrow_indicator_painter.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:info_popup/src/enums/arrow_direction.dart';
3 |
4 | /// [ArrowIndicatorPainter] is custom painter for drawing a triangle indicator for popup
5 | /// to point specific widget
6 | class ArrowIndicatorPainter extends CustomPainter {
7 | /// Creates a [ArrowIndicatorPainter]
8 | const ArrowIndicatorPainter({
9 | required this.arrowDirection,
10 | this.arrowColor = Colors.black,
11 | });
12 |
13 | /// [arrowDirection] is the direction of the arrow
14 | final ArrowDirection arrowDirection;
15 |
16 | /// [arrowColor] is the color of the arrow
17 | final Color arrowColor;
18 |
19 | /// Draws the triangle of specific [size] on [canvas]
20 | @override
21 | void paint(Canvas canvas, Size size) {
22 | final Path arrowPath = Path();
23 | final Paint arrowPaint = Paint();
24 |
25 | arrowPaint.strokeWidth = 2.0;
26 | arrowPaint.color = arrowColor;
27 | arrowPaint.style = PaintingStyle.fill;
28 |
29 | switch (arrowDirection) {
30 | case ArrowDirection.up:
31 | arrowPath.moveTo(size.width / 2.0, 0.0);
32 | arrowPath.lineTo(0.0, size.height + 1);
33 | arrowPath.lineTo(size.width, size.height + 1);
34 | break;
35 | case ArrowDirection.down:
36 | arrowPath.moveTo(0.0, -1.0);
37 | arrowPath.lineTo(size.width, -1.0);
38 | arrowPath.lineTo(size.width / 2.0, size.height);
39 | break;
40 | }
41 |
42 | canvas.drawPath(arrowPath, arrowPaint);
43 | }
44 |
45 | /// [shouldRepaint] is called when the [CustomPainter] is asked to paint again.
46 | @override
47 | bool shouldRepaint(CustomPainter customPainter) => true;
48 | }
49 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
15 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
30 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/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 | UIViewControllerBasedStatusBarAppearance
45 |
46 | CADisableMinimumFrameDurationOnPhone
47 |
48 | UIApplicationSupportsIndirectInputEvents
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/lib/src/themes/info_popup_arrow_theme.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart' show Color, Colors, CustomPainter, Size;
2 | import 'package:info_popup/src/constants/popup_constants.dart';
3 | import 'package:info_popup/src/enums/arrow_direction.dart';
4 |
5 | /// [InfoPopupArrowTheme] is used to customize the arrow of the popup.
6 | class InfoPopupArrowTheme {
7 | /// [InfoPopupArrowTheme] is creates a [InfoPopupArrowTheme] constructor.
8 | /// [arrowDirection] is used to customize the direction of the arrow.
9 | /// [arrowSize] is used to customize the size of the arrow.
10 | /// [arrowGap] is used to customize the gap between the arrow and the content.
11 | /// [arrowPainter] is used to customize the painter of the arrow.
12 | const InfoPopupArrowTheme({
13 | this.arrowSize = PopupConstants.defaultArrowSize,
14 | this.arrowDirection = ArrowDirection.up,
15 | this.color = Colors.black,
16 | this.arrowPainter,
17 | this.enabledAutoArrowDirection = true,
18 | });
19 |
20 | /// The size [arrowSize] of the arrow indicator.
21 | final Size arrowSize;
22 |
23 | /// The [arrowDirection] of the arrow indicator.
24 | final ArrowDirection arrowDirection;
25 |
26 | /// The [color] of the arrow indicator.
27 | final Color color;
28 |
29 | /// The [arrowPainter] is used to draw the arrow indicator.
30 | final CustomPainter? arrowPainter;
31 |
32 | /// The [enabledAutoArrowDirection] is used to enable the auto arrow direction.
33 | final bool enabledAutoArrowDirection;
34 |
35 | /// [copyWith] is used to copy the [InfoPopupArrowTheme] with new values.
36 | InfoPopupArrowTheme copyWith({
37 | Size? arrowSize,
38 | ArrowDirection? arrowDirection,
39 | Color? color,
40 | CustomPainter? arrowPainter,
41 | }) {
42 | return InfoPopupArrowTheme(
43 | arrowSize: arrowSize ?? this.arrowSize,
44 | arrowDirection: arrowDirection ?? this.arrowDirection,
45 | color: color ?? this.color,
46 | arrowPainter: arrowPainter ?? this.arrowPainter,
47 | );
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/example/web/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | example
33 |
34 |
35 |
39 |
40 |
41 |
42 |
43 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion flutter.compileSdkVersion
30 | ndkVersion flutter.ndkVersion
31 |
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_1_8
34 | targetCompatibility JavaVersion.VERSION_1_8
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = '1.8'
39 | }
40 |
41 | sourceSets {
42 | main.java.srcDirs += 'src/main/kotlin'
43 | }
44 |
45 | defaultConfig {
46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
47 | applicationId "com.example.example"
48 | // You can update the following values to match your application needs.
49 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
50 | minSdkVersion flutter.minSdkVersion
51 | targetSdkVersion flutter.targetSdkVersion
52 | versionCode flutterVersionCode.toInteger()
53 | versionName flutterVersionName
54 | }
55 |
56 | buildTypes {
57 | release {
58 | // TODO: Add your own signing config for the release build.
59 | // Signing with the debug keys for now, so `flutter run --release` works.
60 | signingConfig signingConfigs.debug
61 | }
62 | }
63 | }
64 |
65 | flutter {
66 | source '../..'
67 | }
68 |
69 | dependencies {
70 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
71 | }
72 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/lib/src/themes/info_popup_content_theme.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart'
2 | show
3 | BorderRadius,
4 | Color,
5 | Colors,
6 | EdgeInsets,
7 | EdgeInsetsGeometry,
8 | TextAlign,
9 | TextStyle;
10 | import 'package:info_popup/src/constants/popup_constants.dart';
11 |
12 | /// [InfoPopupContentTheme] is used to customize the content of the popup.
13 | class InfoPopupContentTheme {
14 | /// [InfoPopupContentTheme] creates a theme for the content of the popup.
15 | /// [infoTextStyle] is used to customize the text style of the content.
16 | /// [infoTextAlign] is used to customize the text align of the content.
17 | /// [infoContainerBackgroundColor] is used to customize the background color of the content.
18 | /// [contentPadding] is used to customize the padding of the content.
19 | /// [contentBorderRadius] is used to customize the border radius of the content.
20 | const InfoPopupContentTheme({
21 | this.infoTextStyle = PopupConstants.defaultInfoTextStyle,
22 | this.infoTextAlign = TextAlign.center,
23 | this.infoContainerBackgroundColor = Colors.white,
24 | this.contentPadding = const EdgeInsets.all(8.0),
25 | this.contentBorderRadius = PopupConstants.defaultContentBorderRadius,
26 | });
27 |
28 | /// The [infoTextStyle] of the info text.
29 | final TextStyle infoTextStyle;
30 |
31 | /// The [infoTextAlign] of the info text.
32 | final TextAlign infoTextAlign;
33 |
34 | /// The [infoContainerBackgroundColor] of the info container color.
35 | final Color infoContainerBackgroundColor;
36 |
37 | /// The [padding] of the info container.
38 | final EdgeInsetsGeometry contentPadding;
39 |
40 | /// The [borderRadius] of the info container.
41 | final BorderRadius contentBorderRadius;
42 |
43 | /// [copyWith] is used to copy the [InfoPopupContentTheme] with new values.
44 | InfoPopupContentTheme copyWith({
45 | TextStyle? infoTextStyle,
46 | TextAlign? infoTextAlign,
47 | Color? infoContainerBackgroundColor,
48 | EdgeInsetsGeometry? contentPadding,
49 | BorderRadius? contentBorderRadius,
50 | }) {
51 | return InfoPopupContentTheme(
52 | infoTextStyle: infoTextStyle ?? this.infoTextStyle,
53 | infoTextAlign: infoTextAlign ?? this.infoTextAlign,
54 | infoContainerBackgroundColor:
55 | infoContainerBackgroundColor ?? this.infoContainerBackgroundColor,
56 | contentPadding: contentPadding ?? this.contentPadding,
57 | contentBorderRadius: contentBorderRadius ?? this.contentBorderRadius,
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/test/info_popup_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:example/main.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_test/flutter_test.dart';
4 |
5 | void main() {
6 | testWidgets(
7 | 'MyApp has 4 text widgets',
8 | (WidgetTester tester) async {
9 | await tester.pumpWidget(const MyApp());
10 | await tester.pumpAndSettle(const Duration(seconds: 2));
11 |
12 | final Finder widgets = find.byType(Text);
13 |
14 | expect(widgets, findsNWidgets(4));
15 | },
16 | );
17 |
18 | testWidgets(
19 | 'MyApp has normal info popup widget',
20 | (WidgetTester tester) async {
21 | await tester.pumpWidget(const MyApp());
22 | await tester.pumpAndSettle(const Duration(seconds: 2));
23 |
24 | await safeTapByKey(tester, infoPopupTextExampleKey);
25 |
26 | await tester.pumpAndSettle(const Duration(seconds: 2));
27 |
28 | expect(find.text(infoPopupTextExampleText), findsOneWidget);
29 | },
30 | );
31 |
32 | testWidgets(
33 | 'MyApp has info popup widget with custom widget',
34 | (WidgetTester tester) async {
35 | await tester.pumpWidget(const MyApp());
36 | await tester.pumpAndSettle(const Duration(seconds: 2));
37 |
38 | await safeTapByKey(tester, infoPopupCustomExampleKey);
39 | await tester.pumpAndSettle(const Duration(seconds: 2));
40 |
41 | final Finder widgets = find.text(infoPopupCustomExampleText);
42 |
43 | expect(widgets, findsOneWidget);
44 | },
45 | );
46 |
47 | testWidgets(
48 | 'MyApp has info popup widget with long text',
49 | (WidgetTester tester) async {
50 | await tester.pumpWidget(const MyApp());
51 | await tester.pumpAndSettle(const Duration(seconds: 2));
52 |
53 | await safeTapByKey(tester, infoPopupLongTextExampleKey);
54 | await tester.pumpAndSettle(const Duration(seconds: 2));
55 |
56 | final Finder widgets = find.text(infoPopupLongTextExampleText);
57 |
58 | expect(widgets, findsOneWidget);
59 | },
60 | );
61 |
62 | testWidgets(
63 | 'MyApp has info popup widget that has gap',
64 | (WidgetTester tester) async {
65 | await tester.pumpWidget(const MyApp());
66 | await tester.pumpAndSettle(const Duration(seconds: 2));
67 |
68 | await safeTapByKey(tester, infoPopupArrowGapExampleKey);
69 | await tester.pumpAndSettle(const Duration(seconds: 2));
70 |
71 | final Finder widgets = find.text(infoPopupArrowGapExampleText);
72 |
73 | expect(widgets, findsOneWidget);
74 | },
75 | );
76 |
77 | // TODO(salihcanbinboga): 27.09.2022 18:48 - Arrow Example Info Widget Test
78 | // TODO(salihcanbinboga): 27.09.2022 18:48 - Arrow Directional Gap Widget Test
79 | // TODO(salihcanbinboga): 27.09.2022 18:48 - Info Widget Screen Overflow Test
80 | }
81 |
82 | Future safeTapByKey(WidgetTester tester, Key key) async {
83 | await tester.ensureVisible(find.byKey(key));
84 | await tester.pumpAndSettle(const Duration(seconds: 2));
85 | await tester.tap(find.byKey(key));
86 | }
87 |
--------------------------------------------------------------------------------
/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.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 4.3.2
2 |
3 | - chore: Update package icon on pub.dev
4 |
5 | ## 4.3.1
6 |
7 | - fix: The BoxConstraints error was addressed by adding extra validation.
8 |
9 | ## 4.3.0
10 |
11 | - feat: Added a structure that prevents other gestures when the showroom is active.
12 | - feat: You can now disable the content constraints using the [enabledAutomaticConstraint] parameter. (default: true)
13 | - feat: Arrow direction now adjusts automatically. You can manage it using the [enabledAutoArrowDirection] parameter. (default: true)
14 |
15 | ## 4.1.0
16 |
17 | - feat: Added click behaviour to `InfoPopupWidget` (default: `PopupClickTriggerBehavior.onTap`)
18 |
19 | ## 4.0.0
20 |
21 | - fix: Issue where InfoPopupWidget was not updating when a state inside it changed, whether it was active or closed, has been resolved.
22 | - fix: Problem of closing when clicked while active on top of InfoPopupWidget and automatically reopening has been fixed.
23 |
24 | #### Breaking Change:
25 | The customContent prop should now be a function that returns a Widget instead of directly accepting a Widget.
26 |
27 | Previous usage: `customContent: Widget`
28 |
29 | New usage: `customContent: () => Widget`
30 |
31 | This change means that the `customContent` prop must now be a function, requiring adjustments in the existing codebase.
32 |
33 | ## 3.0.6
34 |
35 | - refactor: Reformat code
36 |
37 | ## 3.0.5
38 |
39 | - chore: Update package topics.
40 |
41 | ## 3.0.4
42 |
43 | - fix: Highlight theme usable in `InfoPopupWidget` now
44 |
45 | ## 3.0.3
46 |
47 | - feat: Version upgrade
48 |
49 | ## 3.0.1
50 |
51 | - feat: Added `enableLog` prop to `InfoPopupWidget`
52 | - doc: Added package profile logo.
53 |
54 | ## 3.0.0
55 |
56 | - fix: This fixes the issue of the InfoPopupWidget not updating when the popup content or a property of the InfoPopupWidget changes.
57 | - feat: Upgrade Flutter SDK to 3.7.1
58 | - doc: The readme.md file has been updated.
59 |
60 | ## 2.4.0
61 |
62 | - feature: Added automatic popup dismiss
63 | - feature: Added new dismiss trigger values (`anyWhere`, `manuel`)
64 |
65 | ## 2.3.0
66 |
67 | - feature: **Highlight** feature is now available!
68 | - feature: Improve for popup horizontal alignment calculation
69 | - refactor: Refactored the whole OverlayEntryLayout (Major Update Reason)
70 | - refactor: Improve popup opening animation performance
71 |
72 | ## 2.2.0
73 |
74 | - feature: Added `contentMaxWidth` prop to `InfoPopupWidget`
75 |
76 | ## 2.1.3
77 |
78 | - doc: Update README.md
79 |
80 | ## 2.1.2
81 |
82 | - doc: Deployed to hosting to test an example project
83 | - You can test now! [Info Popup](https://info-popup.web.app/#/ "Info Popup")
84 |
85 | ## 2.1.0
86 |
87 | - feature: Added `indicatorOffset` prop to `InfoPopupWidget`
88 |
89 | ## 2.0.0
90 |
91 | - feature: Added dismiss behavior to info popup
92 | - feature: Support for inside list items!
93 | - feature: Added `contentOffset` prop
94 | - refactor: Some argument names have been changed (major version bump)
95 | - fix: Fixed a bug where the popup would not be dismissed when the user tapped outside of it
96 | - fix: Bottom sheet offset is now calculated correctly in web
97 | - deprecated: Removed `infoText` & `infoWidget` props
98 | - deprecated: Removed `arrowGap` & `arrowAlignment` props
99 |
100 | ## 1.4.0
101 |
102 | - refactor: Improved calculating above and below the target widget remaining area
103 | - refactor: `onControllerCreated` now optional
104 | - refactor: `infoText` & `infoWidget` now deprecated, use `customTitle` & `customContent` instead
105 | - feature: Add popup trigger mode `PopupTriggerBehavior` (default: `PopupTriggerBehavior.onTap`)
106 | - refactor: Some code refactoring and name changes
107 | - feature: Calculate dynamic popup position based on the target widget position
108 | - feature: Add content SafeArea to indicator
109 |
110 | ## 1.3.0
111 |
112 | - fix: Content overflow on device with small screen
113 | - feature: Added arrow alignment option by target
114 |
115 | ## 1.2.0
116 |
117 | - feature: Added custom indicator painter support
118 |
119 | ## 1.1.0
120 |
121 | - feature: Added arrow gap support according to the arrow direction
122 | - doc: Updated example project
123 |
124 | ## 1.0.10
125 |
126 | - doc: update planned task to README.md
127 |
128 | ## 1.0.9
129 |
130 | - doc: add planned task to README.md
131 |
132 | ## 1.0.8
133 |
134 | - doc: GIF size reduced
135 | - doc: README.md updated
136 |
137 | ## 1.0.7
138 |
139 | - doc: GIF speed updated
140 |
141 | ## 1.0.6
142 |
143 | - test: Added some simple widget tests
144 | - refactor: Disabled automatic showing info popup structure in example
145 |
146 | ## 1.0.5
147 |
148 | - Update README.md
149 | - fix: Info overlay layout done catching
150 |
151 | ## 1.0.4
152 |
153 | - Update README.md
154 |
155 | ## 1.0.3
156 |
157 | - Update README.md
158 |
159 | ## 1.0.2
160 |
161 | - Update README.md
162 |
163 | ## 1.0.1
164 |
165 | - Update README.md
166 | - Resize presentation GIF
167 |
168 | ## 1.0.0
169 |
170 | - Initial release
--------------------------------------------------------------------------------
/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: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.17.2"
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_test:
58 | dependency: "direct dev"
59 | description: flutter
60 | source: sdk
61 | version: "0.0.0"
62 | info_popup:
63 | dependency: "direct main"
64 | description:
65 | path: ".."
66 | relative: true
67 | source: path
68 | version: "4.0.0"
69 | matcher:
70 | dependency: transitive
71 | description:
72 | name: matcher
73 | sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
74 | url: "https://pub.dev"
75 | source: hosted
76 | version: "0.12.16"
77 | material_color_utilities:
78 | dependency: transitive
79 | description:
80 | name: material_color_utilities
81 | sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
82 | url: "https://pub.dev"
83 | source: hosted
84 | version: "0.5.0"
85 | meta:
86 | dependency: transitive
87 | description:
88 | name: meta
89 | sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
90 | url: "https://pub.dev"
91 | source: hosted
92 | version: "1.9.1"
93 | path:
94 | dependency: transitive
95 | description:
96 | name: path
97 | sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917"
98 | url: "https://pub.dev"
99 | source: hosted
100 | version: "1.8.3"
101 | sky_engine:
102 | dependency: transitive
103 | description: flutter
104 | source: sdk
105 | version: "0.0.99"
106 | source_span:
107 | dependency: transitive
108 | description:
109 | name: source_span
110 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
111 | url: "https://pub.dev"
112 | source: hosted
113 | version: "1.10.0"
114 | stack_trace:
115 | dependency: transitive
116 | description:
117 | name: stack_trace
118 | sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5
119 | url: "https://pub.dev"
120 | source: hosted
121 | version: "1.11.0"
122 | stream_channel:
123 | dependency: transitive
124 | description:
125 | name: stream_channel
126 | sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8"
127 | url: "https://pub.dev"
128 | source: hosted
129 | version: "2.1.1"
130 | string_scanner:
131 | dependency: transitive
132 | description:
133 | name: string_scanner
134 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
135 | url: "https://pub.dev"
136 | source: hosted
137 | version: "1.2.0"
138 | term_glyph:
139 | dependency: transitive
140 | description:
141 | name: term_glyph
142 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
143 | url: "https://pub.dev"
144 | source: hosted
145 | version: "1.2.1"
146 | test_api:
147 | dependency: transitive
148 | description:
149 | name: test_api
150 | sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
151 | url: "https://pub.dev"
152 | source: hosted
153 | version: "0.6.0"
154 | vector_math:
155 | dependency: transitive
156 | description:
157 | name: vector_math
158 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
159 | url: "https://pub.dev"
160 | source: hosted
161 | version: "2.1.4"
162 | web:
163 | dependency: transitive
164 | description:
165 | name: web
166 | sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
167 | url: "https://pub.dev"
168 | source: hosted
169 | version: "0.1.4-beta"
170 | sdks:
171 | dart: ">=3.1.0-185.0.dev <4.0.0"
172 | flutter: ">=2.0.0"
173 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | analyzer:
2 | errors:
3 | missing_required_param: warning
4 | missing_return: warning
5 | todo: ignore
6 | deprecated_member_use_from_same_package: ignore
7 | sdk_version_async_exported_from_core: ignore
8 | unnecessary_null_comparison: ignore
9 | always_require_non_null_named_parameters: false
10 | exclude:
11 | - '**/*.g.dart'
12 | - 'lib/src/generated/*.dart'
13 | - '**/*.mocks.dart'
14 | language:
15 | strict-casts: true
16 | strict-raw-types: true
17 |
18 | linter:
19 | rules:
20 | - always_declare_return_types
21 | - always_put_control_body_on_new_line
22 | - always_require_non_null_named_parameters
23 | - always_specify_types
24 | - annotate_overrides
25 | - avoid_bool_literals_in_conditional_expressions
26 | - avoid_classes_with_only_static_members
27 | - avoid_double_and_int_checks
28 | - avoid_empty_else
29 | - avoid_equals_and_hash_code_on_mutable_classes
30 | - avoid_escaping_inner_quotes
31 | - avoid_field_initializers_in_const_classes
32 | - avoid_function_literals_in_foreach_calls
33 | - avoid_init_to_null
34 | - avoid_js_rounded_ints
35 | - avoid_null_checks_in_equality_operators
36 | - avoid_redundant_argument_values
37 | - avoid_relative_lib_imports
38 | - avoid_renaming_method_parameters
39 | - avoid_return_types_on_setters
40 | - avoid_returning_null_for_future
41 | - avoid_returning_null_for_void
42 | - avoid_setters_without_getters
43 | - avoid_shadowing_type_parameters
44 | - avoid_single_cascade_in_expression_statements
45 | - avoid_slow_async_io
46 | - avoid_type_to_string
47 | - avoid_types_as_parameter_names
48 | - avoid_unnecessary_containers
49 | - avoid_unused_constructor_parameters
50 | - avoid_void_async
51 | - await_only_futures
52 | - camel_case_extensions
53 | - camel_case_types
54 | - cancel_subscriptions
55 | - cast_nullable_to_non_nullable
56 | - control_flow_in_finally
57 | - deprecated_consistency
58 | - directives_ordering
59 | - empty_catches
60 | - empty_constructor_bodies
61 | - empty_statements
62 | - eol_at_end_of_file
63 | - exhaustive_cases
64 | - file_names
65 | - flutter_style_todos
66 | - hash_and_equals
67 | - implementation_imports
68 |
69 | - leading_newlines_in_multiline_strings
70 | - library_names
71 | - library_prefixes
72 | - library_private_types_in_public_api
73 | - missing_whitespace_between_adjacent_strings
74 | - no_adjacent_strings_in_list
75 | - no_duplicate_case_values
76 | - no_leading_underscores_for_library_prefixes
77 | - no_logic_in_create_state
78 | - non_constant_identifier_names
79 | - noop_primitive_operations
80 | - null_check_on_nullable_type_parameter
81 | - null_closures
82 | - overridden_fields
83 | - package_api_docs
84 | - package_names
85 | - package_prefixed_library_names
86 | - prefer_adjacent_string_concatenation
87 | - prefer_asserts_in_initializer_lists
88 | - prefer_collection_literals
89 | - prefer_conditional_assignment
90 | - prefer_const_constructors
91 | - prefer_const_constructors_in_immutables
92 | - prefer_const_declarations
93 | - prefer_const_literals_to_create_immutables
94 | - prefer_contains
95 | - prefer_final_fields
96 | - prefer_final_in_for_each
97 | - prefer_final_locals
98 | - prefer_for_elements_to_map_fromIterable
99 | - prefer_foreach
100 | - prefer_function_declarations_over_variables
101 | - prefer_generic_function_type_aliases
102 | - prefer_if_elements_to_conditional_expressions
103 | - prefer_if_null_operators
104 | - prefer_initializing_formals
105 | - prefer_inlined_adds
106 | - prefer_interpolation_to_compose_strings
107 | - prefer_is_empty
108 | - prefer_is_not_empty
109 | - prefer_is_not_operator
110 | - prefer_iterable_whereType
111 | - prefer_null_aware_operators
112 | - prefer_single_quotes
113 | - prefer_spread_collections
114 | - prefer_typing_uninitialized_variables
115 | - prefer_void_to_null
116 | - provide_deprecation_message
117 | - recursive_getters
118 | - secure_pubspec_urls
119 | - slash_for_doc_comments
120 | - sort_child_properties_last
121 | - sort_constructors_first
122 | - sort_unnamed_constructors_first
123 | - test_types_in_equals
124 | - throw_in_finally
125 | - tighten_type_of_initializing_formals
126 | - type_init_formals
127 | - unnecessary_brace_in_string_interps
128 | - unnecessary_const
129 | - unnecessary_constructor_name
130 | - unnecessary_getters_setters
131 | - unnecessary_late
132 | - unnecessary_new
133 | - unnecessary_null_aware_assignments
134 | - unnecessary_null_checks
135 | - unnecessary_null_in_if_null_operators
136 | - unnecessary_nullable_for_final_variable_declarations
137 | - unnecessary_overrides
138 | - unnecessary_parenthesis
139 | - unnecessary_statements
140 | - unnecessary_string_escapes
141 | - unnecessary_string_interpolations
142 | - unnecessary_this
143 | - unrelated_type_equality_checks
144 | - unsafe_html
145 | - use_full_hex_values_for_flutter_colors
146 | - use_function_type_syntax_for_parameters
147 | - use_if_null_to_convert_nulls_to_bools
148 | - use_is_even_rather_than_modulo
149 | - use_key_in_widget_constructors
150 | - use_late_for_private_fields_and_variables
151 | - use_raw_strings
152 | - use_rethrow_when_possible
153 | - use_setters_to_change_properties
154 | - valid_regexps
155 | - void_checks
156 | - no_runtimeType_toString
157 | - public_member_api_docs
158 | - sort_pub_dependencies
--------------------------------------------------------------------------------
/example/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | analyzer:
2 | language:
3 | strict-casts: true
4 | strict-raw-types: true
5 | errors:
6 | deprecated_member_use_from_same_package: ignore
7 | unnecessary_null_comparison: ignore
8 | exclude:
9 | - "build/**"
10 |
11 | linter:
12 | rules:
13 | # This list is derived from the list of all available lints located at
14 | # https://github.com/dart-lang/linter/blob/master/example/all.yaml
15 | - always_declare_return_types
16 | - always_specify_types
17 | - always_put_control_body_on_new_line
18 | - always_require_non_null_named_parameters
19 | - annotate_overrides
20 | - avoid_bool_literals_in_conditional_expressions
21 | - avoid_classes_with_only_static_members
22 | - avoid_double_and_int_checks
23 | - avoid_dynamic_calls
24 | - avoid_empty_else
25 | - avoid_equals_and_hash_code_on_mutable_classes
26 | - avoid_escaping_inner_quotes
27 | - avoid_field_initializers_in_const_classes
28 | - avoid_function_literals_in_foreach_calls
29 | - avoid_implementing_value_types
30 | - avoid_init_to_null
31 | - avoid_js_rounded_ints
32 | - avoid_null_checks_in_equality_operators
33 | - avoid_print
34 | - avoid_redundant_argument_values
35 | - avoid_relative_lib_imports
36 | - avoid_renaming_method_parameters
37 | - avoid_return_types_on_setters
38 | - avoid_returning_null
39 | - avoid_returning_null_for_future
40 | - avoid_returning_null_for_void
41 | - avoid_setters_without_getters
42 | - avoid_shadowing_type_parameters
43 | - avoid_single_cascade_in_expression_statements
44 | - avoid_slow_async_io
45 | - avoid_type_to_string
46 | - avoid_types_as_parameter_names
47 | - avoid_unnecessary_containers
48 | - avoid_unused_constructor_parameters
49 | - avoid_void_async
50 | - await_only_futures
51 | - camel_case_extensions
52 | - camel_case_types
53 | - cancel_subscriptions
54 | - cast_nullable_to_non_nullable
55 | - conditional_uri_does_not_exist
56 | - control_flow_in_finally
57 | - curly_braces_in_flow_control_structures
58 | - depend_on_referenced_packages
59 | - deprecated_consistency
60 | - directives_ordering
61 | - empty_catches
62 | - empty_constructor_bodies
63 | - empty_statements
64 | - eol_at_end_of_file
65 | - exhaustive_cases
66 | - file_names
67 | - hash_and_equals
68 | - implementation_imports
69 | - library_names
70 | - library_prefixes
71 | - library_private_types_in_public_api
72 | - missing_whitespace_between_adjacent_strings
73 | - no_default_cases
74 | - no_duplicate_case_values
75 | - no_leading_underscores_for_library_prefixes
76 | - no_leading_underscores_for_local_identifiers
77 | - no_logic_in_create_state
78 | - non_constant_identifier_names
79 | - noop_primitive_operations
80 | - null_check_on_nullable_type_parameter
81 | - null_closures
82 | - only_throw_errors
83 | - overridden_fields
84 | - package_api_docs
85 | - package_names
86 | - package_prefixed_library_names
87 | - prefer_adjacent_string_concatenation
88 | - prefer_asserts_in_initializer_lists
89 | - prefer_collection_literals
90 | - prefer_conditional_assignment
91 | - prefer_const_constructors
92 | - prefer_const_constructors_in_immutables
93 | - prefer_const_declarations
94 | - prefer_const_literals_to_create_immutables
95 | - prefer_contains
96 | - prefer_equal_for_default_values
97 | - prefer_final_fields
98 | - prefer_final_in_for_each
99 | - prefer_final_locals
100 | - prefer_for_elements_to_map_fromIterable
101 | - prefer_foreach
102 | - prefer_function_declarations_over_variables
103 | - prefer_generic_function_type_aliases
104 | - prefer_if_elements_to_conditional_expressions
105 | - prefer_if_null_operators
106 | - prefer_initializing_formals
107 | - prefer_inlined_adds
108 | - prefer_interpolation_to_compose_strings
109 | - prefer_is_empty
110 | - prefer_is_not_empty
111 | - prefer_is_not_operator
112 | - prefer_iterable_whereType
113 | - prefer_null_aware_operators
114 | - prefer_relative_imports
115 | - prefer_single_quotes
116 | - prefer_spread_collections
117 | - prefer_typing_uninitialized_variables
118 | - prefer_void_to_null
119 | - provide_deprecation_message
120 | - recursive_getters
121 | - secure_pubspec_urls
122 | - sized_box_for_whitespace
123 | - slash_for_doc_comments
124 | - sort_child_properties_last
125 | - sort_constructors_first
126 | - sort_unnamed_constructors_first
127 | - test_types_in_equals
128 | - throw_in_finally
129 | - tighten_type_of_initializing_formals
130 | - type_init_formals
131 | - unnecessary_await_in_return
132 | - unnecessary_brace_in_string_interps
133 | - unnecessary_const
134 | - unnecessary_constructor_name
135 | - unnecessary_getters_setters
136 | - unnecessary_late
137 | - unnecessary_new
138 | - unnecessary_null_aware_assignments
139 | - unnecessary_null_checks
140 | - unnecessary_null_in_if_null_operators
141 | - unnecessary_nullable_for_final_variable_declarations
142 | - unnecessary_overrides
143 | - unnecessary_parenthesis
144 | - unnecessary_statements
145 | - unnecessary_string_escapes
146 | - unnecessary_string_interpolations
147 | - unnecessary_this
148 | - unrelated_type_equality_checks
149 | - unsafe_html
150 | - use_build_context_synchronously
151 | - use_full_hex_values_for_flutter_colors
152 | - use_function_type_syntax_for_parameters
153 | - use_if_null_to_convert_nulls_to_bools
154 | - use_is_even_rather_than_modulo
155 | - use_key_in_widget_constructors
156 | - use_late_for_private_fields_and_variables
157 | - use_named_constants
158 | - use_raw_strings
159 | - use_rethrow_when_possible
160 | - use_setters_to_change_properties
161 | - use_test_throws_matchers
162 | - valid_regexps
163 | - void_checks
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Introduction
2 |
3 | The `info_popup` package allows you to easily show a **simple**, **customizable** popup on your wrapped widget. The **highlight feature**, which can be activated at will, helps draw the user's attention to the desired location.
4 |
5 | You can test it now on the [Info Popup preview page](https://info-popup.web.app/#/ "Info Popup").
6 | Note that the website experience may be different.
7 |
8 | ## Features
9 |
10 | - Display a ***customizable*** popup on your wrapped widget
11 | - Activate the ***highlight*** feature to draw the user's attention to the desired location
12 | - Fully customize the content of the popup
13 | - Add margins to the popup from ***any side***
14 |
15 | 
16 | 
17 |
18 | ## Getting Started
19 |
20 | To use this package, add `info_popup` as a dependency in your `pubspec.yaml` file.
21 |
22 | ```yaml
23 | dependencies:
24 | info_popup: ^3.0.0
25 | ```
26 |
27 | Alternatively, you can add it to your project by running the following commands in your terminal:
28 |
29 | with Dart:
30 |
31 | ```shell
32 | $ dart pub add info_popup
33 | ```
34 |
35 | with Flutter:
36 |
37 | ```shell
38 | $ flutter pub add info_popup
39 | ```
40 |
41 | ## Usage
42 |
43 | To show a popup, wrap the widget that you want to display the popup on with the `InfoPopupWidget` widget. All you have to do is wrap it in the widget you want to show information with the `InfoPopupWidget` widget. With the `InfoPopupController`, you can customize it as you wish, and turn it on and off.
44 |
45 | ```dart
46 | import 'package:info_popup/info_popup.dart';
47 | ```
48 |
49 | ```dart
50 | InfoPopupWidget(
51 | contentTitle: 'Info Popup Details',
52 | child: Icon(
53 | Icons.info,
54 | color: Colors.pink,
55 | ),
56 | ),
57 | ```
58 |
59 | ## Example
60 |
61 | This is a normal info text displayed using the `InfoPopupWidget` widget with several optional parameters.
62 |
63 | ```dart
64 | InfoPopupWidget(
65 | contentTitle: 'Info Popup Details',
66 | arrowTheme: InfoPopupArrowTheme(
67 | color: Colors.pink,
68 | arrowDirection: ArrowDirection.up,
69 | ),
70 | contentTheme: InfoPopupContentTheme(
71 | infoContainerBackgroundColor: Colors.black,
72 | infoTextStyle: TextStyle(color: Colors.white),
73 | contentPadding: const EdgeInsets.all(8),
74 | contentBorderRadius: BorderRadius.all(Radius.circular(10)),
75 | infoTextAlign: TextAlign.center,
76 | ),
77 | dismissTriggerBehavior: PopupDismissTriggerBehavior.onTapArea,
78 | areaBackgroundColor: Colors.transparent,
79 | indicatorOffset: Offset.zero,
80 | contentOffset: Offset.zero,
81 | onControllerCreated: (controller) {
82 | print('Info Popup Controller Created');
83 | },
84 | onAreaPressed: (InfoPopupController controller) {
85 | print('Area Pressed');
86 | },
87 | infoPopupDismissed: () {
88 | print('Info Popup Dismissed');
89 | },
90 | onLayoutMounted: (Size size) {
91 | print('Info Popup Layout Mounted');
92 | },
93 | child: Icon(
94 | Icons.info,
95 | color: Colors.pink,
96 | ),
97 | ),
98 | ```
99 | "This is a ***custom popup*** example created using the `InfoPopupWidget` widget.
100 |
101 | ##### Custom Popup Widget
102 | ```dart
103 | InfoPopupWidget(
104 | customContent: Container(
105 | decoration: BoxDecoration(
106 | color: Colors.blueGrey,
107 | borderRadius: BorderRadius.circular(10),
108 | ),
109 | padding: const EdgeInsets.all(10),
110 | child: Column(
111 | children: const [
112 | TextField(
113 | decoration: InputDecoration(
114 | hintText: 'Enter your name',
115 | hintStyle: TextStyle(color: Colors.white),
116 | enabledBorder: OutlineInputBorder(
117 | borderSide: BorderSide(color: Colors.white),
118 | ),
119 | ),
120 | ),
121 | SizedBox(height: 10),
122 | Center(
123 | child: Text(
124 | 'Example of Info Popup inside a Bottom Sheet',
125 | style: TextStyle(
126 | color: Colors.white,
127 | ),
128 | ),
129 | ),
130 | ],
131 | ),
132 | ),
133 | arrowTheme: const InfoPopupArrowTheme(
134 | color: Colors.pink,
135 | arrowDirection: ArrowDirection.up,
136 | ),
137 | dismissTriggerBehavior: PopupDismissTriggerBehavior.onTapArea,
138 | areaBackgroundColor: Colors.transparent,
139 | indicatorOffset: Offset.zero,
140 | contentOffset: Offset.zero,
141 | onControllerCreated: (controller) {
142 | print('Info Popup Controller Created');
143 | },
144 | onAreaPressed: (InfoPopupController controller) {
145 | print('Area Pressed');
146 | },
147 | infoPopupDismissed: () {
148 | print('Info Popup Dismissed');
149 | },
150 | onLayoutMounted: (Size size) {
151 | print('Info Popup Layout Mounted');
152 | },
153 | child: Icon(
154 | Icons.info,
155 | color: Colors.pink,
156 | ),
157 | ),
158 | ```
159 |
160 |
161 | ## Conclusion
162 |
163 | The info_popup package provides a simple and effective way to show a customizable popup on your wrapped widget. With the highlight feature, you can draw the user's attention to the desired location.
164 |
165 | ## License
166 |
167 | This project is licensed under the MIT License - see the [LICENSE](https://github.com/SalihCanBinboga/info_popup/blob/master/LICENSE "LICENSE") file for details.
168 |
--------------------------------------------------------------------------------
/lib/src/controllers/info_popup_controller.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/gestures.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter/rendering.dart';
4 | import 'package:info_popup/info_popup.dart';
5 | import 'package:info_popup/src/extensions/context_extensions.dart';
6 |
7 | import '../painters/arrow_indicator_painter.dart';
8 |
9 | part '../overlays/overlay_entry_layout.dart';
10 |
11 | part '../painters/high_lighter.dart';
12 |
13 | /// Popup manager for the InfoPopup widget.
14 | /// [InfoPopupController] is used to show and dismiss the popup.
15 | class InfoPopupController {
16 | /// Creates a [InfoPopupController] widget.
17 | InfoPopupController({
18 | required this.context,
19 | required RenderBox targetRenderBox,
20 | required this.areaBackgroundColor,
21 | required this.arrowTheme,
22 | required this.contentTheme,
23 | required this.layerLink,
24 | required this.dismissTriggerBehavior,
25 | required this.enableHighlight,
26 | required this.highLightTheme,
27 | required this.enabledAutomaticConstraint,
28 | this.infoPopupDismissed,
29 | this.contentTitle,
30 | this.customContent,
31 | this.onAreaPressed,
32 | this.onLayoutMounted,
33 | this.contentOffset = Offset.zero,
34 | this.indicatorOffset = Offset.zero,
35 | this.contentMaxWidth,
36 | }) : _targetRenderBox = targetRenderBox;
37 |
38 | /// The [layerLink] is the layer link of the popup.
39 | final LayerLink layerLink;
40 |
41 | /// The context of the widget.
42 | final BuildContext context;
43 |
44 | /// The [_targetRenderBox] is the render box of the info text.
45 | RenderBox _targetRenderBox;
46 |
47 | /// The [infoPopupDismissed] is the callback function when the popup is dismissed.
48 | final VoidCallback? infoPopupDismissed;
49 |
50 | /// The [contentTitle] to show in the popup.
51 | final String? contentTitle;
52 |
53 | /// The [customContent] is the widget that will be custom shown in the popup.
54 | final Widget? Function()? customContent;
55 |
56 | /// The [areaBackgroundColor] is the background color of the area that
57 | final Color areaBackgroundColor;
58 |
59 | /// [arrowTheme] is the arrow theme of the popup.
60 | final InfoPopupArrowTheme arrowTheme;
61 |
62 | /// [contentTheme] is the content theme of the popup.
63 | final InfoPopupContentTheme contentTheme;
64 |
65 | /// [onAreaPressed] Called when the area outside the popup is pressed.
66 | final OnAreaPressed? onAreaPressed;
67 |
68 | /// [onLayoutMounted] Called when the info layout is mounted.
69 | final Function(Size size)? onLayoutMounted;
70 |
71 | /// The [contentOffset] is the offset of the content.
72 | final Offset contentOffset;
73 |
74 | /// The [indicatorOffset] is the offset of the indicator.
75 | final Offset indicatorOffset;
76 |
77 | /// The [dismissTriggerBehavior] is the dismissing behavior of the popup.
78 | final PopupDismissTriggerBehavior dismissTriggerBehavior;
79 |
80 | /// The [_infoPopupOverlayEntry] is the overlay entry of the popup.
81 | OverlayEntry? _infoPopupOverlayEntry;
82 |
83 | /// The [infoPopupContainerSize] is the size of the popup.
84 | Size? infoPopupContainerSize;
85 |
86 | /// [contentMaxWidth] is the max width of the content that is shown.
87 | /// If the [contentMaxWidth] is null, the max width will be eighty percent
88 | /// of the screen.
89 | final double? contentMaxWidth;
90 |
91 | /// The [enableHighlight] is the boolean value that indicates whether the
92 | /// highlight is enabled or not.
93 | final bool enableHighlight;
94 |
95 | /// The [highLightTheme] is the theme of the highlight. Can customize the
96 | /// highlight border radius and the padding.
97 | final HighLightTheme highLightTheme;
98 |
99 | /// The [enabledAutomaticConstraint] is the boolean value that indicates
100 | /// whether the popup will be constrained automatically or not.
101 | final bool enabledAutomaticConstraint;
102 |
103 | /// The [show] method is used to show the popup.
104 | void show() {
105 | _infoPopupOverlayEntry = OverlayEntry(
106 | builder: (_) {
107 | return OverlayInfoPopup(
108 | targetRenderBox: _targetRenderBox,
109 | contentTitle: contentTitle,
110 | layerLink: layerLink,
111 | customContent: customContent,
112 | areaBackgroundColor: areaBackgroundColor,
113 | indicatorTheme: arrowTheme,
114 | contentTheme: contentTheme,
115 | contentOffset: contentOffset,
116 | indicatorOffset: indicatorOffset,
117 | enableHighlight: enableHighlight,
118 | highlightTheme: highLightTheme,
119 | dismissTriggerBehavior: dismissTriggerBehavior,
120 | contentMaxWidth: contentMaxWidth,
121 | enabledAutomaticConstraint: enabledAutomaticConstraint,
122 | hideOverlay: dismissInfoPopup,
123 | onLayoutMounted: (Size size) {
124 | Future.delayed(
125 | const Duration(milliseconds: 30),
126 | () {
127 | infoPopupContainerSize = size;
128 | _infoPopupOverlayEntry?.markNeedsBuild();
129 |
130 | if (onLayoutMounted != null) {
131 | onLayoutMounted!.call(size);
132 | }
133 | },
134 | );
135 | },
136 | onAreaPressed: () {
137 | if (onAreaPressed != null) {
138 | onAreaPressed!.call(this);
139 | return;
140 | }
141 |
142 | dismissInfoPopup();
143 | },
144 | );
145 | },
146 | );
147 |
148 | Overlay.of(context).insert(_infoPopupOverlayEntry!);
149 | }
150 |
151 | /// The [isShowing] method is used to check if the popup is showing.
152 | bool get isShowing => _infoPopupOverlayEntry != null;
153 |
154 | /// [dismissInfoPopup] is used to dismiss the popup.
155 | void dismissInfoPopup() {
156 | if (_infoPopupOverlayEntry != null) {
157 | _infoPopupOverlayEntry!.remove();
158 | _infoPopupOverlayEntry = null;
159 |
160 | if (infoPopupDismissed != null) {
161 | infoPopupDismissed!.call();
162 | }
163 | }
164 | }
165 |
166 | /// [updateInfoPopupTargetRenderBox] is used to update the render box of the info text.
167 | void updateInfoPopupTargetRenderBox(RenderBox renderBox) {
168 | _targetRenderBox = renderBox;
169 | _infoPopupOverlayEntry?.markNeedsBuild();
170 | }
171 |
172 | /// [updateContent] is used to update the custom content of the popup.
173 | void updateContent() {
174 | Future.microtask(
175 | () {
176 | _infoPopupOverlayEntry?.markNeedsBuild();
177 | },
178 | );
179 | }
180 | }
181 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:info_popup/info_popup.dart';
3 |
4 | import 'list_example.dart';
5 |
6 | void main() => runApp(const MyApp());
7 |
8 | const Key infoPopupTextExampleKey = Key('info_popup_text_example');
9 | const String infoPopupTextExampleText = 'This is a popup';
10 |
11 | const Key infoPopupCustomExampleKey = Key('info_popup_custom_example');
12 | const String infoPopupCustomExampleText = 'This is a custom widget';
13 |
14 | const Key infoPopupLongTextExampleKey = Key('info_popup_long_text_example');
15 | const String infoPopupLongTextExampleText = '''
16 | Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Enim lobortis scelerisque fermentum dui faucibus in ornare quam viverra. Consectetur adipiscing elit ut aliquam purus sit. Nisl vel pretium lectus quam. Et odio pellentesque diam volutpat commodo. Diam vulputate ut pharetra sit amet aliquam id diam maecenas. Malesuada fames ac turpis egestas. Et sollicitudin ac orci phasellus egestas tellus rutrum. Pretium lectus quam id leo in. Semper risus in hendrerit gravida. Nullam ac tortor vitae purus faucibus ornare suspendisse sed. Non tellus orci ac auctor. Quis risus sed vulputate odio ut enim blandit.
17 | \n
18 | Nullam eget felis eget nunc lobortis mattis aliquam faucibus purus. Aenean et tortor at risus viverra adipiscing at in. Augue eget arcu dictum varius duis at consectetur. Est pellentesque elit ullamcorper dignissim cras. At consectetur lorem donec massa sapien faucibus et. Sit amet venenatis urna cursus eget. Dignissim cras tincidunt lobortis feugiat vivamus. Eget arcu dictum varius duis at. Aenean pharetra magna ac placerat. Enim nec dui nunc mattis enim ut tellus elementum. Laoreet suspendisse interdum consectetur libero. Tellus mauris a diam maecenas sed enim. Tortor posuere ac ut consequat semper viverra nam libero. Tellus molestie nunc non blandit massa.
19 | ''';
20 |
21 | const Key infoPopupArrowGapExampleKey = Key('info_popup_arrow_gap_example');
22 | const String infoPopupArrowGapExampleText = infoPopupLongTextExampleText;
23 |
24 | class MyApp extends StatelessWidget {
25 | const MyApp({super.key});
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return const MaterialApp(
30 | home: InfoPopupPage(),
31 | );
32 | }
33 | }
34 |
35 | class InfoPopupPage extends StatefulWidget {
36 | const InfoPopupPage({super.key});
37 |
38 | @override
39 | State createState() => _InfoPopupPageState();
40 | }
41 |
42 | class _InfoPopupPageState extends State {
43 | @override
44 | Widget build(BuildContext context) {
45 | final Size size = MediaQuery.sizeOf(context);
46 |
47 | return Scaffold(
48 | body: Align(
49 | child: SingleChildScrollView(
50 | child: Column(
51 | mainAxisAlignment: MainAxisAlignment.center,
52 | children: [
53 | const InfoPopupWidget(
54 | arrowTheme: InfoPopupArrowTheme(
55 | arrowDirection: ArrowDirection.down,
56 | color: Colors.pink,
57 | ),
58 | contentTitle: infoPopupTextExampleText,
59 | child: Text('Info Popup Info Text Example'),
60 | ),
61 | const SizedBox(height: 30),
62 | InfoPopupWidget(
63 | arrowTheme: const InfoPopupArrowTheme(
64 | color: Colors.black87,
65 | arrowDirection: ArrowDirection.down,
66 | ),
67 | customContent: () => Container(
68 | width: size.width * .8,
69 | decoration: BoxDecoration(
70 | color: Colors.blueGrey,
71 | borderRadius: BorderRadius.circular(10),
72 | ),
73 | padding: const EdgeInsets.all(10),
74 | child: const Column(
75 | children: [
76 | TextField(
77 | decoration: InputDecoration(
78 | hintText: 'Enter your name',
79 | hintStyle: TextStyle(color: Colors.white),
80 | enabledBorder: OutlineInputBorder(
81 | borderSide: BorderSide(color: Colors.white),
82 | ),
83 | ),
84 | ),
85 | SizedBox(height: 10),
86 | Center(
87 | child: Text(
88 | infoPopupCustomExampleText,
89 | style: TextStyle(
90 | color: Colors.white,
91 | ),
92 | ),
93 | ),
94 | ],
95 | ),
96 | ),
97 | child: const Text('Info Popup Custom Widget Example'),
98 | ),
99 | const SizedBox(height: 30),
100 | const InfoPopupWidget(
101 | arrowTheme: InfoPopupArrowTheme(
102 | color: Colors.pink,
103 | ),
104 | contentTitle: infoPopupLongTextExampleText,
105 | child: Text('Info Popup Long Info Text Example'),
106 | ),
107 | const SizedBox(height: 30),
108 | GestureDetector(
109 | onTap: () {
110 | showModalBottomSheet(
111 | context: context,
112 | builder: (_) {
113 | return Container(
114 | color: Colors.white,
115 | height: MediaQuery.of(context).size.height * .5,
116 | padding: const EdgeInsets.all(20),
117 | child: const Column(
118 | mainAxisAlignment: MainAxisAlignment.center,
119 | crossAxisAlignment: CrossAxisAlignment.start,
120 | children: [
121 | Row(
122 | children: [
123 | InfoPopupWidget(
124 | contentTitle: infoPopupLongTextExampleText,
125 | child: Icon(
126 | Icons.info,
127 | color: Colors.pink,
128 | ),
129 | ),
130 | SizedBox(width: 8),
131 | Text('Info Popup Inside Bottom Sheet Example'),
132 | ],
133 | ),
134 | ],
135 | ),
136 | );
137 | },
138 | );
139 | },
140 | behavior: HitTestBehavior.translucent,
141 | child: const Text('Bottom Sheet Inside Example'),
142 | ),
143 | const SizedBox(height: 30),
144 | const InfoPopupWidget(
145 | contentTitle: 'Info Popup Icon Examplee',
146 | arrowTheme: InfoPopupArrowTheme(
147 | color: Colors.pink,
148 | ),
149 | child: Icon(
150 | Icons.info,
151 | color: Colors.pink,
152 | ),
153 | ),
154 | const SizedBox(height: 30),
155 | const InfoPopupWidget(
156 | contentOffset: Offset(0, 30),
157 | contentTitle: infoPopupArrowGapExampleText,
158 | child: Text('Info Popup Arrow Gap Example'),
159 | ),
160 | const SizedBox(height: 30),
161 | GestureDetector(
162 | onTap: () {
163 | Navigator.of(context).push(
164 | MaterialPageRoute(
165 | builder: (_) {
166 | return const ListExample();
167 | },
168 | ),
169 | );
170 | },
171 | behavior: HitTestBehavior.translucent,
172 | child: const Text('List Example'),
173 | ),
174 | const SizedBox(height: 30),
175 | const InfoPopupWidget(
176 | enableHighlight: true,
177 | contentTitle: 'This is a HighLighted Info Popup',
178 | child: Text('HighLighted Info Popup Example'),
179 | ),
180 | ],
181 | ),
182 | ),
183 | ),
184 | );
185 | }
186 | }
187 |
--------------------------------------------------------------------------------
/lib/src/info_popup_widget.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/gestures.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter/rendering.dart';
4 | import 'package:flutter/scheduler.dart';
5 | import 'package:info_popup/info_popup.dart';
6 |
7 | /// A widget that shows a popup with text.
8 | class InfoPopupWidget extends StatefulWidget {
9 | /// Creates a [InfoPopupWidget] widget.
10 | const InfoPopupWidget({
11 | required this.child,
12 | this.onControllerCreated,
13 | this.infoPopupDismissed,
14 | this.contentTitle,
15 | this.customContent,
16 | this.areaBackgroundColor,
17 | this.arrowTheme,
18 | this.contentTheme,
19 | this.onAreaPressed,
20 | this.onLayoutMounted,
21 | this.dismissTriggerBehavior = PopupDismissTriggerBehavior.onTapArea,
22 | this.popupClickTriggerBehavior = PopupClickTriggerBehavior.onTap,
23 | this.contentOffset,
24 | this.indicatorOffset,
25 | this.contentMaxWidth,
26 | this.enableHighlight = false,
27 | this.highLightTheme,
28 | this.enableLog = false,
29 | this.enabledAutomaticConstraint = true,
30 | super.key,
31 | }) : assert(customContent == null || contentTitle == null,
32 | 'You can not use both customContent and contentTitle at the same time.');
33 |
34 | /// The [child] of the [InfoPopupWidget].
35 | final Widget child;
36 |
37 | /// [onControllerCreated] is called when the [InfoPopupController] is created.
38 | final OnControllerCreated? onControllerCreated;
39 |
40 | /// The [infoPopupDismissed] is the callback function when the popup is dismissed.
41 | final VoidCallback? infoPopupDismissed;
42 |
43 | /// The [contentTitle] to show in the popup.
44 | final String? contentTitle;
45 |
46 | /// The [customContent] is the widget that will be custom shown in the popup.
47 | final Widget? Function()? customContent;
48 |
49 | /// The [areaBackgroundColor] is the background color of the area that
50 | final Color? areaBackgroundColor;
51 |
52 | /// [arrowTheme] is the arrow theme of the popup.
53 | final InfoPopupArrowTheme? arrowTheme;
54 |
55 | /// [contentTheme] is the content theme of the popup.
56 | final InfoPopupContentTheme? contentTheme;
57 |
58 | /// [onAreaPressed] Called when the area outside the popup is pressed.
59 | final OnAreaPressed? onAreaPressed;
60 |
61 | /// [onLayoutMounted] Called when the info layout is mounted.
62 | final Function(Size size)? onLayoutMounted;
63 |
64 | /// The [dismissTriggerBehavior] is the showing behavior of the popup.
65 | final PopupDismissTriggerBehavior dismissTriggerBehavior;
66 |
67 | /// The [popupClickTriggerBehavior] is the click behavior of the popup.
68 | final PopupClickTriggerBehavior popupClickTriggerBehavior;
69 |
70 | /// The [contentOffset] is the offset of the content..
71 | final Offset? contentOffset;
72 |
73 | /// The [indicatorOffset] is the offset of the indicator.
74 | final Offset? indicatorOffset;
75 |
76 | /// [contentMaxWidth] is the max width of the content that is shown.
77 | /// If the [contentMaxWidth] is null, the max width will be eighty percent
78 | /// of the screen.
79 | final double? contentMaxWidth;
80 |
81 | /// The [enableHighlight] is the boolean value that indicates whether the
82 | /// highlight is enabled or not.
83 | final bool enableHighlight;
84 |
85 | /// The [enableLog] is the boolean value that indicates whether the
86 | /// log is enabled or not.
87 | ///
88 | /// If the [enableLog] is true, the log will be shown in the console.
89 | final bool enableLog;
90 |
91 | /// The [highLightTheme] is the theme of the highlight. Can customize the
92 | /// highlight border radius and the padding.
93 | final HighLightTheme? highLightTheme;
94 |
95 | /// The [enabledAutomaticConstraint] is the boolean value that indicates
96 | /// whether the popup will be constrained automatically or not.
97 | final bool enabledAutomaticConstraint;
98 |
99 | @override
100 | State createState() => _InfoPopupWidgetState();
101 | }
102 |
103 | class _InfoPopupWidgetState extends State {
104 | final GlobalKey> _infoPopupTargetKey = GlobalKey();
105 | InfoPopupController? _infoPopupController;
106 | bool _isControllerInitialized = false;
107 | final LayerLink _layerLink = LayerLink();
108 |
109 | @override
110 | void dispose() {
111 | if (_infoPopupController != null && _infoPopupController!.isShowing) {
112 | _infoPopupController!.dismissInfoPopup();
113 | }
114 | super.dispose();
115 | }
116 |
117 | bool get _isMouseRegionPermitted {
118 | final bool mouseIsConnected =
119 | RendererBinding.instance.mouseTracker.mouseIsConnected;
120 |
121 | if (!mouseIsConnected || !_isControllerInitialized) {
122 | return false;
123 | }
124 |
125 | return true;
126 | }
127 |
128 | @override
129 | void didUpdateWidget(InfoPopupWidget oldWidget) {
130 | if (widget.customContent != null && _infoPopupController != null) {
131 | _infoPopupController!.updateContent();
132 | } else if (oldWidget.contentTitle != widget.contentTitle ||
133 | oldWidget.areaBackgroundColor != widget.areaBackgroundColor ||
134 | oldWidget.arrowTheme != widget.arrowTheme ||
135 | oldWidget.contentTheme != widget.contentTheme ||
136 | oldWidget.contentOffset != widget.contentOffset ||
137 | oldWidget.indicatorOffset != widget.indicatorOffset ||
138 | oldWidget.contentMaxWidth != widget.contentMaxWidth ||
139 | oldWidget.enableHighlight != widget.enableHighlight ||
140 | oldWidget.highLightTheme != widget.highLightTheme ||
141 | oldWidget.dismissTriggerBehavior != widget.dismissTriggerBehavior) {
142 | if (_infoPopupController != null || _isControllerInitialized) {
143 | _infoPopupController!.dismissInfoPopup();
144 | _infoPopupController = null;
145 | _isControllerInitialized = false;
146 | }
147 | setState(() {});
148 | }
149 |
150 | super.didUpdateWidget(oldWidget);
151 | }
152 |
153 | @override
154 | Widget build(BuildContext context) {
155 | SchedulerBinding.instance.addPostFrameCallback((_) => _updateRenderBox());
156 | return MouseRegion(
157 | onHover: (PointerHoverEvent event) {
158 | if (_isMouseRegionPermitted && !_infoPopupController!.isShowing) {
159 | _infoPopupController!.show();
160 | }
161 | },
162 | onExit: (PointerExitEvent event) {
163 | if (widget.dismissTriggerBehavior ==
164 | PopupDismissTriggerBehavior.manuel) {
165 | return;
166 | }
167 |
168 | if (_isMouseRegionPermitted && _infoPopupController!.isShowing) {
169 | _infoPopupController!.dismissInfoPopup();
170 | }
171 | },
172 | child: GestureDetector(
173 | onTap: _behaviour(),
174 | onLongPressEnd: _onLongPressEnd,
175 | behavior: HitTestBehavior.translucent,
176 | child: CompositedTransformTarget(
177 | link: _layerLink,
178 | child: Container(
179 | key: _infoPopupTargetKey,
180 | child: widget.child,
181 | ),
182 | ),
183 | ),
184 | );
185 | }
186 |
187 | Function()? _behaviour() {
188 | // ignore: use_if_null_to_convert_nulls_to_bools
189 | if (_infoPopupController?.isShowing == true) {
190 | return null;
191 | }
192 |
193 | if (widget.popupClickTriggerBehavior == PopupClickTriggerBehavior.none) {
194 | return null;
195 | }
196 |
197 | return () {
198 | if (_infoPopupController != null && !_infoPopupController!.isShowing) {
199 | _infoPopupController!.show();
200 | }
201 | };
202 | }
203 |
204 | Future _updateRenderBox() async {
205 | final BuildContext? context = _infoPopupTargetKey.currentContext;
206 |
207 | if (!mounted || context == null) {
208 | return;
209 | }
210 |
211 | final RenderBox? renderBox = context.findRenderObject() as RenderBox?;
212 |
213 | if (renderBox == null) {
214 | return;
215 | }
216 |
217 | _infoPopupController = _infoPopupController ??= InfoPopupController(
218 | context: context,
219 | targetRenderBox: renderBox,
220 | layerLink: _layerLink,
221 | contentTitle: widget.contentTitle,
222 | customContent: widget.customContent,
223 | areaBackgroundColor: widget.areaBackgroundColor ??
224 | PopupConstants.defaultAreaBackgroundColor,
225 | arrowTheme: widget.arrowTheme ?? const InfoPopupArrowTheme(),
226 | contentTheme: widget.contentTheme ?? const InfoPopupContentTheme(),
227 | onAreaPressed: widget.onAreaPressed,
228 | enabledAutomaticConstraint: widget.enabledAutomaticConstraint,
229 | onLayoutMounted: (Size size) {
230 | setState(() {
231 | widget.onLayoutMounted?.call(size);
232 | });
233 | },
234 | dismissTriggerBehavior: widget.dismissTriggerBehavior,
235 | infoPopupDismissed: () {
236 | setState(() {
237 | widget.infoPopupDismissed?.call();
238 | });
239 | },
240 | contentOffset: widget.contentOffset ?? const Offset(0, 0),
241 | indicatorOffset: widget.indicatorOffset ?? const Offset(0, 0),
242 | contentMaxWidth: widget.contentMaxWidth,
243 | enableHighlight: widget.enableHighlight,
244 | highLightTheme: widget.highLightTheme ?? HighLightTheme.defaultTheme(),
245 | );
246 |
247 | if (!_isControllerInitialized && widget.onControllerCreated != null) {
248 | widget.onControllerCreated!.call(_infoPopupController!);
249 | }
250 |
251 | _infoPopupController!.updateInfoPopupTargetRenderBox(renderBox);
252 |
253 | _isControllerInitialized = true;
254 | }
255 |
256 | Function? _onLongPressEnd(LongPressEndDetails details) {
257 | if (widget.popupClickTriggerBehavior !=
258 | PopupClickTriggerBehavior.onLongPress) {
259 | return null;
260 | }
261 |
262 | return () {
263 | if (_infoPopupController != null && !_infoPopupController!.isShowing) {
264 | _infoPopupController!.show();
265 | }
266 | };
267 | }
268 | }
269 |
--------------------------------------------------------------------------------
/lib/src/overlays/overlay_entry_layout.dart:
--------------------------------------------------------------------------------
1 | part of '../controllers/info_popup_controller.dart';
2 |
3 | /// [InfoPopup] is a widget that shows a popup with a text and an arrow indicator.
4 | class OverlayInfoPopup extends StatefulWidget {
5 | /// Creates a [InfoPopup] widget.
6 | const OverlayInfoPopup({
7 | required LayerLink layerLink,
8 | required RenderBox targetRenderBox,
9 | required Color areaBackgroundColor,
10 | required InfoPopupArrowTheme indicatorTheme,
11 | required InfoPopupContentTheme contentTheme,
12 | required VoidCallback onAreaPressed,
13 | required Function(Size size) onLayoutMounted,
14 | required Offset contentOffset,
15 | required Offset indicatorOffset,
16 | required PopupDismissTriggerBehavior dismissTriggerBehavior,
17 | required bool enableHighlight,
18 | required HighLightTheme highlightTheme,
19 | required VoidCallback hideOverlay,
20 | required bool enabledAutomaticConstraint,
21 | Widget? Function()? customContent,
22 | String? contentTitle,
23 | double? contentMaxWidth,
24 | super.key,
25 | }) : _layerLink = layerLink,
26 | _targetRenderBox = targetRenderBox,
27 | _areaBackgroundColor = areaBackgroundColor,
28 | _indicatorTheme = indicatorTheme,
29 | _contentTheme = contentTheme,
30 | _onAreaPressed = onAreaPressed,
31 | _onLayoutMounted = onLayoutMounted,
32 | _contentOffset = contentOffset,
33 | _indicatorOffset = indicatorOffset,
34 | _dismissTriggerBehavior = dismissTriggerBehavior,
35 | _customContent = customContent,
36 | _contentTitle = contentTitle,
37 | _contentMaxWidth = contentMaxWidth,
38 | _enableHighlight = enableHighlight,
39 | _highLightTheme = highlightTheme,
40 | _enabledAutomaticConstraint = enabledAutomaticConstraint,
41 | _hideOverlay = hideOverlay;
42 |
43 | final LayerLink _layerLink;
44 | final RenderBox _targetRenderBox;
45 | final Widget? Function()? _customContent;
46 | final String? _contentTitle;
47 | final Color _areaBackgroundColor;
48 | final InfoPopupArrowTheme _indicatorTheme;
49 | final InfoPopupContentTheme _contentTheme;
50 | final VoidCallback _onAreaPressed;
51 | final Function(Size size) _onLayoutMounted;
52 | final Offset _contentOffset;
53 | final Offset _indicatorOffset;
54 | final PopupDismissTriggerBehavior _dismissTriggerBehavior;
55 | final double? _contentMaxWidth;
56 | final bool _enableHighlight;
57 | final HighLightTheme _highLightTheme;
58 | final VoidCallback _hideOverlay;
59 | final bool _enabledAutomaticConstraint;
60 |
61 | @override
62 | State createState() => _OverlayInfoPopupState();
63 | }
64 |
65 | class _OverlayInfoPopupState extends State {
66 | final GlobalKey _bodyKey = GlobalKey();
67 |
68 | @override
69 | void initState() {
70 | GestureBinding.instance.pointerRouter.addGlobalRoute(_handlePointerEvent);
71 | WidgetsBinding.instance.addPostFrameCallback(
72 | (_) {
73 | _updateContentLayoutSize();
74 | },
75 | );
76 | super.initState();
77 | }
78 |
79 | bool _isPointListenerDisposed = false;
80 |
81 | void _handlePointerEvent(PointerEvent event) {
82 | if (!mounted) {
83 | return;
84 | }
85 |
86 | final bool mouseIsConnected =
87 | RendererBinding.instance.mouseTracker.mouseIsConnected;
88 |
89 | if (mouseIsConnected) {
90 | GestureBinding.instance.pointerRouter.removeGlobalRoute(
91 | _handlePointerEvent,
92 | );
93 | _isPointListenerDisposed = true;
94 | return;
95 | }
96 |
97 | final BuildContext? bodyContext = _bodyKey.currentContext;
98 |
99 | if (bodyContext == null) {
100 | return;
101 | }
102 |
103 | final RenderBox? bodyRenderBox =
104 | bodyContext.findRenderObject() as RenderBox?;
105 |
106 | if (bodyRenderBox == null) {
107 | return;
108 | }
109 |
110 | final Offset clickPosition = event.position;
111 | final Offset contentPosition = bodyRenderBox.localToGlobal(Offset.zero);
112 |
113 | switch (widget._dismissTriggerBehavior) {
114 | case PopupDismissTriggerBehavior.onTapContent:
115 | if (clickPosition.dx >= contentPosition.dx &&
116 | clickPosition.dx <= contentPosition.dx + contentSize.width &&
117 | clickPosition.dy >= contentPosition.dy &&
118 | clickPosition.dy <= contentPosition.dy + contentSize.height) {
119 | widget._hideOverlay();
120 | }
121 | break;
122 | case PopupDismissTriggerBehavior.onTapArea:
123 | if (!(clickPosition.dx >= contentPosition.dx &&
124 | clickPosition.dx <= contentPosition.dx + contentSize.width &&
125 | clickPosition.dy >= contentPosition.dy &&
126 | clickPosition.dy <= contentPosition.dy + contentSize.height)) {
127 | widget._onAreaPressed();
128 | }
129 | break;
130 | case PopupDismissTriggerBehavior.anyWhere:
131 | widget._hideOverlay();
132 | break;
133 | case PopupDismissTriggerBehavior.manuel:
134 | // do nothing
135 | break;
136 | }
137 | }
138 |
139 | @override
140 | void dispose() {
141 | if (!_isPointListenerDisposed) {
142 | GestureBinding.instance.pointerRouter.removeGlobalRoute(
143 | _handlePointerEvent,
144 | );
145 | }
146 |
147 | super.dispose();
148 | }
149 |
150 | ArrowDirection? _overridenArrowDirection;
151 |
152 | ArrowDirection get arrowDirection {
153 | if (_overridenArrowDirection != null) {
154 | return _overridenArrowDirection!;
155 | }
156 |
157 | return widget._indicatorTheme.arrowDirection;
158 | }
159 |
160 | Offset get _indicatorOffset {
161 | final double indicatorWidth = widget._indicatorTheme.arrowSize.width;
162 | switch (arrowDirection) {
163 | case ArrowDirection.up:
164 | return Offset(
165 | _targetWidgetRect.width / 2 - indicatorWidth / 2,
166 | _targetWidgetRect.height,
167 | ) +
168 | widget._indicatorOffset +
169 | _highlightOffset;
170 | case ArrowDirection.down:
171 | return Offset(
172 | _targetWidgetRect.width / 2 - indicatorWidth / 2,
173 | -widget._indicatorTheme.arrowSize.height,
174 | ) +
175 | widget._indicatorOffset +
176 | _highlightOffset;
177 | }
178 | }
179 |
180 | Offset get _highlightOffset {
181 | double highlightVerticalGap = 0;
182 |
183 | if (widget._enableHighlight) {
184 | highlightVerticalGap = widget._highLightTheme.padding.bottom;
185 | }
186 |
187 | switch (arrowDirection) {
188 | case ArrowDirection.up:
189 | return Offset(0, highlightVerticalGap);
190 | case ArrowDirection.down:
191 | return Offset(0, -highlightVerticalGap);
192 | }
193 | }
194 |
195 | Offset get _bodyOffset {
196 | Offset targetCenterOffset = Offset.zero;
197 |
198 | final double contentWidth = contentSize.width;
199 | final double contentHeight = contentSize.height;
200 | final double targetWidth = _targetWidgetRect.width;
201 | final double targetHeight = _targetWidgetRect.height;
202 | final double contentDxCenter = targetWidth / 2 - contentWidth / 2;
203 |
204 | switch (arrowDirection) {
205 | case ArrowDirection.up:
206 | targetCenterOffset = Offset(
207 | contentDxCenter,
208 | targetHeight + widget._indicatorTheme.arrowSize.height,
209 | );
210 | break;
211 | case ArrowDirection.down:
212 | targetCenterOffset = Offset(
213 | contentDxCenter,
214 | -(contentHeight + widget._indicatorTheme.arrowSize.height),
215 | );
216 | break;
217 | }
218 |
219 | targetCenterOffset += _highlightOffset;
220 |
221 | final double contentLeft = contentDxCenter + _targetOffset.dx;
222 | final double contentRight = contentLeft + contentWidth;
223 | final double screenWidth = context.screenWidth;
224 |
225 | if (contentLeft < 0) {
226 | targetCenterOffset += Offset(-contentLeft, 0);
227 | } else if (contentRight > screenWidth) {
228 | targetCenterOffset += Offset(screenWidth - contentRight, 0);
229 | }
230 |
231 | return targetCenterOffset + widget._contentOffset;
232 | }
233 |
234 | Size? _contentSize;
235 |
236 | Size get contentSize {
237 | if (!_isLayoutMounted) {
238 | return widget._targetRenderBox.size;
239 | } else {
240 | return _contentSize!;
241 | }
242 | }
243 |
244 | @override
245 | void didUpdateWidget(covariant OverlayInfoPopup oldWidget) {
246 | _updateContentLayoutSize();
247 | _contentMaxHeight;
248 | super.didUpdateWidget(oldWidget);
249 | }
250 |
251 | @override
252 | void didChangeDependencies() {
253 | _updateContentLayoutSize();
254 | _contentMaxHeight;
255 | super.didChangeDependencies();
256 | }
257 |
258 | bool _isLayoutMounted = false;
259 |
260 | void _updateContentLayoutSize() {
261 | Future.microtask(
262 | () {
263 | Future.delayed(
264 | const Duration(milliseconds: 50),
265 | () {
266 | if (!mounted) {
267 | return;
268 | }
269 |
270 | if (_bodyKey.currentContext == null) {
271 | return;
272 | }
273 |
274 | final RenderBox? renderBox =
275 | _bodyKey.currentContext!.findRenderObject() as RenderBox?;
276 |
277 | if (renderBox == null) {
278 | return;
279 | }
280 |
281 | final Size size = renderBox.size;
282 |
283 | if (size != _contentSize) {
284 | setState(
285 | () {
286 | _contentSize = size;
287 |
288 | widget._onLayoutMounted(size);
289 | _isLayoutMounted = true;
290 | },
291 | );
292 | }
293 | },
294 | );
295 | },
296 | );
297 | }
298 |
299 | Rect get _targetWidgetRect {
300 | if (!widget._targetRenderBox.attached) {
301 | return Rect.zero;
302 | }
303 |
304 | final Offset offset = widget._targetRenderBox.localToGlobal(Offset.zero);
305 |
306 | return Rect.fromLTWH(
307 | offset.dx,
308 | offset.dy,
309 | widget._targetRenderBox.size.width,
310 | widget._targetRenderBox.size.height,
311 | );
312 | }
313 |
314 | double get _contentMaxWidth {
315 | if (widget._contentMaxWidth == null) {
316 | return context.screenWidth * .8;
317 | } else {
318 | return widget._contentMaxWidth!;
319 | }
320 | }
321 |
322 | double get _contentMaxHeight {
323 | const int padding = 16;
324 | final double screenHeight = context.screenHeight;
325 | final double bottomPadding = context.mediaQuery.padding.bottom;
326 | final double topPadding = context.mediaQuery.padding.top;
327 | final double targetWidgetTopPosition = _targetWidgetRect.top;
328 | final double contentHeight = _contentSize?.height ?? 0;
329 | final bool isArrowDirectionOverriden = _overridenArrowDirection != null;
330 |
331 | switch (arrowDirection) {
332 | case ArrowDirection.up:
333 | final double belowSpace = screenHeight -
334 | targetWidgetTopPosition -
335 | _targetWidgetRect.height -
336 | padding -
337 | bottomPadding;
338 |
339 | if (!widget._indicatorTheme.enabledAutoArrowDirection) {
340 | return belowSpace;
341 | }
342 |
343 | if ((belowSpace - contentHeight) > 0 || isArrowDirectionOverriden) {
344 | return belowSpace;
345 | } else {
346 | _setIndicatorDirection(ArrowDirection.down);
347 | return 0;
348 | }
349 | case ArrowDirection.down:
350 | final double aboveSpace = targetWidgetTopPosition - topPadding;
351 |
352 | if (!widget._indicatorTheme.enabledAutoArrowDirection) {
353 | return aboveSpace;
354 | }
355 |
356 | if ((aboveSpace - contentHeight) > 0 || isArrowDirectionOverriden) {
357 | return aboveSpace;
358 | } else {
359 | _setIndicatorDirection(ArrowDirection.up);
360 | return 0;
361 | }
362 | }
363 | }
364 |
365 | Offset get _areaOffset {
366 | if (widget._enableHighlight) {
367 | return Offset(-_targetWidgetRect.left, -_targetWidgetRect.top);
368 | }
369 |
370 | switch (widget._dismissTriggerBehavior) {
371 | case PopupDismissTriggerBehavior.onTapContent:
372 | return _bodyOffset;
373 | case PopupDismissTriggerBehavior.onTapArea:
374 | case PopupDismissTriggerBehavior.anyWhere:
375 | case PopupDismissTriggerBehavior.manuel:
376 | return Offset(-_targetWidgetRect.left, -_targetWidgetRect.top);
377 | }
378 | }
379 |
380 | Offset get _targetOffset {
381 | if (!widget._targetRenderBox.attached) {
382 | return Offset.zero;
383 | }
384 |
385 | return widget._targetRenderBox.localToGlobal(Offset.zero);
386 | }
387 |
388 | bool get _dismissBehaviorIsOnTapContent =>
389 | widget._dismissTriggerBehavior ==
390 | PopupDismissTriggerBehavior.onTapContent;
391 |
392 | @override
393 | Widget build(BuildContext context) {
394 | _contentMaxHeight;
395 | return ClipPath(
396 | clipper: widget._enableHighlight
397 | ? _HighLighter(
398 | area: Rect.fromLTWH(
399 | _targetWidgetRect.left,
400 | _targetWidgetRect.top,
401 | _targetWidgetRect.width,
402 | _targetWidgetRect.height,
403 | ),
404 | padding: widget._highLightTheme.padding,
405 | radius: widget._highLightTheme.radius,
406 | )
407 | : null,
408 | child: Align(
409 | child: CompositedTransformFollower(
410 | link: widget._layerLink,
411 | showWhenUnlinked: false,
412 | offset: _areaOffset,
413 | child: Material(
414 | color: widget._enableHighlight
415 | ? widget._highLightTheme.backgroundColor
416 | : widget._areaBackgroundColor,
417 | type: (!widget._enableHighlight &&
418 | widget._areaBackgroundColor == Colors.transparent)
419 | ? MaterialType.transparency
420 | : MaterialType.canvas,
421 | child: SizedBox(
422 | height:
423 | _dismissBehaviorIsOnTapContent ? null : context.screenHeight,
424 | width:
425 | _dismissBehaviorIsOnTapContent ? null : context.screenWidth,
426 | child: Column(
427 | children: [
428 | CompositedTransformFollower(
429 | link: widget._layerLink,
430 | showWhenUnlinked: false,
431 | offset: _indicatorOffset,
432 | child: AnimatedScale(
433 | scale: _isLayoutMounted ? 1.0 : 0.0,
434 | duration: const Duration(milliseconds: 50),
435 | alignment: Alignment.topCenter,
436 | child: CustomPaint(
437 | size: widget._indicatorTheme.arrowSize,
438 | painter: widget._indicatorTheme.arrowPainter ??
439 | ArrowIndicatorPainter(
440 | arrowDirection: arrowDirection,
441 | arrowColor: widget._indicatorTheme.color,
442 | ),
443 | ),
444 | ),
445 | ),
446 | CompositedTransformFollower(
447 | link: widget._layerLink,
448 | showWhenUnlinked: false,
449 | offset: _bodyOffset,
450 | child: AnimatedScale(
451 | scale: _isLayoutMounted ? 1.0 : 0.0,
452 | duration: const Duration(milliseconds: 50),
453 | alignment: Alignment.topCenter,
454 | child: Builder(builder: (BuildContext context) {
455 | final Container content = Container(
456 | key: _bodyKey,
457 | decoration: widget._customContent != null
458 | ? null
459 | : BoxDecoration(
460 | color: widget._contentTheme
461 | .infoContainerBackgroundColor,
462 | borderRadius:
463 | widget._contentTheme.contentBorderRadius,
464 | boxShadow: const [
465 | BoxShadow(
466 | color: Color(0xFF808080),
467 | blurRadius: 1.0,
468 | ),
469 | ],
470 | ),
471 | padding: widget._customContent != null
472 | ? null
473 | : widget._contentTheme.contentPadding,
474 | child: SingleChildScrollView(
475 | child: widget._customContent == null
476 | ? Text(
477 | widget._contentTitle ?? '',
478 | style: widget._contentTheme.infoTextStyle,
479 | textAlign:
480 | widget._contentTheme.infoTextAlign,
481 | )
482 | : widget._customContent!(),
483 | ),
484 | );
485 |
486 | if (!widget._enabledAutomaticConstraint) {
487 | return content;
488 | }
489 |
490 | if (_contentMaxHeight < 0) {
491 | return content;
492 | }
493 |
494 | return ConstrainedBox(
495 | constraints: BoxConstraints(
496 | maxWidth: _contentMaxWidth,
497 | maxHeight: _contentMaxHeight,
498 | ),
499 | child: content,
500 | );
501 | }),
502 | ),
503 | )
504 | ],
505 | ),
506 | ),
507 | ),
508 | ),
509 | ),
510 | );
511 | }
512 |
513 | void _setIndicatorDirection(ArrowDirection newDirection) {
514 | if (!widget._indicatorTheme.enabledAutoArrowDirection) {
515 | return;
516 | }
517 |
518 | setState(() {
519 | _overridenArrowDirection = newDirection;
520 | });
521 | }
522 | }
523 |
--------------------------------------------------------------------------------
/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 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXCopyFilesBuildPhase section */
19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
20 | isa = PBXCopyFilesBuildPhase;
21 | buildActionMask = 2147483647;
22 | dstPath = "";
23 | dstSubfolderSpec = 10;
24 | files = (
25 | );
26 | name = "Embed Frameworks";
27 | runOnlyForDeploymentPostprocessing = 0;
28 | };
29 | /* End PBXCopyFilesBuildPhase section */
30 |
31 | /* Begin PBXFileReference section */
32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | /* End PBXFrameworksBuildPhase section */
56 |
57 | /* Begin PBXGroup section */
58 | 9740EEB11CF90186004384FC /* Flutter */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
65 | );
66 | name = Flutter;
67 | sourceTree = "";
68 | };
69 | 97C146E51CF9000F007C117D = {
70 | isa = PBXGroup;
71 | children = (
72 | 9740EEB11CF90186004384FC /* Flutter */,
73 | 97C146F01CF9000F007C117D /* Runner */,
74 | 97C146EF1CF9000F007C117D /* Products */,
75 | );
76 | sourceTree = "";
77 | };
78 | 97C146EF1CF9000F007C117D /* Products */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 97C146EE1CF9000F007C117D /* Runner.app */,
82 | );
83 | name = Products;
84 | sourceTree = "";
85 | };
86 | 97C146F01CF9000F007C117D /* Runner */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
92 | 97C147021CF9000F007C117D /* Info.plist */,
93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
97 | );
98 | path = Runner;
99 | sourceTree = "";
100 | };
101 | /* End PBXGroup section */
102 |
103 | /* Begin PBXNativeTarget section */
104 | 97C146ED1CF9000F007C117D /* Runner */ = {
105 | isa = PBXNativeTarget;
106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
107 | buildPhases = (
108 | 9740EEB61CF901F6004384FC /* Run Script */,
109 | 97C146EA1CF9000F007C117D /* Sources */,
110 | 97C146EB1CF9000F007C117D /* Frameworks */,
111 | 97C146EC1CF9000F007C117D /* Resources */,
112 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
114 | );
115 | buildRules = (
116 | );
117 | dependencies = (
118 | );
119 | name = Runner;
120 | productName = Runner;
121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
122 | productType = "com.apple.product-type.application";
123 | };
124 | /* End PBXNativeTarget section */
125 |
126 | /* Begin PBXProject section */
127 | 97C146E61CF9000F007C117D /* Project object */ = {
128 | isa = PBXProject;
129 | attributes = {
130 | LastUpgradeCheck = 1430;
131 | ORGANIZATIONNAME = "";
132 | TargetAttributes = {
133 | 97C146ED1CF9000F007C117D = {
134 | CreatedOnToolsVersion = 7.3.1;
135 | LastSwiftMigration = 1100;
136 | };
137 | };
138 | };
139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
140 | compatibilityVersion = "Xcode 9.3";
141 | developmentRegion = en;
142 | hasScannedForEncodings = 0;
143 | knownRegions = (
144 | en,
145 | Base,
146 | );
147 | mainGroup = 97C146E51CF9000F007C117D;
148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
149 | projectDirPath = "";
150 | projectRoot = "";
151 | targets = (
152 | 97C146ED1CF9000F007C117D /* Runner */,
153 | );
154 | };
155 | /* End PBXProject section */
156 |
157 | /* Begin PBXResourcesBuildPhase section */
158 | 97C146EC1CF9000F007C117D /* Resources */ = {
159 | isa = PBXResourcesBuildPhase;
160 | buildActionMask = 2147483647;
161 | files = (
162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
166 | );
167 | runOnlyForDeploymentPostprocessing = 0;
168 | };
169 | /* End PBXResourcesBuildPhase section */
170 |
171 | /* Begin PBXShellScriptBuildPhase section */
172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
173 | isa = PBXShellScriptBuildPhase;
174 | alwaysOutOfDate = 1;
175 | buildActionMask = 2147483647;
176 | files = (
177 | );
178 | inputPaths = (
179 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
180 | );
181 | name = "Thin Binary";
182 | outputPaths = (
183 | );
184 | runOnlyForDeploymentPostprocessing = 0;
185 | shellPath = /bin/sh;
186 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
187 | };
188 | 9740EEB61CF901F6004384FC /* Run Script */ = {
189 | isa = PBXShellScriptBuildPhase;
190 | alwaysOutOfDate = 1;
191 | buildActionMask = 2147483647;
192 | files = (
193 | );
194 | inputPaths = (
195 | );
196 | name = "Run Script";
197 | outputPaths = (
198 | );
199 | runOnlyForDeploymentPostprocessing = 0;
200 | shellPath = /bin/sh;
201 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
202 | };
203 | /* End PBXShellScriptBuildPhase section */
204 |
205 | /* Begin PBXSourcesBuildPhase section */
206 | 97C146EA1CF9000F007C117D /* Sources */ = {
207 | isa = PBXSourcesBuildPhase;
208 | buildActionMask = 2147483647;
209 | files = (
210 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
211 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
212 | );
213 | runOnlyForDeploymentPostprocessing = 0;
214 | };
215 | /* End PBXSourcesBuildPhase section */
216 |
217 | /* Begin PBXVariantGroup section */
218 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
219 | isa = PBXVariantGroup;
220 | children = (
221 | 97C146FB1CF9000F007C117D /* Base */,
222 | );
223 | name = Main.storyboard;
224 | sourceTree = "";
225 | };
226 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
227 | isa = PBXVariantGroup;
228 | children = (
229 | 97C147001CF9000F007C117D /* Base */,
230 | );
231 | name = LaunchScreen.storyboard;
232 | sourceTree = "";
233 | };
234 | /* End PBXVariantGroup section */
235 |
236 | /* Begin XCBuildConfiguration section */
237 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
238 | isa = XCBuildConfiguration;
239 | buildSettings = {
240 | ALWAYS_SEARCH_USER_PATHS = NO;
241 | CLANG_ANALYZER_NONNULL = YES;
242 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
243 | CLANG_CXX_LIBRARY = "libc++";
244 | CLANG_ENABLE_MODULES = YES;
245 | CLANG_ENABLE_OBJC_ARC = YES;
246 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
247 | CLANG_WARN_BOOL_CONVERSION = YES;
248 | CLANG_WARN_COMMA = YES;
249 | CLANG_WARN_CONSTANT_CONVERSION = YES;
250 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
252 | CLANG_WARN_EMPTY_BODY = YES;
253 | CLANG_WARN_ENUM_CONVERSION = YES;
254 | CLANG_WARN_INFINITE_RECURSION = YES;
255 | CLANG_WARN_INT_CONVERSION = YES;
256 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
257 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
258 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
259 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
260 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
261 | CLANG_WARN_STRICT_PROTOTYPES = YES;
262 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
263 | CLANG_WARN_UNREACHABLE_CODE = YES;
264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
265 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
266 | COPY_PHASE_STRIP = NO;
267 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
268 | ENABLE_NS_ASSERTIONS = NO;
269 | ENABLE_STRICT_OBJC_MSGSEND = YES;
270 | GCC_C_LANGUAGE_STANDARD = gnu99;
271 | GCC_NO_COMMON_BLOCKS = YES;
272 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
273 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
274 | GCC_WARN_UNDECLARED_SELECTOR = YES;
275 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
276 | GCC_WARN_UNUSED_FUNCTION = YES;
277 | GCC_WARN_UNUSED_VARIABLE = YES;
278 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
279 | MTL_ENABLE_DEBUG_INFO = NO;
280 | SDKROOT = iphoneos;
281 | SUPPORTED_PLATFORMS = iphoneos;
282 | TARGETED_DEVICE_FAMILY = "1,2";
283 | VALIDATE_PRODUCT = YES;
284 | };
285 | name = Profile;
286 | };
287 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
288 | isa = XCBuildConfiguration;
289 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
290 | buildSettings = {
291 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
292 | CLANG_ENABLE_MODULES = YES;
293 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
294 | DEVELOPMENT_TEAM = J5QH55CXD8;
295 | ENABLE_BITCODE = NO;
296 | INFOPLIST_FILE = Runner/Info.plist;
297 | LD_RUNPATH_SEARCH_PATHS = (
298 | "$(inherited)",
299 | "@executable_path/Frameworks",
300 | );
301 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
302 | PRODUCT_NAME = "$(TARGET_NAME)";
303 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
304 | SWIFT_VERSION = 5.0;
305 | VERSIONING_SYSTEM = "apple-generic";
306 | };
307 | name = Profile;
308 | };
309 | 97C147031CF9000F007C117D /* Debug */ = {
310 | isa = XCBuildConfiguration;
311 | buildSettings = {
312 | ALWAYS_SEARCH_USER_PATHS = NO;
313 | CLANG_ANALYZER_NONNULL = YES;
314 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
315 | CLANG_CXX_LIBRARY = "libc++";
316 | CLANG_ENABLE_MODULES = YES;
317 | CLANG_ENABLE_OBJC_ARC = YES;
318 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
319 | CLANG_WARN_BOOL_CONVERSION = YES;
320 | CLANG_WARN_COMMA = YES;
321 | CLANG_WARN_CONSTANT_CONVERSION = YES;
322 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
323 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
324 | CLANG_WARN_EMPTY_BODY = YES;
325 | CLANG_WARN_ENUM_CONVERSION = YES;
326 | CLANG_WARN_INFINITE_RECURSION = YES;
327 | CLANG_WARN_INT_CONVERSION = YES;
328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
332 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
333 | CLANG_WARN_STRICT_PROTOTYPES = YES;
334 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
335 | CLANG_WARN_UNREACHABLE_CODE = YES;
336 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
337 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
338 | COPY_PHASE_STRIP = NO;
339 | DEBUG_INFORMATION_FORMAT = dwarf;
340 | ENABLE_STRICT_OBJC_MSGSEND = YES;
341 | ENABLE_TESTABILITY = YES;
342 | GCC_C_LANGUAGE_STANDARD = gnu99;
343 | GCC_DYNAMIC_NO_PIC = NO;
344 | GCC_NO_COMMON_BLOCKS = YES;
345 | GCC_OPTIMIZATION_LEVEL = 0;
346 | GCC_PREPROCESSOR_DEFINITIONS = (
347 | "DEBUG=1",
348 | "$(inherited)",
349 | );
350 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
351 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
352 | GCC_WARN_UNDECLARED_SELECTOR = YES;
353 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
354 | GCC_WARN_UNUSED_FUNCTION = YES;
355 | GCC_WARN_UNUSED_VARIABLE = YES;
356 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
357 | MTL_ENABLE_DEBUG_INFO = YES;
358 | ONLY_ACTIVE_ARCH = YES;
359 | SDKROOT = iphoneos;
360 | TARGETED_DEVICE_FAMILY = "1,2";
361 | };
362 | name = Debug;
363 | };
364 | 97C147041CF9000F007C117D /* Release */ = {
365 | isa = XCBuildConfiguration;
366 | buildSettings = {
367 | ALWAYS_SEARCH_USER_PATHS = NO;
368 | CLANG_ANALYZER_NONNULL = YES;
369 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
370 | CLANG_CXX_LIBRARY = "libc++";
371 | CLANG_ENABLE_MODULES = YES;
372 | CLANG_ENABLE_OBJC_ARC = YES;
373 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
374 | CLANG_WARN_BOOL_CONVERSION = YES;
375 | CLANG_WARN_COMMA = YES;
376 | CLANG_WARN_CONSTANT_CONVERSION = YES;
377 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
378 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
379 | CLANG_WARN_EMPTY_BODY = YES;
380 | CLANG_WARN_ENUM_CONVERSION = YES;
381 | CLANG_WARN_INFINITE_RECURSION = YES;
382 | CLANG_WARN_INT_CONVERSION = YES;
383 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
384 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
385 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
386 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
387 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
388 | CLANG_WARN_STRICT_PROTOTYPES = YES;
389 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
390 | CLANG_WARN_UNREACHABLE_CODE = YES;
391 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
392 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
393 | COPY_PHASE_STRIP = NO;
394 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
395 | ENABLE_NS_ASSERTIONS = NO;
396 | ENABLE_STRICT_OBJC_MSGSEND = YES;
397 | GCC_C_LANGUAGE_STANDARD = gnu99;
398 | GCC_NO_COMMON_BLOCKS = YES;
399 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
400 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
401 | GCC_WARN_UNDECLARED_SELECTOR = YES;
402 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
403 | GCC_WARN_UNUSED_FUNCTION = YES;
404 | GCC_WARN_UNUSED_VARIABLE = YES;
405 | IPHONEOS_DEPLOYMENT_TARGET = 11.0;
406 | MTL_ENABLE_DEBUG_INFO = NO;
407 | SDKROOT = iphoneos;
408 | SUPPORTED_PLATFORMS = iphoneos;
409 | SWIFT_COMPILATION_MODE = wholemodule;
410 | SWIFT_OPTIMIZATION_LEVEL = "-O";
411 | TARGETED_DEVICE_FAMILY = "1,2";
412 | VALIDATE_PRODUCT = YES;
413 | };
414 | name = Release;
415 | };
416 | 97C147061CF9000F007C117D /* Debug */ = {
417 | isa = XCBuildConfiguration;
418 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
419 | buildSettings = {
420 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
421 | CLANG_ENABLE_MODULES = YES;
422 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
423 | DEVELOPMENT_TEAM = J5QH55CXD8;
424 | ENABLE_BITCODE = NO;
425 | INFOPLIST_FILE = Runner/Info.plist;
426 | LD_RUNPATH_SEARCH_PATHS = (
427 | "$(inherited)",
428 | "@executable_path/Frameworks",
429 | );
430 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
431 | PRODUCT_NAME = "$(TARGET_NAME)";
432 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
433 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
434 | SWIFT_VERSION = 5.0;
435 | VERSIONING_SYSTEM = "apple-generic";
436 | };
437 | name = Debug;
438 | };
439 | 97C147071CF9000F007C117D /* Release */ = {
440 | isa = XCBuildConfiguration;
441 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
442 | buildSettings = {
443 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
444 | CLANG_ENABLE_MODULES = YES;
445 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
446 | DEVELOPMENT_TEAM = J5QH55CXD8;
447 | ENABLE_BITCODE = NO;
448 | INFOPLIST_FILE = Runner/Info.plist;
449 | LD_RUNPATH_SEARCH_PATHS = (
450 | "$(inherited)",
451 | "@executable_path/Frameworks",
452 | );
453 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
454 | PRODUCT_NAME = "$(TARGET_NAME)";
455 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
456 | SWIFT_VERSION = 5.0;
457 | VERSIONING_SYSTEM = "apple-generic";
458 | };
459 | name = Release;
460 | };
461 | /* End XCBuildConfiguration section */
462 |
463 | /* Begin XCConfigurationList section */
464 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
465 | isa = XCConfigurationList;
466 | buildConfigurations = (
467 | 97C147031CF9000F007C117D /* Debug */,
468 | 97C147041CF9000F007C117D /* Release */,
469 | 249021D3217E4FDB00AE95B9 /* Profile */,
470 | );
471 | defaultConfigurationIsVisible = 0;
472 | defaultConfigurationName = Release;
473 | };
474 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
475 | isa = XCConfigurationList;
476 | buildConfigurations = (
477 | 97C147061CF9000F007C117D /* Debug */,
478 | 97C147071CF9000F007C117D /* Release */,
479 | 249021D4217E4FDB00AE95B9 /* Profile */,
480 | );
481 | defaultConfigurationIsVisible = 0;
482 | defaultConfigurationName = Release;
483 | };
484 | /* End XCConfigurationList section */
485 | };
486 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
487 | }
488 |
--------------------------------------------------------------------------------