Showing
100 changed files
with
3959 additions
and
0 deletions
Too many changes to show.
To preserve performance only 100 of 100+ files are displayed.
.gitignore
0 → 100644
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 | +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. | ||
26 | +/pubspec.lock | ||
27 | +**/doc/api/ | ||
28 | +.dart_tool/ | ||
29 | +build/ |
.metadata
0 → 100644
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: "78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9" | ||
8 | + channel: "stable" | ||
9 | + | ||
10 | +project_type: plugin | ||
11 | + | ||
12 | +# Tracks metadata for the flutter migrate command | ||
13 | +migration: | ||
14 | + platforms: | ||
15 | + - platform: root | ||
16 | + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 | ||
17 | + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 | ||
18 | + - platform: android | ||
19 | + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 | ||
20 | + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 | ||
21 | + - platform: ios | ||
22 | + create_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 | ||
23 | + base_revision: 78666c8dc57e9f7548ca9f8dd0740fbf0c658dc9 | ||
24 | + | ||
25 | + # User provided section | ||
26 | + | ||
27 | + # List of Local paths (relative to this file) that should be | ||
28 | + # ignored by the migrate tool. | ||
29 | + # | ||
30 | + # Files that are not part of the templates will be ignored by default. | ||
31 | + unmanaged_files: | ||
32 | + - 'lib/main.dart' | ||
33 | + - 'ios/Runner.xcodeproj/project.pbxproj' |
CHANGELOG.md
0 → 100644
README.md
0 → 100644
1 | +# auto_track | ||
2 | + | ||
3 | +Auto Track Plugin | ||
4 | + | ||
5 | +## Getting Started | ||
6 | + | ||
7 | +This project is a starting point for a Flutter | ||
8 | +[plug-in package](https://flutter.dev/developing-packages/), | ||
9 | +a specialized package that includes platform-specific implementation code for | ||
10 | +Android and/or iOS. | ||
11 | + | ||
12 | +For help getting started with Flutter development, view the | ||
13 | +[online documentation](https://flutter.dev/docs), which offers tutorials, | ||
14 | +samples, guidance on mobile development, and a full API reference. | ||
15 | + |
analysis_options.yaml
0 → 100644
android/.gitignore
0 → 100644
android/build.gradle
0 → 100644
1 | +group 'mobi.iflow.flutter.auto_track.auto_track' | ||
2 | +version '1.0-SNAPSHOT' | ||
3 | + | ||
4 | +buildscript { | ||
5 | + ext.kotlin_version = '1.7.10' | ||
6 | + repositories { | ||
7 | + google() | ||
8 | + mavenCentral() | ||
9 | + } | ||
10 | + | ||
11 | + dependencies { | ||
12 | + classpath 'com.android.tools.build:gradle:7.3.0' | ||
13 | + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | ||
14 | + } | ||
15 | +} | ||
16 | + | ||
17 | +allprojects { | ||
18 | + repositories { | ||
19 | + google() | ||
20 | + mavenCentral() | ||
21 | + } | ||
22 | +} | ||
23 | + | ||
24 | +apply plugin: 'com.android.library' | ||
25 | +apply plugin: 'kotlin-android' | ||
26 | + | ||
27 | +android { | ||
28 | + if (project.android.hasProperty("namespace")) { | ||
29 | + namespace 'mobi.iflow.flutter.auto_track.auto_track' | ||
30 | + } | ||
31 | + | ||
32 | + compileSdkVersion 33 | ||
33 | + | ||
34 | + compileOptions { | ||
35 | + sourceCompatibility JavaVersion.VERSION_1_8 | ||
36 | + targetCompatibility JavaVersion.VERSION_1_8 | ||
37 | + } | ||
38 | + | ||
39 | + kotlinOptions { | ||
40 | + jvmTarget = '1.8' | ||
41 | + } | ||
42 | + | ||
43 | + sourceSets { | ||
44 | + main.java.srcDirs += 'src/main/kotlin' | ||
45 | + test.java.srcDirs += 'src/test/kotlin' | ||
46 | + } | ||
47 | + | ||
48 | + defaultConfig { | ||
49 | + minSdkVersion 19 | ||
50 | + } | ||
51 | + | ||
52 | + dependencies { | ||
53 | + testImplementation 'org.jetbrains.kotlin:kotlin-test' | ||
54 | + testImplementation 'org.mockito:mockito-core:5.0.0' | ||
55 | + } | ||
56 | + | ||
57 | + testOptions { | ||
58 | + unitTests.all { | ||
59 | + useJUnitPlatform() | ||
60 | + | ||
61 | + testLogging { | ||
62 | + events "passed", "skipped", "failed", "standardOut", "standardError" | ||
63 | + outputs.upToDateWhen {false} | ||
64 | + showStandardStreams = true | ||
65 | + } | ||
66 | + } | ||
67 | + } | ||
68 | +} |
android/settings.gradle
0 → 100644
1 | +rootProject.name = 'auto_track' |
android/src/main/AndroidManifest.xml
0 → 100644
1 | +package mobi.iflow.flutter.auto_track.auto_track | ||
2 | + | ||
3 | +import androidx.annotation.NonNull | ||
4 | + | ||
5 | +import io.flutter.embedding.engine.plugins.FlutterPlugin | ||
6 | +import io.flutter.plugin.common.MethodCall | ||
7 | +import io.flutter.plugin.common.MethodChannel | ||
8 | +import io.flutter.plugin.common.MethodChannel.MethodCallHandler | ||
9 | +import io.flutter.plugin.common.MethodChannel.Result | ||
10 | + | ||
11 | +/** AutoTrackPlugin */ | ||
12 | +class AutoTrackPlugin: FlutterPlugin, MethodCallHandler { | ||
13 | + /// The MethodChannel that will the communication between Flutter and native Android | ||
14 | + /// | ||
15 | + /// This local reference serves to register the plugin with the Flutter Engine and unregister it | ||
16 | + /// when the Flutter Engine is detached from the Activity | ||
17 | + private lateinit var channel : MethodChannel | ||
18 | + | ||
19 | + override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { | ||
20 | + channel = MethodChannel(flutterPluginBinding.binaryMessenger, "auto_track") | ||
21 | + channel.setMethodCallHandler(this) | ||
22 | + } | ||
23 | + | ||
24 | + override fun onMethodCall(call: MethodCall, result: Result) { | ||
25 | + if (call.method == "getPlatformVersion") { | ||
26 | + result.success("Android ${android.os.Build.VERSION.RELEASE}") | ||
27 | + } else { | ||
28 | + result.notImplemented() | ||
29 | + } | ||
30 | + } | ||
31 | + | ||
32 | + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { | ||
33 | + channel.setMethodCallHandler(null) | ||
34 | + } | ||
35 | +} |
1 | +package mobi.iflow.flutter.auto_track.auto_track | ||
2 | + | ||
3 | +import io.flutter.plugin.common.MethodCall | ||
4 | +import io.flutter.plugin.common.MethodChannel | ||
5 | +import kotlin.test.Test | ||
6 | +import org.mockito.Mockito | ||
7 | + | ||
8 | +/* | ||
9 | + * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation. | ||
10 | + * | ||
11 | + * Once you have built the plugin's example app, you can run these tests from the command | ||
12 | + * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or | ||
13 | + * you can run them directly from IDEs that support JUnit such as Android Studio. | ||
14 | + */ | ||
15 | + | ||
16 | +internal class AutoTrackPluginTest { | ||
17 | + @Test | ||
18 | + fun onMethodCall_getPlatformVersion_returnsExpectedValue() { | ||
19 | + val plugin = AutoTrackPlugin() | ||
20 | + | ||
21 | + val call = MethodCall("getPlatformVersion", null) | ||
22 | + val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java) | ||
23 | + plugin.onMethodCall(call, mockResult) | ||
24 | + | ||
25 | + Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE) | ||
26 | + } | ||
27 | +} |
example/.gitignore
0 → 100644
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 | +.pub-cache/ | ||
31 | +.pub/ | ||
32 | +/build/ | ||
33 | + | ||
34 | +# Symbolication related | ||
35 | +app.*.symbols | ||
36 | + | ||
37 | +# Obfuscation related | ||
38 | +app.*.map.json | ||
39 | + | ||
40 | +# Android Studio will place build artifacts here | ||
41 | +/android/app/debug | ||
42 | +/android/app/profile | ||
43 | +/android/app/release |
example/README.md
0 → 100644
1 | +# auto_track_example | ||
2 | + | ||
3 | +Demonstrates how to use the auto_track plugin. | ||
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. |
example/analysis_options.yaml
0 → 100644
1 | +# This file configures the analyzer, which statically analyzes Dart code to | ||
2 | +# check for errors, warnings, and lints. | ||
3 | +# | ||
4 | +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled | ||
5 | +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be | ||
6 | +# invoked from the command line by running `flutter analyze`. | ||
7 | + | ||
8 | +# The following line activates a set of recommended lints for Flutter apps, | ||
9 | +# packages, and plugins designed to encourage good coding practices. | ||
10 | +include: package:flutter_lints/flutter.yaml | ||
11 | + | ||
12 | +linter: | ||
13 | + # The lint rules applied to this project can be customized in the | ||
14 | + # section below to disable rules from the `package:flutter_lints/flutter.yaml` | ||
15 | + # included above or to enable additional rules. A list of all available lints | ||
16 | + # and their documentation is published at https://dart.dev/lints. | ||
17 | + # | ||
18 | + # Instead of disabling a lint rule for the entire project in the | ||
19 | + # section below, it can also be suppressed for a single line of code | ||
20 | + # or a specific dart file by using the `// ignore: name_of_lint` and | ||
21 | + # `// ignore_for_file: name_of_lint` syntax on the line or in the file | ||
22 | + # producing the lint. | ||
23 | + rules: | ||
24 | + # avoid_print: false # Uncomment to disable the `avoid_print` rule | ||
25 | + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule | ||
26 | + | ||
27 | +# Additional information about this file can be found at | ||
28 | +# https://dart.dev/guides/language/analysis-options |
example/android/.gitignore
0 → 100644
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 |
example/android/app/build.gradle
0 → 100644
1 | +plugins { | ||
2 | + id "com.android.application" | ||
3 | + id "kotlin-android" | ||
4 | + id "dev.flutter.flutter-gradle-plugin" | ||
5 | +} | ||
6 | + | ||
7 | +def localProperties = new Properties() | ||
8 | +def localPropertiesFile = rootProject.file('local.properties') | ||
9 | +if (localPropertiesFile.exists()) { | ||
10 | + localPropertiesFile.withReader('UTF-8') { reader -> | ||
11 | + localProperties.load(reader) | ||
12 | + } | ||
13 | +} | ||
14 | + | ||
15 | +def flutterVersionCode = localProperties.getProperty('flutter.versionCode') | ||
16 | +if (flutterVersionCode == null) { | ||
17 | + flutterVersionCode = '1' | ||
18 | +} | ||
19 | + | ||
20 | +def flutterVersionName = localProperties.getProperty('flutter.versionName') | ||
21 | +if (flutterVersionName == null) { | ||
22 | + flutterVersionName = '1.0' | ||
23 | +} | ||
24 | + | ||
25 | +android { | ||
26 | + namespace "mobi.iflow.flutter.auto_track.auto_track_example" | ||
27 | + compileSdkVersion flutter.compileSdkVersion | ||
28 | + ndkVersion flutter.ndkVersion | ||
29 | + | ||
30 | + compileOptions { | ||
31 | + sourceCompatibility JavaVersion.VERSION_1_8 | ||
32 | + targetCompatibility JavaVersion.VERSION_1_8 | ||
33 | + } | ||
34 | + | ||
35 | + kotlinOptions { | ||
36 | + jvmTarget = '1.8' | ||
37 | + } | ||
38 | + | ||
39 | + sourceSets { | ||
40 | + main.java.srcDirs += 'src/main/kotlin' | ||
41 | + } | ||
42 | + | ||
43 | + defaultConfig { | ||
44 | + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). | ||
45 | + applicationId "mobi.iflow.flutter.auto_track.auto_track_example" | ||
46 | + // You can update the following values to match your application needs. | ||
47 | + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. | ||
48 | + minSdkVersion flutter.minSdkVersion | ||
49 | + targetSdkVersion flutter.targetSdkVersion | ||
50 | + versionCode flutterVersionCode.toInteger() | ||
51 | + versionName flutterVersionName | ||
52 | + } | ||
53 | + | ||
54 | + buildTypes { | ||
55 | + release { | ||
56 | + // TODO: Add your own signing config for the release build. | ||
57 | + // Signing with the debug keys for now, so `flutter run --release` works. | ||
58 | + signingConfig signingConfigs.debug | ||
59 | + } | ||
60 | + } | ||
61 | +} | ||
62 | + | ||
63 | +flutter { | ||
64 | + source '../..' | ||
65 | +} | ||
66 | + | ||
67 | +dependencies {} |
1 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
2 | + <!-- The INTERNET permission is required for development. Specifically, | ||
3 | + the Flutter tool needs it to communicate with the running application | ||
4 | + to allow setting breakpoints, to provide hot reload, etc. | ||
5 | + --> | ||
6 | + <uses-permission android:name="android.permission.INTERNET"/> | ||
7 | +</manifest> |
1 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
2 | + <application | ||
3 | + android:label="auto_track_example" | ||
4 | + android:name="${applicationName}" | ||
5 | + android:icon="@mipmap/ic_launcher"> | ||
6 | + <activity | ||
7 | + android:name=".MainActivity" | ||
8 | + android:exported="true" | ||
9 | + android:launchMode="singleTop" | ||
10 | + android:theme="@style/LaunchTheme" | ||
11 | + android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" | ||
12 | + android:hardwareAccelerated="true" | ||
13 | + android:windowSoftInputMode="adjustResize"> | ||
14 | + <!-- Specifies an Android theme to apply to this Activity as soon as | ||
15 | + the Android process has started. This theme is visible to the user | ||
16 | + while the Flutter UI initializes. After that, this theme continues | ||
17 | + to determine the Window background behind the Flutter UI. --> | ||
18 | + <meta-data | ||
19 | + android:name="io.flutter.embedding.android.NormalTheme" | ||
20 | + android:resource="@style/NormalTheme" | ||
21 | + /> | ||
22 | + <intent-filter> | ||
23 | + <action android:name="android.intent.action.MAIN"/> | ||
24 | + <category android:name="android.intent.category.LAUNCHER"/> | ||
25 | + </intent-filter> | ||
26 | + </activity> | ||
27 | + <!-- Don't delete the meta-data below. | ||
28 | + This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> | ||
29 | + <meta-data | ||
30 | + android:name="flutterEmbedding" | ||
31 | + android:value="2" /> | ||
32 | + </application> | ||
33 | +</manifest> |
1 | +<?xml version="1.0" encoding="utf-8"?> | ||
2 | +<!-- Modify this file to customize your launch splash screen --> | ||
3 | +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | ||
4 | + <item android:drawable="?android:colorBackground" /> | ||
5 | + | ||
6 | + <!-- You can insert your own image assets here --> | ||
7 | + <!-- <item> | ||
8 | + <bitmap | ||
9 | + android:gravity="center" | ||
10 | + android:src="@mipmap/launch_image" /> | ||
11 | + </item> --> | ||
12 | +</layer-list> |
1 | +<?xml version="1.0" encoding="utf-8"?> | ||
2 | +<!-- Modify this file to customize your launch splash screen --> | ||
3 | +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> | ||
4 | + <item android:drawable="@android:color/white" /> | ||
5 | + | ||
6 | + <!-- You can insert your own image assets here --> | ||
7 | + <!-- <item> | ||
8 | + <bitmap | ||
9 | + android:gravity="center" | ||
10 | + android:src="@mipmap/launch_image" /> | ||
11 | + </item> --> | ||
12 | +</layer-list> |

544 Bytes

442 Bytes

721 Bytes

1.01 KB

1.41 KB
1 | +<?xml version="1.0" encoding="utf-8"?> | ||
2 | +<resources> | ||
3 | + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> | ||
4 | + <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> | ||
5 | + <!-- Show a splash screen on the activity. Automatically removed when | ||
6 | + the Flutter engine draws its first frame --> | ||
7 | + <item name="android:windowBackground">@drawable/launch_background</item> | ||
8 | + </style> | ||
9 | + <!-- Theme applied to the Android Window as soon as the process has started. | ||
10 | + This theme determines the color of the Android Window while your | ||
11 | + Flutter UI initializes, as well as behind your Flutter UI while its | ||
12 | + running. | ||
13 | + | ||
14 | + This Theme is only used starting with V2 of Flutter's Android embedding. --> | ||
15 | + <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar"> | ||
16 | + <item name="android:windowBackground">?android:colorBackground</item> | ||
17 | + </style> | ||
18 | +</resources> |
1 | +<?xml version="1.0" encoding="utf-8"?> | ||
2 | +<resources> | ||
3 | + <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> | ||
4 | + <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> | ||
5 | + <!-- Show a splash screen on the activity. Automatically removed when | ||
6 | + the Flutter engine draws its first frame --> | ||
7 | + <item name="android:windowBackground">@drawable/launch_background</item> | ||
8 | + </style> | ||
9 | + <!-- Theme applied to the Android Window as soon as the process has started. | ||
10 | + This theme determines the color of the Android Window while your | ||
11 | + Flutter UI initializes, as well as behind your Flutter UI while its | ||
12 | + running. | ||
13 | + | ||
14 | + This Theme is only used starting with V2 of Flutter's Android embedding. --> | ||
15 | + <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar"> | ||
16 | + <item name="android:windowBackground">?android:colorBackground</item> | ||
17 | + </style> | ||
18 | +</resources> |
1 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> | ||
2 | + <!-- The INTERNET permission is required for development. Specifically, | ||
3 | + the Flutter tool needs it to communicate with the running application | ||
4 | + to allow setting breakpoints, to provide hot reload, etc. | ||
5 | + --> | ||
6 | + <uses-permission android:name="android.permission.INTERNET"/> | ||
7 | +</manifest> |
example/android/build.gradle
0 → 100644
1 | +buildscript { | ||
2 | + ext.kotlin_version = '1.7.10' | ||
3 | + repositories { | ||
4 | + google() | ||
5 | + mavenCentral() | ||
6 | + } | ||
7 | + | ||
8 | + dependencies { | ||
9 | + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | ||
10 | + } | ||
11 | +} | ||
12 | + | ||
13 | +allprojects { | ||
14 | + repositories { | ||
15 | + google() | ||
16 | + mavenCentral() | ||
17 | + } | ||
18 | +} | ||
19 | + | ||
20 | +rootProject.buildDir = '../build' | ||
21 | +subprojects { | ||
22 | + project.buildDir = "${rootProject.buildDir}/${project.name}" | ||
23 | +} | ||
24 | +subprojects { | ||
25 | + project.evaluationDependsOn(':app') | ||
26 | +} | ||
27 | + | ||
28 | +tasks.register("clean", Delete) { | ||
29 | + delete rootProject.buildDir | ||
30 | +} |
example/android/gradle.properties
0 → 100644
example/android/settings.gradle
0 → 100644
1 | +pluginManagement { | ||
2 | + def flutterSdkPath = { | ||
3 | + def properties = new Properties() | ||
4 | + file("local.properties").withInputStream { properties.load(it) } | ||
5 | + def flutterSdkPath = properties.getProperty("flutter.sdk") | ||
6 | + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" | ||
7 | + return flutterSdkPath | ||
8 | + } | ||
9 | + settings.ext.flutterSdkPath = flutterSdkPath() | ||
10 | + | ||
11 | + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") | ||
12 | + | ||
13 | + repositories { | ||
14 | + google() | ||
15 | + mavenCentral() | ||
16 | + gradlePluginPortal() | ||
17 | + } | ||
18 | + | ||
19 | + plugins { | ||
20 | + id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false | ||
21 | + } | ||
22 | +} | ||
23 | + | ||
24 | +plugins { | ||
25 | + id "dev.flutter.flutter-plugin-loader" version "1.0.0" | ||
26 | + id "com.android.application" version "7.3.0" apply false | ||
27 | +} | ||
28 | + | ||
29 | +include ":app" |
1 | +// This is a basic Flutter integration test. | ||
2 | +// | ||
3 | +// Since integration tests run in a full Flutter application, they can interact | ||
4 | +// with the host side of a plugin implementation, unlike Dart unit tests. | ||
5 | +// | ||
6 | +// For more information about Flutter integration tests, please see | ||
7 | +// https://docs.flutter.dev/cookbook/testing/integration/introduction | ||
8 | + | ||
9 | + | ||
10 | +import 'package:flutter_test/flutter_test.dart'; | ||
11 | +import 'package:integration_test/integration_test.dart'; | ||
12 | + | ||
13 | +import 'package:auto_track/auto_track.dart'; | ||
14 | + | ||
15 | +void main() { | ||
16 | + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); | ||
17 | + | ||
18 | + testWidgets('getPlatformVersion test', (WidgetTester tester) async { | ||
19 | + final AutoTrack plugin = AutoTrack(); | ||
20 | + // final String? version = await plugin.getPlatformVersion(); | ||
21 | + // The version string depends on the host platform running the test, so | ||
22 | + // just assert that some non-empty string is returned. | ||
23 | + // expect(version?.isNotEmpty, true); | ||
24 | + }); | ||
25 | +} |
example/ios/.gitignore
0 → 100644
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 |
example/ios/Flutter/AppFrameworkInfo.plist
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
3 | +<plist version="1.0"> | ||
4 | +<dict> | ||
5 | + <key>CFBundleDevelopmentRegion</key> | ||
6 | + <string>en</string> | ||
7 | + <key>CFBundleExecutable</key> | ||
8 | + <string>App</string> | ||
9 | + <key>CFBundleIdentifier</key> | ||
10 | + <string>io.flutter.flutter.app</string> | ||
11 | + <key>CFBundleInfoDictionaryVersion</key> | ||
12 | + <string>6.0</string> | ||
13 | + <key>CFBundleName</key> | ||
14 | + <string>App</string> | ||
15 | + <key>CFBundlePackageType</key> | ||
16 | + <string>FMWK</string> | ||
17 | + <key>CFBundleShortVersionString</key> | ||
18 | + <string>1.0</string> | ||
19 | + <key>CFBundleSignature</key> | ||
20 | + <string>????</string> | ||
21 | + <key>CFBundleVersion</key> | ||
22 | + <string>1.0</string> | ||
23 | + <key>MinimumOSVersion</key> | ||
24 | + <string>11.0</string> | ||
25 | +</dict> | ||
26 | +</plist> |
example/ios/Flutter/Debug.xcconfig
0 → 100644
example/ios/Flutter/Release.xcconfig
0 → 100644
example/ios/Podfile
0 → 100644
1 | +# Uncomment this line to define a global platform for your project | ||
2 | +# platform :ios, '11.0' | ||
3 | + | ||
4 | +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. | ||
5 | +ENV['COCOAPODS_DISABLE_STATS'] = 'true' | ||
6 | + | ||
7 | +project 'Runner', { | ||
8 | + 'Debug' => :debug, | ||
9 | + 'Profile' => :release, | ||
10 | + 'Release' => :release, | ||
11 | +} | ||
12 | + | ||
13 | +def flutter_root | ||
14 | + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) | ||
15 | + unless File.exist?(generated_xcode_build_settings_path) | ||
16 | + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" | ||
17 | + end | ||
18 | + | ||
19 | + File.foreach(generated_xcode_build_settings_path) do |line| | ||
20 | + matches = line.match(/FLUTTER_ROOT\=(.*)/) | ||
21 | + return matches[1].strip if matches | ||
22 | + end | ||
23 | + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" | ||
24 | +end | ||
25 | + | ||
26 | +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) | ||
27 | + | ||
28 | +flutter_ios_podfile_setup | ||
29 | + | ||
30 | +target 'Runner' do | ||
31 | + use_frameworks! | ||
32 | + use_modular_headers! | ||
33 | + | ||
34 | + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) | ||
35 | + target 'RunnerTests' do | ||
36 | + inherit! :search_paths | ||
37 | + end | ||
38 | +end | ||
39 | + | ||
40 | +post_install do |installer| | ||
41 | + installer.pods_project.targets.each do |target| | ||
42 | + flutter_additional_ios_build_settings(target) | ||
43 | + end | ||
44 | +end |
example/ios/Podfile.lock
0 → 100644
1 | +PODS: | ||
2 | + - auto_track (0.0.1): | ||
3 | + - Flutter | ||
4 | + - Flutter (1.0.0) | ||
5 | + - integration_test (0.0.1): | ||
6 | + - Flutter | ||
7 | + | ||
8 | +DEPENDENCIES: | ||
9 | + - auto_track (from `.symlinks/plugins/auto_track/ios`) | ||
10 | + - Flutter (from `Flutter`) | ||
11 | + - integration_test (from `.symlinks/plugins/integration_test/ios`) | ||
12 | + | ||
13 | +EXTERNAL SOURCES: | ||
14 | + auto_track: | ||
15 | + :path: ".symlinks/plugins/auto_track/ios" | ||
16 | + Flutter: | ||
17 | + :path: Flutter | ||
18 | + integration_test: | ||
19 | + :path: ".symlinks/plugins/integration_test/ios" | ||
20 | + | ||
21 | +SPEC CHECKSUMS: | ||
22 | + auto_track: bca6ccfba824abcc8de9d51ac0eeed3dfb141d5d | ||
23 | + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 | ||
24 | + integration_test: 13825b8a9334a850581300559b8839134b124670 | ||
25 | + | ||
26 | +PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189 | ||
27 | + | ||
28 | +COCOAPODS: 1.12.1 |
example/ios/Runner.xcodeproj/project.pbxproj
0 → 100644
1 | +// !$*UTF8*$! | ||
2 | +{ | ||
3 | + archiveVersion = 1; | ||
4 | + classes = { | ||
5 | + }; | ||
6 | + objectVersion = 54; | ||
7 | + objects = { | ||
8 | + | ||
9 | +/* Begin PBXBuildFile section */ | ||
10 | + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; | ||
11 | + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; | ||
12 | + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; | ||
13 | + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; | ||
14 | + 868313707E054002C9EBBC23 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 830356C44B37234273CFF03C /* Pods_RunnerTests.framework */; }; | ||
15 | + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; | ||
16 | + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; | ||
17 | + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; | ||
18 | + 9A4BC01B8C3E977CE74C747D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD602501C62F389F5FFD3029 /* Pods_Runner.framework */; }; | ||
19 | +/* End PBXBuildFile section */ | ||
20 | + | ||
21 | +/* Begin PBXContainerItemProxy section */ | ||
22 | + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { | ||
23 | + isa = PBXContainerItemProxy; | ||
24 | + containerPortal = 97C146E61CF9000F007C117D /* Project object */; | ||
25 | + proxyType = 1; | ||
26 | + remoteGlobalIDString = 97C146ED1CF9000F007C117D; | ||
27 | + remoteInfo = Runner; | ||
28 | + }; | ||
29 | +/* End PBXContainerItemProxy section */ | ||
30 | + | ||
31 | +/* Begin PBXCopyFilesBuildPhase section */ | ||
32 | + 9705A1C41CF9048500538489 /* Embed Frameworks */ = { | ||
33 | + isa = PBXCopyFilesBuildPhase; | ||
34 | + buildActionMask = 2147483647; | ||
35 | + dstPath = ""; | ||
36 | + dstSubfolderSpec = 10; | ||
37 | + files = ( | ||
38 | + ); | ||
39 | + name = "Embed Frameworks"; | ||
40 | + runOnlyForDeploymentPostprocessing = 0; | ||
41 | + }; | ||
42 | +/* End PBXCopyFilesBuildPhase section */ | ||
43 | + | ||
44 | +/* Begin PBXFileReference section */ | ||
45 | + 12AD15FE86700BFEC11CC92D /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; }; | ||
46 | + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; | ||
47 | + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; | ||
48 | + 229DB93165071F31B9EA4231 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | ||
49 | + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; }; | ||
50 | + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
51 | + 361BA2F76E56261380B96FE7 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; | ||
52 | + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; | ||
53 | + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; | ||
54 | + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | ||
55 | + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; | ||
56 | + 830356C44B37234273CFF03C /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
57 | + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; | ||
58 | + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; | ||
59 | + 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
60 | + 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; }; | ||
61 | + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; | ||
62 | + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; | ||
63 | + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | ||
64 | + D1653FC9B1E82CA1821FD739 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | ||
65 | + DD602501C62F389F5FFD3029 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
66 | + EB9A8197B759B617DFCCB6F1 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; }; | ||
67 | + F782189BA737B0E3C9A3C330 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; }; | ||
68 | +/* End PBXFileReference section */ | ||
69 | + | ||
70 | +/* Begin PBXFrameworksBuildPhase section */ | ||
71 | + 93DAF74BCB585A23752E0704 /* Frameworks */ = { | ||
72 | + isa = PBXFrameworksBuildPhase; | ||
73 | + buildActionMask = 2147483647; | ||
74 | + files = ( | ||
75 | + 868313707E054002C9EBBC23 /* Pods_RunnerTests.framework in Frameworks */, | ||
76 | + ); | ||
77 | + runOnlyForDeploymentPostprocessing = 0; | ||
78 | + }; | ||
79 | + 97C146EB1CF9000F007C117D /* Frameworks */ = { | ||
80 | + isa = PBXFrameworksBuildPhase; | ||
81 | + buildActionMask = 2147483647; | ||
82 | + files = ( | ||
83 | + 9A4BC01B8C3E977CE74C747D /* Pods_Runner.framework in Frameworks */, | ||
84 | + ); | ||
85 | + runOnlyForDeploymentPostprocessing = 0; | ||
86 | + }; | ||
87 | +/* End PBXFrameworksBuildPhase section */ | ||
88 | + | ||
89 | +/* Begin PBXGroup section */ | ||
90 | + 331C8082294A63A400263BE5 /* RunnerTests */ = { | ||
91 | + isa = PBXGroup; | ||
92 | + children = ( | ||
93 | + 331C807B294A618700263BE5 /* RunnerTests.swift */, | ||
94 | + ); | ||
95 | + path = RunnerTests; | ||
96 | + sourceTree = "<group>"; | ||
97 | + }; | ||
98 | + 5F8A6DB13075A4D449F6A685 /* Frameworks */ = { | ||
99 | + isa = PBXGroup; | ||
100 | + children = ( | ||
101 | + DD602501C62F389F5FFD3029 /* Pods_Runner.framework */, | ||
102 | + 830356C44B37234273CFF03C /* Pods_RunnerTests.framework */, | ||
103 | + ); | ||
104 | + name = Frameworks; | ||
105 | + sourceTree = "<group>"; | ||
106 | + }; | ||
107 | + 936DC10DBAFF5951E82260FA /* Pods */ = { | ||
108 | + isa = PBXGroup; | ||
109 | + children = ( | ||
110 | + D1653FC9B1E82CA1821FD739 /* Pods-Runner.debug.xcconfig */, | ||
111 | + 229DB93165071F31B9EA4231 /* Pods-Runner.release.xcconfig */, | ||
112 | + 361BA2F76E56261380B96FE7 /* Pods-Runner.profile.xcconfig */, | ||
113 | + EB9A8197B759B617DFCCB6F1 /* Pods-RunnerTests.debug.xcconfig */, | ||
114 | + F782189BA737B0E3C9A3C330 /* Pods-RunnerTests.release.xcconfig */, | ||
115 | + 12AD15FE86700BFEC11CC92D /* Pods-RunnerTests.profile.xcconfig */, | ||
116 | + ); | ||
117 | + name = Pods; | ||
118 | + path = Pods; | ||
119 | + sourceTree = "<group>"; | ||
120 | + }; | ||
121 | + 9740EEB11CF90186004384FC /* Flutter */ = { | ||
122 | + isa = PBXGroup; | ||
123 | + children = ( | ||
124 | + 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, | ||
125 | + 9740EEB21CF90195004384FC /* Debug.xcconfig */, | ||
126 | + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, | ||
127 | + 9740EEB31CF90195004384FC /* Generated.xcconfig */, | ||
128 | + ); | ||
129 | + name = Flutter; | ||
130 | + sourceTree = "<group>"; | ||
131 | + }; | ||
132 | + 97C146E51CF9000F007C117D = { | ||
133 | + isa = PBXGroup; | ||
134 | + children = ( | ||
135 | + 9740EEB11CF90186004384FC /* Flutter */, | ||
136 | + 97C146F01CF9000F007C117D /* Runner */, | ||
137 | + 97C146EF1CF9000F007C117D /* Products */, | ||
138 | + 331C8082294A63A400263BE5 /* RunnerTests */, | ||
139 | + 936DC10DBAFF5951E82260FA /* Pods */, | ||
140 | + 5F8A6DB13075A4D449F6A685 /* Frameworks */, | ||
141 | + ); | ||
142 | + sourceTree = "<group>"; | ||
143 | + }; | ||
144 | + 97C146EF1CF9000F007C117D /* Products */ = { | ||
145 | + isa = PBXGroup; | ||
146 | + children = ( | ||
147 | + 97C146EE1CF9000F007C117D /* Runner.app */, | ||
148 | + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, | ||
149 | + ); | ||
150 | + name = Products; | ||
151 | + sourceTree = "<group>"; | ||
152 | + }; | ||
153 | + 97C146F01CF9000F007C117D /* Runner */ = { | ||
154 | + isa = PBXGroup; | ||
155 | + children = ( | ||
156 | + 97C146FA1CF9000F007C117D /* Main.storyboard */, | ||
157 | + 97C146FD1CF9000F007C117D /* Assets.xcassets */, | ||
158 | + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, | ||
159 | + 97C147021CF9000F007C117D /* Info.plist */, | ||
160 | + 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, | ||
161 | + 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, | ||
162 | + 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, | ||
163 | + 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, | ||
164 | + ); | ||
165 | + path = Runner; | ||
166 | + sourceTree = "<group>"; | ||
167 | + }; | ||
168 | +/* End PBXGroup section */ | ||
169 | + | ||
170 | +/* Begin PBXNativeTarget section */ | ||
171 | + 331C8080294A63A400263BE5 /* RunnerTests */ = { | ||
172 | + isa = PBXNativeTarget; | ||
173 | + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; | ||
174 | + buildPhases = ( | ||
175 | + E48DE49AC08A847B9639DEEA /* [CP] Check Pods Manifest.lock */, | ||
176 | + 331C807D294A63A400263BE5 /* Sources */, | ||
177 | + 331C807F294A63A400263BE5 /* Resources */, | ||
178 | + 93DAF74BCB585A23752E0704 /* Frameworks */, | ||
179 | + ); | ||
180 | + buildRules = ( | ||
181 | + ); | ||
182 | + dependencies = ( | ||
183 | + 331C8086294A63A400263BE5 /* PBXTargetDependency */, | ||
184 | + ); | ||
185 | + name = RunnerTests; | ||
186 | + productName = RunnerTests; | ||
187 | + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; | ||
188 | + productType = "com.apple.product-type.bundle.unit-test"; | ||
189 | + }; | ||
190 | + 97C146ED1CF9000F007C117D /* Runner */ = { | ||
191 | + isa = PBXNativeTarget; | ||
192 | + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; | ||
193 | + buildPhases = ( | ||
194 | + 34BC5E1D1DEA8AEF16370400 /* [CP] Check Pods Manifest.lock */, | ||
195 | + 9740EEB61CF901F6004384FC /* Run Script */, | ||
196 | + 97C146EA1CF9000F007C117D /* Sources */, | ||
197 | + 97C146EB1CF9000F007C117D /* Frameworks */, | ||
198 | + 97C146EC1CF9000F007C117D /* Resources */, | ||
199 | + 9705A1C41CF9048500538489 /* Embed Frameworks */, | ||
200 | + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, | ||
201 | + DE97054EB3448BA88001379F /* [CP] Embed Pods Frameworks */, | ||
202 | + ); | ||
203 | + buildRules = ( | ||
204 | + ); | ||
205 | + dependencies = ( | ||
206 | + ); | ||
207 | + name = Runner; | ||
208 | + productName = Runner; | ||
209 | + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; | ||
210 | + productType = "com.apple.product-type.application"; | ||
211 | + }; | ||
212 | +/* End PBXNativeTarget section */ | ||
213 | + | ||
214 | +/* Begin PBXProject section */ | ||
215 | + 97C146E61CF9000F007C117D /* Project object */ = { | ||
216 | + isa = PBXProject; | ||
217 | + attributes = { | ||
218 | + BuildIndependentTargetsInParallel = YES; | ||
219 | + LastUpgradeCheck = 1430; | ||
220 | + ORGANIZATIONNAME = ""; | ||
221 | + TargetAttributes = { | ||
222 | + 331C8080294A63A400263BE5 = { | ||
223 | + CreatedOnToolsVersion = 14.0; | ||
224 | + TestTargetID = 97C146ED1CF9000F007C117D; | ||
225 | + }; | ||
226 | + 97C146ED1CF9000F007C117D = { | ||
227 | + CreatedOnToolsVersion = 7.3.1; | ||
228 | + LastSwiftMigration = 1100; | ||
229 | + }; | ||
230 | + }; | ||
231 | + }; | ||
232 | + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; | ||
233 | + compatibilityVersion = "Xcode 9.3"; | ||
234 | + developmentRegion = en; | ||
235 | + hasScannedForEncodings = 0; | ||
236 | + knownRegions = ( | ||
237 | + en, | ||
238 | + Base, | ||
239 | + ); | ||
240 | + mainGroup = 97C146E51CF9000F007C117D; | ||
241 | + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; | ||
242 | + projectDirPath = ""; | ||
243 | + projectRoot = ""; | ||
244 | + targets = ( | ||
245 | + 97C146ED1CF9000F007C117D /* Runner */, | ||
246 | + 331C8080294A63A400263BE5 /* RunnerTests */, | ||
247 | + ); | ||
248 | + }; | ||
249 | +/* End PBXProject section */ | ||
250 | + | ||
251 | +/* Begin PBXResourcesBuildPhase section */ | ||
252 | + 331C807F294A63A400263BE5 /* Resources */ = { | ||
253 | + isa = PBXResourcesBuildPhase; | ||
254 | + buildActionMask = 2147483647; | ||
255 | + files = ( | ||
256 | + ); | ||
257 | + runOnlyForDeploymentPostprocessing = 0; | ||
258 | + }; | ||
259 | + 97C146EC1CF9000F007C117D /* Resources */ = { | ||
260 | + isa = PBXResourcesBuildPhase; | ||
261 | + buildActionMask = 2147483647; | ||
262 | + files = ( | ||
263 | + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, | ||
264 | + 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, | ||
265 | + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, | ||
266 | + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, | ||
267 | + ); | ||
268 | + runOnlyForDeploymentPostprocessing = 0; | ||
269 | + }; | ||
270 | +/* End PBXResourcesBuildPhase section */ | ||
271 | + | ||
272 | +/* Begin PBXShellScriptBuildPhase section */ | ||
273 | + 34BC5E1D1DEA8AEF16370400 /* [CP] Check Pods Manifest.lock */ = { | ||
274 | + isa = PBXShellScriptBuildPhase; | ||
275 | + buildActionMask = 2147483647; | ||
276 | + files = ( | ||
277 | + ); | ||
278 | + inputFileListPaths = ( | ||
279 | + ); | ||
280 | + inputPaths = ( | ||
281 | + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", | ||
282 | + "${PODS_ROOT}/Manifest.lock", | ||
283 | + ); | ||
284 | + name = "[CP] Check Pods Manifest.lock"; | ||
285 | + outputFileListPaths = ( | ||
286 | + ); | ||
287 | + outputPaths = ( | ||
288 | + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", | ||
289 | + ); | ||
290 | + runOnlyForDeploymentPostprocessing = 0; | ||
291 | + shellPath = /bin/sh; | ||
292 | + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | ||
293 | + showEnvVarsInLog = 0; | ||
294 | + }; | ||
295 | + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { | ||
296 | + isa = PBXShellScriptBuildPhase; | ||
297 | + alwaysOutOfDate = 1; | ||
298 | + buildActionMask = 2147483647; | ||
299 | + files = ( | ||
300 | + ); | ||
301 | + inputPaths = ( | ||
302 | + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", | ||
303 | + ); | ||
304 | + name = "Thin Binary"; | ||
305 | + outputPaths = ( | ||
306 | + ); | ||
307 | + runOnlyForDeploymentPostprocessing = 0; | ||
308 | + shellPath = /bin/sh; | ||
309 | + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; | ||
310 | + }; | ||
311 | + 9740EEB61CF901F6004384FC /* Run Script */ = { | ||
312 | + isa = PBXShellScriptBuildPhase; | ||
313 | + alwaysOutOfDate = 1; | ||
314 | + buildActionMask = 2147483647; | ||
315 | + files = ( | ||
316 | + ); | ||
317 | + inputPaths = ( | ||
318 | + ); | ||
319 | + name = "Run Script"; | ||
320 | + outputPaths = ( | ||
321 | + ); | ||
322 | + runOnlyForDeploymentPostprocessing = 0; | ||
323 | + shellPath = /bin/sh; | ||
324 | + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; | ||
325 | + }; | ||
326 | + DE97054EB3448BA88001379F /* [CP] Embed Pods Frameworks */ = { | ||
327 | + isa = PBXShellScriptBuildPhase; | ||
328 | + buildActionMask = 2147483647; | ||
329 | + files = ( | ||
330 | + ); | ||
331 | + inputFileListPaths = ( | ||
332 | + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", | ||
333 | + ); | ||
334 | + name = "[CP] Embed Pods Frameworks"; | ||
335 | + outputFileListPaths = ( | ||
336 | + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", | ||
337 | + ); | ||
338 | + runOnlyForDeploymentPostprocessing = 0; | ||
339 | + shellPath = /bin/sh; | ||
340 | + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | ||
341 | + showEnvVarsInLog = 0; | ||
342 | + }; | ||
343 | + E48DE49AC08A847B9639DEEA /* [CP] Check Pods Manifest.lock */ = { | ||
344 | + isa = PBXShellScriptBuildPhase; | ||
345 | + buildActionMask = 2147483647; | ||
346 | + files = ( | ||
347 | + ); | ||
348 | + inputFileListPaths = ( | ||
349 | + ); | ||
350 | + inputPaths = ( | ||
351 | + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", | ||
352 | + "${PODS_ROOT}/Manifest.lock", | ||
353 | + ); | ||
354 | + name = "[CP] Check Pods Manifest.lock"; | ||
355 | + outputFileListPaths = ( | ||
356 | + ); | ||
357 | + outputPaths = ( | ||
358 | + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", | ||
359 | + ); | ||
360 | + runOnlyForDeploymentPostprocessing = 0; | ||
361 | + shellPath = /bin/sh; | ||
362 | + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | ||
363 | + showEnvVarsInLog = 0; | ||
364 | + }; | ||
365 | +/* End PBXShellScriptBuildPhase section */ | ||
366 | + | ||
367 | +/* Begin PBXSourcesBuildPhase section */ | ||
368 | + 331C807D294A63A400263BE5 /* Sources */ = { | ||
369 | + isa = PBXSourcesBuildPhase; | ||
370 | + buildActionMask = 2147483647; | ||
371 | + files = ( | ||
372 | + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, | ||
373 | + ); | ||
374 | + runOnlyForDeploymentPostprocessing = 0; | ||
375 | + }; | ||
376 | + 97C146EA1CF9000F007C117D /* Sources */ = { | ||
377 | + isa = PBXSourcesBuildPhase; | ||
378 | + buildActionMask = 2147483647; | ||
379 | + files = ( | ||
380 | + 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, | ||
381 | + 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, | ||
382 | + ); | ||
383 | + runOnlyForDeploymentPostprocessing = 0; | ||
384 | + }; | ||
385 | +/* End PBXSourcesBuildPhase section */ | ||
386 | + | ||
387 | +/* Begin PBXTargetDependency section */ | ||
388 | + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { | ||
389 | + isa = PBXTargetDependency; | ||
390 | + target = 97C146ED1CF9000F007C117D /* Runner */; | ||
391 | + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; | ||
392 | + }; | ||
393 | +/* End PBXTargetDependency section */ | ||
394 | + | ||
395 | +/* Begin PBXVariantGroup section */ | ||
396 | + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { | ||
397 | + isa = PBXVariantGroup; | ||
398 | + children = ( | ||
399 | + 97C146FB1CF9000F007C117D /* Base */, | ||
400 | + ); | ||
401 | + name = Main.storyboard; | ||
402 | + sourceTree = "<group>"; | ||
403 | + }; | ||
404 | + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { | ||
405 | + isa = PBXVariantGroup; | ||
406 | + children = ( | ||
407 | + 97C147001CF9000F007C117D /* Base */, | ||
408 | + ); | ||
409 | + name = LaunchScreen.storyboard; | ||
410 | + sourceTree = "<group>"; | ||
411 | + }; | ||
412 | +/* End PBXVariantGroup section */ | ||
413 | + | ||
414 | +/* Begin XCBuildConfiguration section */ | ||
415 | + 249021D3217E4FDB00AE95B9 /* Profile */ = { | ||
416 | + isa = XCBuildConfiguration; | ||
417 | + buildSettings = { | ||
418 | + ALWAYS_SEARCH_USER_PATHS = NO; | ||
419 | + CLANG_ANALYZER_NONNULL = YES; | ||
420 | + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | ||
421 | + CLANG_CXX_LIBRARY = "libc++"; | ||
422 | + CLANG_ENABLE_MODULES = YES; | ||
423 | + CLANG_ENABLE_OBJC_ARC = YES; | ||
424 | + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||
425 | + CLANG_WARN_BOOL_CONVERSION = YES; | ||
426 | + CLANG_WARN_COMMA = YES; | ||
427 | + CLANG_WARN_CONSTANT_CONVERSION = YES; | ||
428 | + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | ||
429 | + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||
430 | + CLANG_WARN_EMPTY_BODY = YES; | ||
431 | + CLANG_WARN_ENUM_CONVERSION = YES; | ||
432 | + CLANG_WARN_INFINITE_RECURSION = YES; | ||
433 | + CLANG_WARN_INT_CONVERSION = YES; | ||
434 | + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | ||
435 | + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | ||
436 | + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | ||
437 | + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||
438 | + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | ||
439 | + CLANG_WARN_STRICT_PROTOTYPES = YES; | ||
440 | + CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||
441 | + CLANG_WARN_UNREACHABLE_CODE = YES; | ||
442 | + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||
443 | + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | ||
444 | + COPY_PHASE_STRIP = NO; | ||
445 | + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | ||
446 | + ENABLE_NS_ASSERTIONS = NO; | ||
447 | + ENABLE_STRICT_OBJC_MSGSEND = YES; | ||
448 | + GCC_C_LANGUAGE_STANDARD = gnu99; | ||
449 | + GCC_NO_COMMON_BLOCKS = YES; | ||
450 | + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||
451 | + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||
452 | + GCC_WARN_UNDECLARED_SELECTOR = YES; | ||
453 | + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||
454 | + GCC_WARN_UNUSED_FUNCTION = YES; | ||
455 | + GCC_WARN_UNUSED_VARIABLE = YES; | ||
456 | + IPHONEOS_DEPLOYMENT_TARGET = 11.0; | ||
457 | + MTL_ENABLE_DEBUG_INFO = NO; | ||
458 | + SDKROOT = iphoneos; | ||
459 | + SUPPORTED_PLATFORMS = iphoneos; | ||
460 | + TARGETED_DEVICE_FAMILY = "1,2"; | ||
461 | + VALIDATE_PRODUCT = YES; | ||
462 | + }; | ||
463 | + name = Profile; | ||
464 | + }; | ||
465 | + 249021D4217E4FDB00AE95B9 /* Profile */ = { | ||
466 | + isa = XCBuildConfiguration; | ||
467 | + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; | ||
468 | + buildSettings = { | ||
469 | + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||
470 | + CLANG_ENABLE_MODULES = YES; | ||
471 | + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | ||
472 | + DEVELOPMENT_TEAM = FVLFR9QYGC; | ||
473 | + ENABLE_BITCODE = NO; | ||
474 | + INFOPLIST_FILE = Runner/Info.plist; | ||
475 | + LD_RUNPATH_SEARCH_PATHS = ( | ||
476 | + "$(inherited)", | ||
477 | + "@executable_path/Frameworks", | ||
478 | + ); | ||
479 | + PRODUCT_BUNDLE_IDENTIFIER = mobi.iflow.flutter.autotrack.autoTrackExample; | ||
480 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
481 | + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | ||
482 | + SWIFT_VERSION = 5.0; | ||
483 | + VERSIONING_SYSTEM = "apple-generic"; | ||
484 | + }; | ||
485 | + name = Profile; | ||
486 | + }; | ||
487 | + 331C8088294A63A400263BE5 /* Debug */ = { | ||
488 | + isa = XCBuildConfiguration; | ||
489 | + baseConfigurationReference = EB9A8197B759B617DFCCB6F1 /* Pods-RunnerTests.debug.xcconfig */; | ||
490 | + buildSettings = { | ||
491 | + BUNDLE_LOADER = "$(TEST_HOST)"; | ||
492 | + CODE_SIGN_STYLE = Automatic; | ||
493 | + CURRENT_PROJECT_VERSION = 1; | ||
494 | + GENERATE_INFOPLIST_FILE = YES; | ||
495 | + MARKETING_VERSION = 1.0; | ||
496 | + PRODUCT_BUNDLE_IDENTIFIER = mobi.iflow.flutter.autotrack.autoTrackExample.RunnerTests; | ||
497 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
498 | + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; | ||
499 | + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||
500 | + SWIFT_VERSION = 5.0; | ||
501 | + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | ||
502 | + }; | ||
503 | + name = Debug; | ||
504 | + }; | ||
505 | + 331C8089294A63A400263BE5 /* Release */ = { | ||
506 | + isa = XCBuildConfiguration; | ||
507 | + baseConfigurationReference = F782189BA737B0E3C9A3C330 /* Pods-RunnerTests.release.xcconfig */; | ||
508 | + buildSettings = { | ||
509 | + BUNDLE_LOADER = "$(TEST_HOST)"; | ||
510 | + CODE_SIGN_STYLE = Automatic; | ||
511 | + CURRENT_PROJECT_VERSION = 1; | ||
512 | + GENERATE_INFOPLIST_FILE = YES; | ||
513 | + MARKETING_VERSION = 1.0; | ||
514 | + PRODUCT_BUNDLE_IDENTIFIER = mobi.iflow.flutter.autotrack.autoTrackExample.RunnerTests; | ||
515 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
516 | + SWIFT_VERSION = 5.0; | ||
517 | + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | ||
518 | + }; | ||
519 | + name = Release; | ||
520 | + }; | ||
521 | + 331C808A294A63A400263BE5 /* Profile */ = { | ||
522 | + isa = XCBuildConfiguration; | ||
523 | + baseConfigurationReference = 12AD15FE86700BFEC11CC92D /* Pods-RunnerTests.profile.xcconfig */; | ||
524 | + buildSettings = { | ||
525 | + BUNDLE_LOADER = "$(TEST_HOST)"; | ||
526 | + CODE_SIGN_STYLE = Automatic; | ||
527 | + CURRENT_PROJECT_VERSION = 1; | ||
528 | + GENERATE_INFOPLIST_FILE = YES; | ||
529 | + MARKETING_VERSION = 1.0; | ||
530 | + PRODUCT_BUNDLE_IDENTIFIER = mobi.iflow.flutter.autotrack.autoTrackExample.RunnerTests; | ||
531 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
532 | + SWIFT_VERSION = 5.0; | ||
533 | + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | ||
534 | + }; | ||
535 | + name = Profile; | ||
536 | + }; | ||
537 | + 97C147031CF9000F007C117D /* Debug */ = { | ||
538 | + isa = XCBuildConfiguration; | ||
539 | + buildSettings = { | ||
540 | + ALWAYS_SEARCH_USER_PATHS = NO; | ||
541 | + CLANG_ANALYZER_NONNULL = YES; | ||
542 | + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | ||
543 | + CLANG_CXX_LIBRARY = "libc++"; | ||
544 | + CLANG_ENABLE_MODULES = YES; | ||
545 | + CLANG_ENABLE_OBJC_ARC = YES; | ||
546 | + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||
547 | + CLANG_WARN_BOOL_CONVERSION = YES; | ||
548 | + CLANG_WARN_COMMA = YES; | ||
549 | + CLANG_WARN_CONSTANT_CONVERSION = YES; | ||
550 | + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | ||
551 | + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||
552 | + CLANG_WARN_EMPTY_BODY = YES; | ||
553 | + CLANG_WARN_ENUM_CONVERSION = YES; | ||
554 | + CLANG_WARN_INFINITE_RECURSION = YES; | ||
555 | + CLANG_WARN_INT_CONVERSION = YES; | ||
556 | + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | ||
557 | + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | ||
558 | + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | ||
559 | + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||
560 | + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | ||
561 | + CLANG_WARN_STRICT_PROTOTYPES = YES; | ||
562 | + CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||
563 | + CLANG_WARN_UNREACHABLE_CODE = YES; | ||
564 | + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||
565 | + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | ||
566 | + COPY_PHASE_STRIP = NO; | ||
567 | + DEBUG_INFORMATION_FORMAT = dwarf; | ||
568 | + ENABLE_STRICT_OBJC_MSGSEND = YES; | ||
569 | + ENABLE_TESTABILITY = YES; | ||
570 | + GCC_C_LANGUAGE_STANDARD = gnu99; | ||
571 | + GCC_DYNAMIC_NO_PIC = NO; | ||
572 | + GCC_NO_COMMON_BLOCKS = YES; | ||
573 | + GCC_OPTIMIZATION_LEVEL = 0; | ||
574 | + GCC_PREPROCESSOR_DEFINITIONS = ( | ||
575 | + "DEBUG=1", | ||
576 | + "$(inherited)", | ||
577 | + ); | ||
578 | + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||
579 | + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||
580 | + GCC_WARN_UNDECLARED_SELECTOR = YES; | ||
581 | + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||
582 | + GCC_WARN_UNUSED_FUNCTION = YES; | ||
583 | + GCC_WARN_UNUSED_VARIABLE = YES; | ||
584 | + IPHONEOS_DEPLOYMENT_TARGET = 11.0; | ||
585 | + MTL_ENABLE_DEBUG_INFO = YES; | ||
586 | + ONLY_ACTIVE_ARCH = YES; | ||
587 | + SDKROOT = iphoneos; | ||
588 | + TARGETED_DEVICE_FAMILY = "1,2"; | ||
589 | + }; | ||
590 | + name = Debug; | ||
591 | + }; | ||
592 | + 97C147041CF9000F007C117D /* Release */ = { | ||
593 | + isa = XCBuildConfiguration; | ||
594 | + buildSettings = { | ||
595 | + ALWAYS_SEARCH_USER_PATHS = NO; | ||
596 | + CLANG_ANALYZER_NONNULL = YES; | ||
597 | + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; | ||
598 | + CLANG_CXX_LIBRARY = "libc++"; | ||
599 | + CLANG_ENABLE_MODULES = YES; | ||
600 | + CLANG_ENABLE_OBJC_ARC = YES; | ||
601 | + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; | ||
602 | + CLANG_WARN_BOOL_CONVERSION = YES; | ||
603 | + CLANG_WARN_COMMA = YES; | ||
604 | + CLANG_WARN_CONSTANT_CONVERSION = YES; | ||
605 | + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; | ||
606 | + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; | ||
607 | + CLANG_WARN_EMPTY_BODY = YES; | ||
608 | + CLANG_WARN_ENUM_CONVERSION = YES; | ||
609 | + CLANG_WARN_INFINITE_RECURSION = YES; | ||
610 | + CLANG_WARN_INT_CONVERSION = YES; | ||
611 | + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; | ||
612 | + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; | ||
613 | + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; | ||
614 | + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; | ||
615 | + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; | ||
616 | + CLANG_WARN_STRICT_PROTOTYPES = YES; | ||
617 | + CLANG_WARN_SUSPICIOUS_MOVE = YES; | ||
618 | + CLANG_WARN_UNREACHABLE_CODE = YES; | ||
619 | + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; | ||
620 | + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | ||
621 | + COPY_PHASE_STRIP = NO; | ||
622 | + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; | ||
623 | + ENABLE_NS_ASSERTIONS = NO; | ||
624 | + ENABLE_STRICT_OBJC_MSGSEND = YES; | ||
625 | + GCC_C_LANGUAGE_STANDARD = gnu99; | ||
626 | + GCC_NO_COMMON_BLOCKS = YES; | ||
627 | + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; | ||
628 | + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; | ||
629 | + GCC_WARN_UNDECLARED_SELECTOR = YES; | ||
630 | + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; | ||
631 | + GCC_WARN_UNUSED_FUNCTION = YES; | ||
632 | + GCC_WARN_UNUSED_VARIABLE = YES; | ||
633 | + IPHONEOS_DEPLOYMENT_TARGET = 11.0; | ||
634 | + MTL_ENABLE_DEBUG_INFO = NO; | ||
635 | + SDKROOT = iphoneos; | ||
636 | + SUPPORTED_PLATFORMS = iphoneos; | ||
637 | + SWIFT_COMPILATION_MODE = wholemodule; | ||
638 | + SWIFT_OPTIMIZATION_LEVEL = "-O"; | ||
639 | + TARGETED_DEVICE_FAMILY = "1,2"; | ||
640 | + VALIDATE_PRODUCT = YES; | ||
641 | + }; | ||
642 | + name = Release; | ||
643 | + }; | ||
644 | + 97C147061CF9000F007C117D /* Debug */ = { | ||
645 | + isa = XCBuildConfiguration; | ||
646 | + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; | ||
647 | + buildSettings = { | ||
648 | + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||
649 | + CLANG_ENABLE_MODULES = YES; | ||
650 | + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | ||
651 | + DEVELOPMENT_TEAM = FVLFR9QYGC; | ||
652 | + ENABLE_BITCODE = NO; | ||
653 | + INFOPLIST_FILE = Runner/Info.plist; | ||
654 | + LD_RUNPATH_SEARCH_PATHS = ( | ||
655 | + "$(inherited)", | ||
656 | + "@executable_path/Frameworks", | ||
657 | + ); | ||
658 | + PRODUCT_BUNDLE_IDENTIFIER = mobi.iflow.flutter.autotrack.autoTrackExample; | ||
659 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
660 | + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | ||
661 | + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||
662 | + SWIFT_VERSION = 5.0; | ||
663 | + VERSIONING_SYSTEM = "apple-generic"; | ||
664 | + }; | ||
665 | + name = Debug; | ||
666 | + }; | ||
667 | + 97C147071CF9000F007C117D /* Release */ = { | ||
668 | + isa = XCBuildConfiguration; | ||
669 | + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; | ||
670 | + buildSettings = { | ||
671 | + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; | ||
672 | + CLANG_ENABLE_MODULES = YES; | ||
673 | + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; | ||
674 | + DEVELOPMENT_TEAM = FVLFR9QYGC; | ||
675 | + ENABLE_BITCODE = NO; | ||
676 | + INFOPLIST_FILE = Runner/Info.plist; | ||
677 | + LD_RUNPATH_SEARCH_PATHS = ( | ||
678 | + "$(inherited)", | ||
679 | + "@executable_path/Frameworks", | ||
680 | + ); | ||
681 | + PRODUCT_BUNDLE_IDENTIFIER = mobi.iflow.flutter.autotrack.autoTrackExample; | ||
682 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
683 | + SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | ||
684 | + SWIFT_VERSION = 5.0; | ||
685 | + VERSIONING_SYSTEM = "apple-generic"; | ||
686 | + }; | ||
687 | + name = Release; | ||
688 | + }; | ||
689 | +/* End XCBuildConfiguration section */ | ||
690 | + | ||
691 | +/* Begin XCConfigurationList section */ | ||
692 | + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { | ||
693 | + isa = XCConfigurationList; | ||
694 | + buildConfigurations = ( | ||
695 | + 331C8088294A63A400263BE5 /* Debug */, | ||
696 | + 331C8089294A63A400263BE5 /* Release */, | ||
697 | + 331C808A294A63A400263BE5 /* Profile */, | ||
698 | + ); | ||
699 | + defaultConfigurationIsVisible = 0; | ||
700 | + defaultConfigurationName = Release; | ||
701 | + }; | ||
702 | + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { | ||
703 | + isa = XCConfigurationList; | ||
704 | + buildConfigurations = ( | ||
705 | + 97C147031CF9000F007C117D /* Debug */, | ||
706 | + 97C147041CF9000F007C117D /* Release */, | ||
707 | + 249021D3217E4FDB00AE95B9 /* Profile */, | ||
708 | + ); | ||
709 | + defaultConfigurationIsVisible = 0; | ||
710 | + defaultConfigurationName = Release; | ||
711 | + }; | ||
712 | + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { | ||
713 | + isa = XCConfigurationList; | ||
714 | + buildConfigurations = ( | ||
715 | + 97C147061CF9000F007C117D /* Debug */, | ||
716 | + 97C147071CF9000F007C117D /* Release */, | ||
717 | + 249021D4217E4FDB00AE95B9 /* Profile */, | ||
718 | + ); | ||
719 | + defaultConfigurationIsVisible = 0; | ||
720 | + defaultConfigurationName = Release; | ||
721 | + }; | ||
722 | +/* End XCConfigurationList section */ | ||
723 | + }; | ||
724 | + rootObject = 97C146E61CF9000F007C117D /* Project object */; | ||
725 | +} |
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<Scheme | ||
3 | + LastUpgradeVersion = "1430" | ||
4 | + version = "1.3"> | ||
5 | + <BuildAction | ||
6 | + parallelizeBuildables = "YES" | ||
7 | + buildImplicitDependencies = "YES"> | ||
8 | + <BuildActionEntries> | ||
9 | + <BuildActionEntry | ||
10 | + buildForTesting = "YES" | ||
11 | + buildForRunning = "YES" | ||
12 | + buildForProfiling = "YES" | ||
13 | + buildForArchiving = "YES" | ||
14 | + buildForAnalyzing = "YES"> | ||
15 | + <BuildableReference | ||
16 | + BuildableIdentifier = "primary" | ||
17 | + BlueprintIdentifier = "97C146ED1CF9000F007C117D" | ||
18 | + BuildableName = "Runner.app" | ||
19 | + BlueprintName = "Runner" | ||
20 | + ReferencedContainer = "container:Runner.xcodeproj"> | ||
21 | + </BuildableReference> | ||
22 | + </BuildActionEntry> | ||
23 | + </BuildActionEntries> | ||
24 | + </BuildAction> | ||
25 | + <TestAction | ||
26 | + buildConfiguration = "Debug" | ||
27 | + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||
28 | + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||
29 | + shouldUseLaunchSchemeArgsEnv = "YES"> | ||
30 | + <MacroExpansion> | ||
31 | + <BuildableReference | ||
32 | + BuildableIdentifier = "primary" | ||
33 | + BlueprintIdentifier = "97C146ED1CF9000F007C117D" | ||
34 | + BuildableName = "Runner.app" | ||
35 | + BlueprintName = "Runner" | ||
36 | + ReferencedContainer = "container:Runner.xcodeproj"> | ||
37 | + </BuildableReference> | ||
38 | + </MacroExpansion> | ||
39 | + <Testables> | ||
40 | + <TestableReference | ||
41 | + skipped = "NO" | ||
42 | + parallelizable = "YES"> | ||
43 | + <BuildableReference | ||
44 | + BuildableIdentifier = "primary" | ||
45 | + BlueprintIdentifier = "331C8080294A63A400263BE5" | ||
46 | + BuildableName = "RunnerTests.xctest" | ||
47 | + BlueprintName = "RunnerTests" | ||
48 | + ReferencedContainer = "container:Runner.xcodeproj"> | ||
49 | + </BuildableReference> | ||
50 | + </TestableReference> | ||
51 | + </Testables> | ||
52 | + </TestAction> | ||
53 | + <LaunchAction | ||
54 | + buildConfiguration = "Debug" | ||
55 | + selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" | ||
56 | + selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" | ||
57 | + launchStyle = "0" | ||
58 | + useCustomWorkingDirectory = "NO" | ||
59 | + ignoresPersistentStateOnLaunch = "NO" | ||
60 | + debugDocumentVersioning = "YES" | ||
61 | + debugServiceExtension = "internal" | ||
62 | + allowLocationSimulation = "YES"> | ||
63 | + <BuildableProductRunnable | ||
64 | + runnableDebuggingMode = "0"> | ||
65 | + <BuildableReference | ||
66 | + BuildableIdentifier = "primary" | ||
67 | + BlueprintIdentifier = "97C146ED1CF9000F007C117D" | ||
68 | + BuildableName = "Runner.app" | ||
69 | + BlueprintName = "Runner" | ||
70 | + ReferencedContainer = "container:Runner.xcodeproj"> | ||
71 | + </BuildableReference> | ||
72 | + </BuildableProductRunnable> | ||
73 | + </LaunchAction> | ||
74 | + <ProfileAction | ||
75 | + buildConfiguration = "Profile" | ||
76 | + shouldUseLaunchSchemeArgsEnv = "YES" | ||
77 | + savedToolIdentifier = "" | ||
78 | + useCustomWorkingDirectory = "NO" | ||
79 | + debugDocumentVersioning = "YES"> | ||
80 | + <BuildableProductRunnable | ||
81 | + runnableDebuggingMode = "0"> | ||
82 | + <BuildableReference | ||
83 | + BuildableIdentifier = "primary" | ||
84 | + BlueprintIdentifier = "97C146ED1CF9000F007C117D" | ||
85 | + BuildableName = "Runner.app" | ||
86 | + BlueprintName = "Runner" | ||
87 | + ReferencedContainer = "container:Runner.xcodeproj"> | ||
88 | + </BuildableReference> | ||
89 | + </BuildableProductRunnable> | ||
90 | + </ProfileAction> | ||
91 | + <AnalyzeAction | ||
92 | + buildConfiguration = "Debug"> | ||
93 | + </AnalyzeAction> | ||
94 | + <ArchiveAction | ||
95 | + buildConfiguration = "Release" | ||
96 | + revealArchiveInOrganizer = "YES"> | ||
97 | + </ArchiveAction> | ||
98 | +</Scheme> |
example/ios/Runner/AppDelegate.swift
0 → 100644
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 | +} |
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 | +} |

295 Bytes

406 Bytes

450 Bytes

282 Bytes

462 Bytes

704 Bytes

406 Bytes

586 Bytes

862 Bytes

862 Bytes

1.63 KB

762 Bytes
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 | +} |

68 Bytes

68 Bytes

68 Bytes
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. |
1 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM"> | ||
3 | + <dependencies> | ||
4 | + <deployment identifier="iOS"/> | ||
5 | + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/> | ||
6 | + </dependencies> | ||
7 | + <scenes> | ||
8 | + <!--View Controller--> | ||
9 | + <scene sceneID="EHf-IW-A2E"> | ||
10 | + <objects> | ||
11 | + <viewController id="01J-lp-oVM" sceneMemberID="viewController"> | ||
12 | + <layoutGuides> | ||
13 | + <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/> | ||
14 | + <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/> | ||
15 | + </layoutGuides> | ||
16 | + <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> | ||
17 | + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||
18 | + <subviews> | ||
19 | + <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4"> | ||
20 | + </imageView> | ||
21 | + </subviews> | ||
22 | + <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> | ||
23 | + <constraints> | ||
24 | + <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/> | ||
25 | + <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/> | ||
26 | + </constraints> | ||
27 | + </view> | ||
28 | + </viewController> | ||
29 | + <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/> | ||
30 | + </objects> | ||
31 | + <point key="canvasLocation" x="53" y="375"/> | ||
32 | + </scene> | ||
33 | + </scenes> | ||
34 | + <resources> | ||
35 | + <image name="LaunchImage" width="168" height="185"/> | ||
36 | + </resources> | ||
37 | +</document> |
1 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||
2 | +<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r"> | ||
3 | + <dependencies> | ||
4 | + <deployment identifier="iOS"/> | ||
5 | + <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/> | ||
6 | + </dependencies> | ||
7 | + <scenes> | ||
8 | + <!--Flutter View Controller--> | ||
9 | + <scene sceneID="tne-QT-ifu"> | ||
10 | + <objects> | ||
11 | + <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController"> | ||
12 | + <layoutGuides> | ||
13 | + <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/> | ||
14 | + <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/> | ||
15 | + </layoutGuides> | ||
16 | + <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC"> | ||
17 | + <rect key="frame" x="0.0" y="0.0" width="600" height="600"/> | ||
18 | + <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> | ||
19 | + <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/> | ||
20 | + </view> | ||
21 | + </viewController> | ||
22 | + <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/> | ||
23 | + </objects> | ||
24 | + </scene> | ||
25 | + </scenes> | ||
26 | +</document> |
example/ios/Runner/Info.plist
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
3 | +<plist version="1.0"> | ||
4 | +<dict> | ||
5 | + <key>CFBundleDevelopmentRegion</key> | ||
6 | + <string>$(DEVELOPMENT_LANGUAGE)</string> | ||
7 | + <key>CFBundleDisplayName</key> | ||
8 | + <string>Auto Track</string> | ||
9 | + <key>CFBundleExecutable</key> | ||
10 | + <string>$(EXECUTABLE_NAME)</string> | ||
11 | + <key>CFBundleIdentifier</key> | ||
12 | + <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
13 | + <key>CFBundleInfoDictionaryVersion</key> | ||
14 | + <string>6.0</string> | ||
15 | + <key>CFBundleName</key> | ||
16 | + <string>auto_track_example</string> | ||
17 | + <key>CFBundlePackageType</key> | ||
18 | + <string>APPL</string> | ||
19 | + <key>CFBundleShortVersionString</key> | ||
20 | + <string>$(FLUTTER_BUILD_NAME)</string> | ||
21 | + <key>CFBundleSignature</key> | ||
22 | + <string>????</string> | ||
23 | + <key>CFBundleVersion</key> | ||
24 | + <string>$(FLUTTER_BUILD_NUMBER)</string> | ||
25 | + <key>LSRequiresIPhoneOS</key> | ||
26 | + <true/> | ||
27 | + <key>UILaunchStoryboardName</key> | ||
28 | + <string>LaunchScreen</string> | ||
29 | + <key>UIMainStoryboardFile</key> | ||
30 | + <string>Main</string> | ||
31 | + <key>UISupportedInterfaceOrientations</key> | ||
32 | + <array> | ||
33 | + <string>UIInterfaceOrientationPortrait</string> | ||
34 | + <string>UIInterfaceOrientationLandscapeLeft</string> | ||
35 | + <string>UIInterfaceOrientationLandscapeRight</string> | ||
36 | + </array> | ||
37 | + <key>UISupportedInterfaceOrientations~ipad</key> | ||
38 | + <array> | ||
39 | + <string>UIInterfaceOrientationPortrait</string> | ||
40 | + <string>UIInterfaceOrientationPortraitUpsideDown</string> | ||
41 | + <string>UIInterfaceOrientationLandscapeLeft</string> | ||
42 | + <string>UIInterfaceOrientationLandscapeRight</string> | ||
43 | + </array> | ||
44 | + <key>CADisableMinimumFrameDurationOnPhone</key> | ||
45 | + <true/> | ||
46 | + <key>UIApplicationSupportsIndirectInputEvents</key> | ||
47 | + <true/> | ||
48 | +</dict> | ||
49 | +</plist> |
example/ios/Runner/Runner-Bridging-Header.h
0 → 100644
1 | +#import "GeneratedPluginRegistrant.h" |
example/ios/RunnerTests/RunnerTests.swift
0 → 100644
1 | +import Flutter | ||
2 | +import UIKit | ||
3 | +import XCTest | ||
4 | + | ||
5 | +@testable import auto_track | ||
6 | + | ||
7 | +// This demonstrates a simple unit test of the Swift portion of this plugin's implementation. | ||
8 | +// | ||
9 | +// See https://developer.apple.com/documentation/xctest for more information about using XCTest. | ||
10 | + | ||
11 | +class RunnerTests: XCTestCase { | ||
12 | + | ||
13 | + func testGetPlatformVersion() { | ||
14 | + let plugin = AutoTrackPlugin() | ||
15 | + | ||
16 | + let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) | ||
17 | + | ||
18 | + let resultExpectation = expectation(description: "result block must be called.") | ||
19 | + plugin.handle(call) { result in | ||
20 | + XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion) | ||
21 | + resultExpectation.fulfill() | ||
22 | + } | ||
23 | + waitForExpectations(timeout: 1) | ||
24 | + } | ||
25 | + | ||
26 | +} |
example/lib/home.dart
0 → 100644
1 | +import 'package:auto_track_example/page_a.dart'; | ||
2 | +import 'package:flutter/material.dart'; | ||
3 | + | ||
4 | +class Home extends StatelessWidget { | ||
5 | + const Home({super.key}); | ||
6 | + | ||
7 | + @override | ||
8 | + Widget build(BuildContext context) { | ||
9 | + return ListView( | ||
10 | + children: [ | ||
11 | + ListTile( | ||
12 | + title: const Text('page a'), | ||
13 | + onTap: () { | ||
14 | + Navigator.push(context, MaterialPageRoute(builder: (context) { | ||
15 | + return const PageA(); | ||
16 | + })); | ||
17 | + }, | ||
18 | + ) | ||
19 | + ], | ||
20 | + ); | ||
21 | + } | ||
22 | +} |
example/lib/main.dart
0 → 100644
1 | +import 'package:auto_track/auto_track.dart'; | ||
2 | +import 'package:auto_track/auto_track/config/config.dart'; | ||
3 | +import 'package:auto_track/auto_track/index.dart'; | ||
4 | +import 'package:auto_track_example/home.dart'; | ||
5 | +import 'package:auto_track_example/page_a.dart'; | ||
6 | +import 'package:flutter/material.dart'; | ||
7 | + | ||
8 | + | ||
9 | +void main() { | ||
10 | + runApp(const MyApp()); | ||
11 | +} | ||
12 | + | ||
13 | +class MyApp extends StatefulWidget { | ||
14 | + const MyApp({super.key}); | ||
15 | + | ||
16 | + @override | ||
17 | + State<MyApp> createState() => _MyAppState(); | ||
18 | +} | ||
19 | + | ||
20 | +class _MyAppState extends State<MyApp> { | ||
21 | + @override | ||
22 | + void initState() { | ||
23 | + AutoTrack() | ||
24 | + .config(AutoTrackConfig( | ||
25 | + pageConfigs: [ | ||
26 | + AutoTrackPageConfig<PageA>( | ||
27 | + pageID: 'page_a', | ||
28 | + ), | ||
29 | + | ||
30 | + ])) | ||
31 | + .enable() | ||
32 | + .enablePageLeave() | ||
33 | + .enablePageView() | ||
34 | + .enableClick() | ||
35 | + .enableLog(); | ||
36 | + | ||
37 | + super.initState(); | ||
38 | + } | ||
39 | + | ||
40 | + @override | ||
41 | + Widget build(BuildContext context) { | ||
42 | + return MaterialApp( | ||
43 | + home: Scaffold( | ||
44 | + appBar: AppBar( | ||
45 | + title: const Text('auto track example app'), | ||
46 | + ), | ||
47 | + body: const Center( | ||
48 | + child: Home(), | ||
49 | + ), | ||
50 | + ), | ||
51 | + navigatorObservers: AutoTrackNavigationObserver.wrap([]), | ||
52 | + ); | ||
53 | + } | ||
54 | +} |
example/lib/page_a.dart
0 → 100644
1 | +import 'package:flutter/cupertino.dart'; | ||
2 | + | ||
3 | +class PageA extends StatelessWidget { | ||
4 | + const PageA({super.key}); | ||
5 | + | ||
6 | + @override | ||
7 | + Widget build(BuildContext context) { | ||
8 | + return GestureDetector( | ||
9 | + onTap: () { | ||
10 | + print("tap page a"); | ||
11 | + }, | ||
12 | + child: const Text('page a'), | ||
13 | + ); | ||
14 | + } | ||
15 | +} |
example/pubspec.lock
0 → 100644
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 | + auto_track: | ||
13 | + dependency: "direct main" | ||
14 | + description: | ||
15 | + path: ".." | ||
16 | + relative: true | ||
17 | + source: path | ||
18 | + version: "0.0.1" | ||
19 | + boolean_selector: | ||
20 | + dependency: transitive | ||
21 | + description: | ||
22 | + name: boolean_selector | ||
23 | + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" | ||
24 | + url: "https://pub.dev" | ||
25 | + source: hosted | ||
26 | + version: "2.1.1" | ||
27 | + characters: | ||
28 | + dependency: transitive | ||
29 | + description: | ||
30 | + name: characters | ||
31 | + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" | ||
32 | + url: "https://pub.dev" | ||
33 | + source: hosted | ||
34 | + version: "1.3.0" | ||
35 | + clock: | ||
36 | + dependency: transitive | ||
37 | + description: | ||
38 | + name: clock | ||
39 | + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf | ||
40 | + url: "https://pub.dev" | ||
41 | + source: hosted | ||
42 | + version: "1.1.1" | ||
43 | + collection: | ||
44 | + dependency: transitive | ||
45 | + description: | ||
46 | + name: collection | ||
47 | + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a | ||
48 | + url: "https://pub.dev" | ||
49 | + source: hosted | ||
50 | + version: "1.18.0" | ||
51 | + crypto: | ||
52 | + dependency: transitive | ||
53 | + description: | ||
54 | + name: crypto | ||
55 | + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab | ||
56 | + url: "https://pub.dev" | ||
57 | + source: hosted | ||
58 | + version: "3.0.3" | ||
59 | + cupertino_icons: | ||
60 | + dependency: "direct main" | ||
61 | + description: | ||
62 | + name: cupertino_icons | ||
63 | + sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d | ||
64 | + url: "https://pub.dev" | ||
65 | + source: hosted | ||
66 | + version: "1.0.6" | ||
67 | + dio: | ||
68 | + dependency: transitive | ||
69 | + description: | ||
70 | + name: dio | ||
71 | + sha256: "49af28382aefc53562459104f64d16b9dfd1e8ef68c862d5af436cc8356ce5a8" | ||
72 | + url: "https://pub.dev" | ||
73 | + source: hosted | ||
74 | + version: "5.4.1" | ||
75 | + fake_async: | ||
76 | + dependency: transitive | ||
77 | + description: | ||
78 | + name: fake_async | ||
79 | + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" | ||
80 | + url: "https://pub.dev" | ||
81 | + source: hosted | ||
82 | + version: "1.3.1" | ||
83 | + file: | ||
84 | + dependency: transitive | ||
85 | + description: | ||
86 | + name: file | ||
87 | + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" | ||
88 | + url: "https://pub.dev" | ||
89 | + source: hosted | ||
90 | + version: "6.1.4" | ||
91 | + fixnum: | ||
92 | + dependency: transitive | ||
93 | + description: | ||
94 | + name: fixnum | ||
95 | + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" | ||
96 | + url: "https://pub.dev" | ||
97 | + source: hosted | ||
98 | + version: "1.1.0" | ||
99 | + flutter: | ||
100 | + dependency: "direct main" | ||
101 | + description: flutter | ||
102 | + source: sdk | ||
103 | + version: "0.0.0" | ||
104 | + flutter_driver: | ||
105 | + dependency: transitive | ||
106 | + description: flutter | ||
107 | + source: sdk | ||
108 | + version: "0.0.0" | ||
109 | + flutter_lints: | ||
110 | + dependency: "direct dev" | ||
111 | + description: | ||
112 | + name: flutter_lints | ||
113 | + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 | ||
114 | + url: "https://pub.dev" | ||
115 | + source: hosted | ||
116 | + version: "2.0.3" | ||
117 | + flutter_test: | ||
118 | + dependency: "direct dev" | ||
119 | + description: flutter | ||
120 | + source: sdk | ||
121 | + version: "0.0.0" | ||
122 | + fuchsia_remote_debug_protocol: | ||
123 | + dependency: transitive | ||
124 | + description: flutter | ||
125 | + source: sdk | ||
126 | + version: "0.0.0" | ||
127 | + http_parser: | ||
128 | + dependency: transitive | ||
129 | + description: | ||
130 | + name: http_parser | ||
131 | + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" | ||
132 | + url: "https://pub.dev" | ||
133 | + source: hosted | ||
134 | + version: "4.0.2" | ||
135 | + integration_test: | ||
136 | + dependency: "direct dev" | ||
137 | + description: flutter | ||
138 | + source: sdk | ||
139 | + version: "0.0.0" | ||
140 | + lints: | ||
141 | + dependency: transitive | ||
142 | + description: | ||
143 | + name: lints | ||
144 | + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" | ||
145 | + url: "https://pub.dev" | ||
146 | + source: hosted | ||
147 | + version: "2.1.1" | ||
148 | + matcher: | ||
149 | + dependency: transitive | ||
150 | + description: | ||
151 | + name: matcher | ||
152 | + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" | ||
153 | + url: "https://pub.dev" | ||
154 | + source: hosted | ||
155 | + version: "0.12.16" | ||
156 | + material_color_utilities: | ||
157 | + dependency: transitive | ||
158 | + description: | ||
159 | + name: material_color_utilities | ||
160 | + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" | ||
161 | + url: "https://pub.dev" | ||
162 | + source: hosted | ||
163 | + version: "0.5.0" | ||
164 | + meta: | ||
165 | + dependency: transitive | ||
166 | + description: | ||
167 | + name: meta | ||
168 | + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e | ||
169 | + url: "https://pub.dev" | ||
170 | + source: hosted | ||
171 | + version: "1.10.0" | ||
172 | + path: | ||
173 | + dependency: transitive | ||
174 | + description: | ||
175 | + name: path | ||
176 | + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" | ||
177 | + url: "https://pub.dev" | ||
178 | + source: hosted | ||
179 | + version: "1.8.3" | ||
180 | + platform: | ||
181 | + dependency: transitive | ||
182 | + description: | ||
183 | + name: platform | ||
184 | + sha256: ae68c7bfcd7383af3629daafb32fb4e8681c7154428da4febcff06200585f102 | ||
185 | + url: "https://pub.dev" | ||
186 | + source: hosted | ||
187 | + version: "3.1.2" | ||
188 | + plugin_platform_interface: | ||
189 | + dependency: transitive | ||
190 | + description: | ||
191 | + name: plugin_platform_interface | ||
192 | + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" | ||
193 | + url: "https://pub.dev" | ||
194 | + source: hosted | ||
195 | + version: "2.1.8" | ||
196 | + process: | ||
197 | + dependency: transitive | ||
198 | + description: | ||
199 | + name: process | ||
200 | + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" | ||
201 | + url: "https://pub.dev" | ||
202 | + source: hosted | ||
203 | + version: "4.2.4" | ||
204 | + sky_engine: | ||
205 | + dependency: transitive | ||
206 | + description: flutter | ||
207 | + source: sdk | ||
208 | + version: "0.0.99" | ||
209 | + source_span: | ||
210 | + dependency: transitive | ||
211 | + description: | ||
212 | + name: source_span | ||
213 | + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" | ||
214 | + url: "https://pub.dev" | ||
215 | + source: hosted | ||
216 | + version: "1.10.0" | ||
217 | + sprintf: | ||
218 | + dependency: transitive | ||
219 | + description: | ||
220 | + name: sprintf | ||
221 | + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" | ||
222 | + url: "https://pub.dev" | ||
223 | + source: hosted | ||
224 | + version: "7.0.0" | ||
225 | + stack_trace: | ||
226 | + dependency: transitive | ||
227 | + description: | ||
228 | + name: stack_trace | ||
229 | + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" | ||
230 | + url: "https://pub.dev" | ||
231 | + source: hosted | ||
232 | + version: "1.11.1" | ||
233 | + stream_channel: | ||
234 | + dependency: transitive | ||
235 | + description: | ||
236 | + name: stream_channel | ||
237 | + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 | ||
238 | + url: "https://pub.dev" | ||
239 | + source: hosted | ||
240 | + version: "2.1.2" | ||
241 | + string_scanner: | ||
242 | + dependency: transitive | ||
243 | + description: | ||
244 | + name: string_scanner | ||
245 | + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" | ||
246 | + url: "https://pub.dev" | ||
247 | + source: hosted | ||
248 | + version: "1.2.0" | ||
249 | + sync_http: | ||
250 | + dependency: transitive | ||
251 | + description: | ||
252 | + name: sync_http | ||
253 | + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" | ||
254 | + url: "https://pub.dev" | ||
255 | + source: hosted | ||
256 | + version: "0.3.1" | ||
257 | + term_glyph: | ||
258 | + dependency: transitive | ||
259 | + description: | ||
260 | + name: term_glyph | ||
261 | + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 | ||
262 | + url: "https://pub.dev" | ||
263 | + source: hosted | ||
264 | + version: "1.2.1" | ||
265 | + test_api: | ||
266 | + dependency: transitive | ||
267 | + description: | ||
268 | + name: test_api | ||
269 | + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" | ||
270 | + url: "https://pub.dev" | ||
271 | + source: hosted | ||
272 | + version: "0.6.1" | ||
273 | + typed_data: | ||
274 | + dependency: transitive | ||
275 | + description: | ||
276 | + name: typed_data | ||
277 | + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c | ||
278 | + url: "https://pub.dev" | ||
279 | + source: hosted | ||
280 | + version: "1.3.2" | ||
281 | + uuid: | ||
282 | + dependency: transitive | ||
283 | + description: | ||
284 | + name: uuid | ||
285 | + sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 | ||
286 | + url: "https://pub.dev" | ||
287 | + source: hosted | ||
288 | + version: "4.3.3" | ||
289 | + vector_math: | ||
290 | + dependency: transitive | ||
291 | + description: | ||
292 | + name: vector_math | ||
293 | + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" | ||
294 | + url: "https://pub.dev" | ||
295 | + source: hosted | ||
296 | + version: "2.1.4" | ||
297 | + vm_service: | ||
298 | + dependency: transitive | ||
299 | + description: | ||
300 | + name: vm_service | ||
301 | + sha256: c538be99af830f478718b51630ec1b6bee5e74e52c8a802d328d9e71d35d2583 | ||
302 | + url: "https://pub.dev" | ||
303 | + source: hosted | ||
304 | + version: "11.10.0" | ||
305 | + web: | ||
306 | + dependency: transitive | ||
307 | + description: | ||
308 | + name: web | ||
309 | + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 | ||
310 | + url: "https://pub.dev" | ||
311 | + source: hosted | ||
312 | + version: "0.3.0" | ||
313 | + webdriver: | ||
314 | + dependency: transitive | ||
315 | + description: | ||
316 | + name: webdriver | ||
317 | + sha256: "3c923e918918feeb90c4c9fdf1fe39220fa4c0e8e2c0fffaded174498ef86c49" | ||
318 | + url: "https://pub.dev" | ||
319 | + source: hosted | ||
320 | + version: "3.0.2" | ||
321 | +sdks: | ||
322 | + dart: ">=3.2.3 <4.0.0" | ||
323 | + flutter: ">=3.3.0" |
example/pubspec.yaml
0 → 100644
1 | +name: auto_track_example | ||
2 | +description: "Demonstrates how to use the auto_track plugin." | ||
3 | +# The following line prevents the package from being accidentally published to | ||
4 | +# pub.dev using `flutter pub publish`. This is preferred for private packages. | ||
5 | +publish_to: 'none' # Remove this line if you wish to publish to pub.dev | ||
6 | + | ||
7 | +environment: | ||
8 | + sdk: '>=3.2.3 <4.0.0' | ||
9 | + | ||
10 | +# Dependencies specify other packages that your package needs in order to work. | ||
11 | +# To automatically upgrade your package dependencies to the latest versions | ||
12 | +# consider running `flutter pub upgrade --major-versions`. Alternatively, | ||
13 | +# dependencies can be manually updated by changing the version numbers below to | ||
14 | +# the latest version available on pub.dev. To see which dependencies have newer | ||
15 | +# versions available, run `flutter pub outdated`. | ||
16 | +dependencies: | ||
17 | + flutter: | ||
18 | + sdk: flutter | ||
19 | + | ||
20 | + auto_track: | ||
21 | + # When depending on this package from a real application you should use: | ||
22 | + # auto_track: ^x.y.z | ||
23 | + # See https://dart.dev/tools/pub/dependencies#version-constraints | ||
24 | + # The example app is bundled with the plugin so we use a path dependency on | ||
25 | + # the parent directory to use the current plugin's version. | ||
26 | + path: ../ | ||
27 | + | ||
28 | + # The following adds the Cupertino Icons font to your application. | ||
29 | + # Use with the CupertinoIcons class for iOS style icons. | ||
30 | + cupertino_icons: ^1.0.2 | ||
31 | + | ||
32 | +dev_dependencies: | ||
33 | + integration_test: | ||
34 | + sdk: flutter | ||
35 | + flutter_test: | ||
36 | + sdk: flutter | ||
37 | + | ||
38 | + # The "flutter_lints" package below contains a set of recommended lints to | ||
39 | + # encourage good coding practices. The lint set provided by the package is | ||
40 | + # activated in the `analysis_options.yaml` file located at the root of your | ||
41 | + # package. See that file for information about deactivating specific lint | ||
42 | + # rules and activating additional ones. | ||
43 | + flutter_lints: ^2.0.0 | ||
44 | + | ||
45 | +# For information on the generic Dart part of this file, see the | ||
46 | +# following page: https://dart.dev/tools/pub/pubspec | ||
47 | + | ||
48 | +# The following section is specific to Flutter packages. | ||
49 | +flutter: | ||
50 | + | ||
51 | + # The following line ensures that the Material Icons font is | ||
52 | + # included with your application, so that you can use the icons in | ||
53 | + # the material Icons class. | ||
54 | + uses-material-design: true | ||
55 | + | ||
56 | + # To add assets to your application, add an assets section, like this: | ||
57 | + # assets: | ||
58 | + # - images/a_dot_burr.jpeg | ||
59 | + # - images/a_dot_ham.jpeg | ||
60 | + | ||
61 | + # An image asset can refer to one or more resolution-specific "variants", see | ||
62 | + # https://flutter.dev/assets-and-images/#resolution-aware | ||
63 | + | ||
64 | + # For details regarding adding assets from package dependencies, see | ||
65 | + # https://flutter.dev/assets-and-images/#from-packages | ||
66 | + | ||
67 | + # To add custom fonts to your application, add a fonts section here, | ||
68 | + # in this "flutter" section. Each entry in this list should have a | ||
69 | + # "family" key with the font family name, and a "fonts" key with a | ||
70 | + # list giving the asset and other descriptors for the font. For | ||
71 | + # example: | ||
72 | + # fonts: | ||
73 | + # - family: Schyler | ||
74 | + # fonts: | ||
75 | + # - asset: fonts/Schyler-Regular.ttf | ||
76 | + # - asset: fonts/Schyler-Italic.ttf | ||
77 | + # style: italic | ||
78 | + # - family: Trajan Pro | ||
79 | + # fonts: | ||
80 | + # - asset: fonts/TrajanPro.ttf | ||
81 | + # - asset: fonts/TrajanPro_Bold.ttf | ||
82 | + # weight: 700 | ||
83 | + # | ||
84 | + # For details regarding fonts from package dependencies, | ||
85 | + # see https://flutter.dev/custom-fonts/#from-packages |
example/test/widget_test.dart
0 → 100644
1 | +// This is a basic Flutter widget test. | ||
2 | +// | ||
3 | +// To perform an interaction with a widget in your test, use the WidgetTester | ||
4 | +// utility in the flutter_test package. For example, you can send tap and scroll | ||
5 | +// gestures. You can also use WidgetTester to find child widgets in the widget | ||
6 | +// tree, read text, and verify that the values of widget properties are correct. | ||
7 | + | ||
8 | +import 'package:flutter/material.dart'; | ||
9 | +import 'package:flutter_test/flutter_test.dart'; | ||
10 | + | ||
11 | +import 'package:auto_track_example/main.dart'; | ||
12 | + | ||
13 | +void main() { | ||
14 | + testWidgets('Verify Platform version', (WidgetTester tester) async { | ||
15 | + // Build our app and trigger a frame. | ||
16 | + await tester.pumpWidget(const MyApp()); | ||
17 | + | ||
18 | + // Verify that platform version is retrieved. | ||
19 | + expect( | ||
20 | + find.byWidgetPredicate( | ||
21 | + (Widget widget) => widget is Text && | ||
22 | + widget.data!.startsWith('Running on:'), | ||
23 | + ), | ||
24 | + findsOneWidget, | ||
25 | + ); | ||
26 | + }); | ||
27 | +} |
ios/.gitignore
0 → 100644
1 | +.idea/ | ||
2 | +.vagrant/ | ||
3 | +.sconsign.dblite | ||
4 | +.svn/ | ||
5 | + | ||
6 | +.DS_Store | ||
7 | +*.swp | ||
8 | +profile | ||
9 | + | ||
10 | +DerivedData/ | ||
11 | +build/ | ||
12 | +GeneratedPluginRegistrant.h | ||
13 | +GeneratedPluginRegistrant.m | ||
14 | + | ||
15 | +.generated/ | ||
16 | + | ||
17 | +*.pbxuser | ||
18 | +*.mode1v3 | ||
19 | +*.mode2v3 | ||
20 | +*.perspectivev3 | ||
21 | + | ||
22 | +!default.pbxuser | ||
23 | +!default.mode1v3 | ||
24 | +!default.mode2v3 | ||
25 | +!default.perspectivev3 | ||
26 | + | ||
27 | +xcuserdata | ||
28 | + | ||
29 | +*.moved-aside | ||
30 | + | ||
31 | +*.pyc | ||
32 | +*sync/ | ||
33 | +Icon? | ||
34 | +.tags* | ||
35 | + | ||
36 | +/Flutter/Generated.xcconfig | ||
37 | +/Flutter/ephemeral/ | ||
38 | +/Flutter/flutter_export_environment.sh |
ios/Assets/.gitkeep
0 → 100644
ios/Classes/AutoTrackPlugin.swift
0 → 100644
1 | +import Flutter | ||
2 | +import UIKit | ||
3 | + | ||
4 | +public class AutoTrackPlugin: NSObject, FlutterPlugin { | ||
5 | + public static func register(with registrar: FlutterPluginRegistrar) { | ||
6 | + let channel = FlutterMethodChannel(name: "auto_track", binaryMessenger: registrar.messenger()) | ||
7 | + let instance = AutoTrackPlugin() | ||
8 | + registrar.addMethodCallDelegate(instance, channel: channel) | ||
9 | + } | ||
10 | + | ||
11 | + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { | ||
12 | + switch call.method { | ||
13 | + case "getPlatformVersion": | ||
14 | + result("iOS " + UIDevice.current.systemVersion) | ||
15 | + default: | ||
16 | + result(FlutterMethodNotImplemented) | ||
17 | + } | ||
18 | + } | ||
19 | +} |
ios/auto_track.podspec
0 → 100644
1 | +# | ||
2 | +# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. | ||
3 | +# Run `pod lib lint auto_track.podspec` to validate before publishing. | ||
4 | +# | ||
5 | +Pod::Spec.new do |s| | ||
6 | + s.name = 'auto_track' | ||
7 | + s.version = '0.0.1' | ||
8 | + s.summary = 'Auto Track Plugin' | ||
9 | + s.description = <<-DESC | ||
10 | +Auto Track Plugin | ||
11 | + DESC | ||
12 | + s.homepage = 'http://example.com' | ||
13 | + s.license = { :file => '../LICENSE' } | ||
14 | + s.author = { 'Your Company' => 'email@example.com' } | ||
15 | + s.source = { :path => '.' } | ||
16 | + s.source_files = 'Classes/**/*' | ||
17 | + s.dependency 'Flutter' | ||
18 | + s.platform = :ios, '11.0' | ||
19 | + | ||
20 | + # Flutter.framework does not contain a i386 slice. | ||
21 | + s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } | ||
22 | + s.swift_version = '5.0' | ||
23 | +end |
lib/auto_track.dart
0 → 100644
1 | +library autotrack; | ||
2 | + | ||
3 | +export './auto_track/index.dart'; | ||
4 | +export './auto_track/config/config.dart'; | ||
5 | +export './auto_track/page_view/navigation_observer.dart'; | ||
6 | +export './auto_track/click/navigator_key.dart'; | ||
7 | +export './auto_track/click/element_key.dart'; | ||
8 | +export './auto_track/log/logger.dart'; |
lib/auto_track/click/click_info.dart
0 → 100644
1 | +import 'package:flutter/widgets.dart'; | ||
2 | + | ||
3 | +import '../config/manager.dart'; | ||
4 | +import '../page_view/page_info.dart'; | ||
5 | +import '../utils/element_util.dart'; | ||
6 | +import 'element_key.dart'; | ||
7 | +import 'xpath.dart'; | ||
8 | + | ||
9 | +class ClickInfo { | ||
10 | + ClickInfo._(this.pageInfo); | ||
11 | + | ||
12 | + factory ClickInfo.from({ | ||
13 | + required Element gestureElement, | ||
14 | + required PointerDownEvent event, | ||
15 | + required Element pageElement, | ||
16 | + required PageInfo pageInfo, | ||
17 | + }) { | ||
18 | + ClickInfo clickInfo = ClickInfo._(pageInfo); | ||
19 | + clickInfo._touchX = event.position.dx.round(); | ||
20 | + clickInfo._touchY = event.position.dy.round(); | ||
21 | + | ||
22 | + XPath xpath = XPath.createBy(element: gestureElement, pageElement: pageElement); | ||
23 | + Element element = xpath.targetElement; | ||
24 | + clickInfo._elementType = element.widget.runtimeType.toString(); | ||
25 | + clickInfo._elementWidth = element.size?.width.round() ?? 0; | ||
26 | + clickInfo._elementHeight = element.size?.height.round() ?? 0; | ||
27 | + clickInfo._elementPath = xpath.toString(); | ||
28 | + clickInfo._texts = ElementUtil.findTexts(element); | ||
29 | + Key? key = element.widget.key; | ||
30 | + if (key != null && key is ValueKey) { | ||
31 | + clickInfo._elementManualKey = (key).value; | ||
32 | + } else { | ||
33 | + clickInfo._elementManualKey = key?.toString() ?? ''; | ||
34 | + } | ||
35 | + clickInfo._ignore = AutoTrackConfigManager.instance.isIgnoreElement(key); | ||
36 | + if (key is AutoTrackElementKey && !clickInfo._ignore) { | ||
37 | + clickInfo._ignore = key.ignore; | ||
38 | + } | ||
39 | + | ||
40 | + return clickInfo; | ||
41 | + } | ||
42 | + | ||
43 | + int _touchX = 0; | ||
44 | + int get touchX => _touchX; | ||
45 | + | ||
46 | + int _touchY = 0; | ||
47 | + int get touchY => _touchY; | ||
48 | + | ||
49 | + int _elementWidth = 0; | ||
50 | + int get elementWidth => _elementWidth; | ||
51 | + | ||
52 | + int _elementHeight = 0; | ||
53 | + int get elementHeight => _elementHeight; | ||
54 | + | ||
55 | + String _elementType = ''; | ||
56 | + String get elementType => _elementType; | ||
57 | + | ||
58 | + String _elementManualKey = ''; | ||
59 | + String get elementManualKey => _elementManualKey; | ||
60 | + | ||
61 | + String _elementPath = ''; | ||
62 | + String get elementPath => _elementPath; | ||
63 | + | ||
64 | + bool _ignore = false; | ||
65 | + bool get ignore => _ignore; | ||
66 | + | ||
67 | + List<String> _texts = []; | ||
68 | + List<String> get texts => _texts; | ||
69 | + | ||
70 | + final PageInfo pageInfo; | ||
71 | + | ||
72 | + @override | ||
73 | + String toString() { | ||
74 | + return [ | ||
75 | + 'elementType: $elementType', | ||
76 | + 'elementManualKey: $elementManualKey', | ||
77 | + 'elementPath: $elementPath', | ||
78 | + 'touchX: $touchX', | ||
79 | + 'touchY: $touchY', | ||
80 | + 'elementWidth: $elementWidth', | ||
81 | + 'elementHeight: $elementHeight', | ||
82 | + 'texts: ${texts.join(";")}', | ||
83 | + 'pageInfo: $pageInfo', | ||
84 | + ].join(', '); | ||
85 | + } | ||
86 | +} |
lib/auto_track/click/element_key.dart
0 → 100644
1 | +import 'package:flutter/widgets.dart'; | ||
2 | + | ||
3 | +class AutoTrackElementKey extends Key { | ||
4 | + const AutoTrackElementKey(this.name, { | ||
5 | + this.ignore = false | ||
6 | + }) : super.empty(); | ||
7 | + | ||
8 | + final String name; | ||
9 | + final bool ignore; | ||
10 | + | ||
11 | + @override | ||
12 | + String toString() { | ||
13 | + return name; | ||
14 | + } | ||
15 | +} |
lib/auto_track/click/navigator_key.dart
0 → 100644
1 | +import 'package:flutter/widgets.dart'; | ||
2 | + | ||
3 | +class AutoTrackNavigatorKey { | ||
4 | + static AutoTrackNavigatorKey? _instance; | ||
5 | + AutoTrackNavigatorKey._(); | ||
6 | + | ||
7 | + static AutoTrackNavigatorKey _getInstance() { | ||
8 | + _instance ??= AutoTrackNavigatorKey._(); | ||
9 | + return _instance!; | ||
10 | + } | ||
11 | + | ||
12 | + GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>(); | ||
13 | + | ||
14 | + static GlobalKey<NavigatorState> navigatorKeyWrap(GlobalKey<NavigatorState>? navigatorKey) { | ||
15 | + if (navigatorKey != null) { | ||
16 | + _getInstance()._navigatorKey = navigatorKey; | ||
17 | + } | ||
18 | + return _getInstance()._navigatorKey; | ||
19 | + } | ||
20 | + | ||
21 | + static GlobalKey<NavigatorState> get navigatorKey => _getInstance()._navigatorKey; | ||
22 | +} |
1 | +import 'dart:collection'; | ||
2 | + | ||
3 | +import 'package:flutter/gestures.dart'; | ||
4 | +import 'package:flutter/rendering.dart'; | ||
5 | +import 'package:flutter/widgets.dart'; | ||
6 | + | ||
7 | + | ||
8 | +import '../log/logger.dart'; | ||
9 | +import '../page_view/page_info.dart'; | ||
10 | +import '../page_view/page_stack.dart'; | ||
11 | +import '../track/track.dart'; | ||
12 | +import '../utils/element_util.dart'; | ||
13 | +import 'click_info.dart'; | ||
14 | + | ||
15 | +class PointerEventListener { | ||
16 | + static PointerEventListener instance = PointerEventListener._(); | ||
17 | + PointerEventListener._(); | ||
18 | + | ||
19 | + bool _started = false; | ||
20 | + _AutoTrackTapGestureRecognizer _gestureRecognizer = _AutoTrackTapGestureRecognizer(); | ||
21 | + | ||
22 | + void start() { | ||
23 | + if (!_started) { | ||
24 | + GestureBinding.instance?.pointerRouter.addGlobalRoute(_pointerRoute); | ||
25 | + _gestureRecognizer = _AutoTrackTapGestureRecognizer(); | ||
26 | + _gestureRecognizer.onTap = (){}; | ||
27 | + _started = true; | ||
28 | + } | ||
29 | + } | ||
30 | + | ||
31 | + void stop() { | ||
32 | + if (_started) { | ||
33 | + GestureBinding.instance?.pointerRouter.removeGlobalRoute(_pointerRoute); | ||
34 | + _gestureRecognizer.dispose(); | ||
35 | + _started = false; | ||
36 | + } | ||
37 | + } | ||
38 | + | ||
39 | + void _pointerRoute(PointerEvent event) { | ||
40 | + try { | ||
41 | + if (event is PointerDownEvent) { | ||
42 | + _gestureRecognizer.addPointer(event); | ||
43 | + } else if (event is PointerUpEvent) { | ||
44 | + _gestureRecognizer.checkPointerUp(event); | ||
45 | + PointerDownEvent? pointerDownEvent = _gestureRecognizer.lastPointerDownEvent; | ||
46 | + if (event.pointer != _gestureRecognizer.rejectPointer && pointerDownEvent != null) { | ||
47 | + _checkTapElementAndTrack(pointerDownEvent, event); | ||
48 | + } | ||
49 | + } | ||
50 | + } catch (e) { | ||
51 | + AutoTrackLogger.getInstance().error(e); | ||
52 | + } | ||
53 | + } | ||
54 | + | ||
55 | + void _checkTapElementAndTrack(PointerDownEvent event, PointerUpEvent upEvent) { | ||
56 | + final page = PageStack.instance.getCurrentPage(); | ||
57 | + if (page == null) { | ||
58 | + return; | ||
59 | + } | ||
60 | + | ||
61 | + LinkedList<_HitEntry> hits = LinkedList(); | ||
62 | + ElementUtil.walkElement(page.element, (child, _) { | ||
63 | + if (child is RenderObjectElement && child.renderObject is RenderBox) { | ||
64 | + RenderBox renderBox = child.renderObject as RenderBox; | ||
65 | + if (!renderBox.hasSize) { | ||
66 | + return false; | ||
67 | + } | ||
68 | + | ||
69 | + Offset localPosition = renderBox.globalToLocal(upEvent.position); | ||
70 | + if (!renderBox.size.contains(localPosition)) { | ||
71 | + return false; | ||
72 | + } | ||
73 | + | ||
74 | + if (renderBox is RenderPointerListener) { | ||
75 | + hits.add(_HitEntry(child)); | ||
76 | + } | ||
77 | + } | ||
78 | + return true; | ||
79 | + }); | ||
80 | + | ||
81 | + if (hits.isEmpty) { | ||
82 | + return; | ||
83 | + } | ||
84 | + | ||
85 | + _HitEntry? entry = hits.last; | ||
86 | + Element? gestureElement; | ||
87 | + while (entry != null) { | ||
88 | + gestureElement = ElementUtil.findAncestorElementOfWidgetType<GestureDetector>(entry.element); | ||
89 | + if (gestureElement != null) { | ||
90 | + break; | ||
91 | + } | ||
92 | + entry = entry.previous; | ||
93 | + } | ||
94 | + | ||
95 | + if (gestureElement != null) { | ||
96 | + _trackClick(gestureElement, event, page.element, page.pageInfo); | ||
97 | + } | ||
98 | + } | ||
99 | + | ||
100 | + void _trackClick(Element gestureElement, PointerDownEvent event, Element pageElement, PageInfo pageInfo) { | ||
101 | + ClickInfo clickInfo = ClickInfo.from( | ||
102 | + gestureElement: gestureElement, | ||
103 | + event: event, | ||
104 | + pageElement: pageElement, | ||
105 | + pageInfo: pageInfo, | ||
106 | + ); | ||
107 | + if (!clickInfo.ignore) { | ||
108 | + Track.instance.click(clickInfo); | ||
109 | + } | ||
110 | + } | ||
111 | +} | ||
112 | + | ||
113 | +class _AutoTrackTapGestureRecognizer extends TapGestureRecognizer { | ||
114 | + _AutoTrackTapGestureRecognizer({ Object? debugOwner }) : super(debugOwner: debugOwner); | ||
115 | + | ||
116 | + PointerDownEvent? lastPointerDownEvent; | ||
117 | + int rejectPointer = 0; | ||
118 | + | ||
119 | + void checkPointerUp(PointerUpEvent upEvent) { | ||
120 | + if (lastPointerDownEvent == null) { | ||
121 | + return; | ||
122 | + } | ||
123 | + | ||
124 | + Offset downPosition = lastPointerDownEvent!.position; | ||
125 | + Offset upPosition = upEvent.position; | ||
126 | + double offset = (downPosition.dx - upPosition.dx).abs() + (downPosition.dy - upPosition.dy).abs(); | ||
127 | + if (offset > 30) { | ||
128 | + rejectGesture(upEvent.pointer); | ||
129 | + } | ||
130 | + } | ||
131 | + | ||
132 | + @override | ||
133 | + void addPointer(PointerDownEvent event) { | ||
134 | + lastPointerDownEvent = event; | ||
135 | + super.addPointer(event); | ||
136 | + } | ||
137 | + | ||
138 | + @override | ||
139 | + void rejectGesture(int pointer) { | ||
140 | + if (lastPointerDownEvent?.pointer == pointer) { | ||
141 | + lastPointerDownEvent = null; | ||
142 | + } | ||
143 | + rejectPointer = pointer; | ||
144 | + super.rejectGesture(pointer); | ||
145 | + } | ||
146 | +} | ||
147 | + | ||
148 | +final class _HitEntry extends LinkedListEntry<_HitEntry> { | ||
149 | + _HitEntry(this.element); | ||
150 | + final Element element; | ||
151 | +} |
lib/auto_track/click/xpath.dart
0 → 100644
1 | +import 'dart:collection'; | ||
2 | + | ||
3 | +import 'package:flutter/material.dart'; | ||
4 | + | ||
5 | +class XPath { | ||
6 | + XPath._(this._targetElement); | ||
7 | + | ||
8 | + factory XPath.createBy({ | ||
9 | + required Element element, | ||
10 | + required Element pageElement, | ||
11 | + }) { | ||
12 | + XPath xpath = XPath._(element); | ||
13 | + xpath._targetElement = element; | ||
14 | + | ||
15 | + final highLevelSet = _PathConst.highLevelSet; | ||
16 | + LinkedList<_ElementEntry> originalPath = LinkedList(); | ||
17 | + originalPath.add(_ElementEntry(element)); | ||
18 | + | ||
19 | + bool lookForTarget = true; | ||
20 | + element.visitAncestorElements((parent) { | ||
21 | + if (parent.widget is GestureDetector) { | ||
22 | + lookForTarget = false; | ||
23 | + } | ||
24 | + if (lookForTarget && highLevelSet.contains(parent.widget.runtimeType)) { | ||
25 | + xpath._targetElement = parent; | ||
26 | + } | ||
27 | + originalPath.add(_ElementEntry(parent)); | ||
28 | + if (pageElement == parent) { | ||
29 | + return false; | ||
30 | + } | ||
31 | + return true; | ||
32 | + }); | ||
33 | + | ||
34 | + LinkedList<PathNode> path = xpath._buildFromOriginal(xpath._targetElement, originalPath); | ||
35 | + xpath._shortPath(path); | ||
36 | + | ||
37 | + if (path.isNotEmpty) { | ||
38 | + path.first.isPage = true; | ||
39 | + } | ||
40 | + for (var node in path) { | ||
41 | + node.computeIndex(); | ||
42 | + } | ||
43 | + xpath._path = path; | ||
44 | + | ||
45 | + return xpath; | ||
46 | + } | ||
47 | + | ||
48 | + Element _targetElement; | ||
49 | + Element get targetElement => _targetElement; | ||
50 | + | ||
51 | + LinkedList<PathNode> _path = LinkedList(); | ||
52 | + LinkedList<PathNode> get path => _path; | ||
53 | + | ||
54 | + LinkedList<PathNode> _buildFromOriginal(Element targetElement, LinkedList<_ElementEntry> originalPath) { | ||
55 | + LinkedList<PathNode> path = LinkedList(); | ||
56 | + if (originalPath.isEmpty) { | ||
57 | + return path; | ||
58 | + } | ||
59 | + | ||
60 | + _ElementEntry? entry = originalPath.last; | ||
61 | + while (entry != null) { | ||
62 | + PathNode node = PathNode(entry.element); | ||
63 | + if (!node.ignore) { | ||
64 | + node.formatName(); | ||
65 | + path.add(node); | ||
66 | + } | ||
67 | + if (entry.element == targetElement) { | ||
68 | + break; | ||
69 | + } | ||
70 | + entry = entry.previous; | ||
71 | + } | ||
72 | + return path; | ||
73 | + } | ||
74 | + | ||
75 | + void _shortPath(LinkedList<PathNode> path) { | ||
76 | + if (path.isEmpty) { | ||
77 | + return; | ||
78 | + } | ||
79 | + | ||
80 | + final shortWidgetMap = _PathConst.shortWidgetMap; | ||
81 | + PathNode? node = path.first; | ||
82 | + while (node != null) { | ||
83 | + if (shortWidgetMap.containsKey(node.name)) { | ||
84 | + _ShortWidgetConfig short = shortWidgetMap[node.name]!; | ||
85 | + node = _removeInternal(path, node, short); | ||
86 | + } else { | ||
87 | + node = node.next; | ||
88 | + } | ||
89 | + } | ||
90 | + } | ||
91 | + | ||
92 | + PathNode? _removeInternal(LinkedList<PathNode> path, PathNode node, _ShortWidgetConfig short) { | ||
93 | + PathNode? internalNode = node.next; | ||
94 | + Element? indexElement; | ||
95 | + for (String internalWidgetName in short.internalWidgets) { | ||
96 | + if (internalNode == null) { | ||
97 | + return null; | ||
98 | + } | ||
99 | + | ||
100 | + if (internalNode.name != internalWidgetName) { | ||
101 | + return internalNode; | ||
102 | + } | ||
103 | + if (internalWidgetName == short.indexWidget) { | ||
104 | + indexElement = internalNode.indexElement; | ||
105 | + } | ||
106 | + PathNode tmpNode = internalNode; | ||
107 | + internalNode = internalNode.next; | ||
108 | + path.remove(tmpNode); | ||
109 | + } | ||
110 | + if (indexElement != null) { | ||
111 | + internalNode?.indexElement = indexElement; | ||
112 | + } | ||
113 | + return internalNode; | ||
114 | + } | ||
115 | + | ||
116 | + @override | ||
117 | + String toString() { | ||
118 | + return path.join('/'); | ||
119 | + } | ||
120 | +} | ||
121 | + | ||
122 | +final class _ElementEntry extends LinkedListEntry<_ElementEntry> { | ||
123 | + _ElementEntry(this.element); | ||
124 | + final Element element; | ||
125 | +} | ||
126 | + | ||
127 | +final class PathNode extends LinkedListEntry<PathNode> { | ||
128 | + PathNode(this.indexElement) { | ||
129 | + _name = indexElement.widget.runtimeType.toString(); | ||
130 | + _checkIgnore(indexElement); | ||
131 | + } | ||
132 | + | ||
133 | + String _name = ''; | ||
134 | + String get name => _name; | ||
135 | + | ||
136 | + int _index = 0; | ||
137 | + int get index => _index; | ||
138 | + | ||
139 | + bool _ignore = false; | ||
140 | + bool get ignore => _ignore; | ||
141 | + | ||
142 | + bool isPage = false; | ||
143 | + Element indexElement; | ||
144 | + | ||
145 | + void formatName() { | ||
146 | + String widgetName = _name; | ||
147 | + int index = widgetName.indexOf('\<'); | ||
148 | + if (index > -1) { | ||
149 | + _name = widgetName.substring(0, index); | ||
150 | + } | ||
151 | + } | ||
152 | + | ||
153 | + void _checkIgnore(Element element) { | ||
154 | + Widget widget = element.widget; | ||
155 | + if (widget is! StatelessWidget && widget is! StatefulWidget) { | ||
156 | + _ignore = true; | ||
157 | + return; | ||
158 | + } | ||
159 | + | ||
160 | + if (_name[0] == '_') { | ||
161 | + _ignore = true; | ||
162 | + return; | ||
163 | + } | ||
164 | + } | ||
165 | + | ||
166 | + void computeIndex() { | ||
167 | + Element? parent; | ||
168 | + indexElement.visitAncestorElements((element) { | ||
169 | + parent = element; | ||
170 | + return false; | ||
171 | + }); | ||
172 | + if (parent == null) { | ||
173 | + isPage = true; | ||
174 | + return; | ||
175 | + } | ||
176 | + | ||
177 | + bool found = false; | ||
178 | + _index = 0; | ||
179 | + parent!.visitChildElements((element) { | ||
180 | + if (element == indexElement) { | ||
181 | + found = true; | ||
182 | + } | ||
183 | + if (!found) { | ||
184 | + _index++; | ||
185 | + } | ||
186 | + }); | ||
187 | + } | ||
188 | + | ||
189 | + @override | ||
190 | + String toString() { | ||
191 | + if (isPage) { | ||
192 | + return _name; | ||
193 | + } | ||
194 | + return '$_name[$_index]'; | ||
195 | + } | ||
196 | +} | ||
197 | + | ||
198 | +class _PathConst { | ||
199 | + static final Set<Type> highLevelSet = { | ||
200 | + InkWell, | ||
201 | + ElevatedButton, | ||
202 | + IconButton, | ||
203 | + TextButton, | ||
204 | + ListTile, | ||
205 | + }; | ||
206 | + /// key: Widget Name, value: Widget Name who handle child/children | ||
207 | + static final Map<String, _ShortWidgetConfig> shortWidgetMap = { | ||
208 | + 'Scaffold': _ShortWidgetConfig([ | ||
209 | + 'ScrollNotificationObserver', | ||
210 | + 'Material', | ||
211 | + 'AnimatedPhysicalModel', | ||
212 | + 'AnimatedDefaultTextStyle', | ||
213 | + 'AnimatedBuilder', | ||
214 | + 'Actions' | ||
215 | + ]), | ||
216 | + 'AppBar': _ShortWidgetConfig([ | ||
217 | + 'Material', | ||
218 | + 'AnimatedPhysicalModel', | ||
219 | + 'AnimatedDefaultTextStyle', | ||
220 | + 'SafeArea', | ||
221 | + 'Builder', | ||
222 | + 'NavigationToolbar', | ||
223 | + ]), | ||
224 | + 'BottomNavigationBar': _ShortWidgetConfig([ | ||
225 | + 'Material', | ||
226 | + 'AnimatedPhysicalModel', | ||
227 | + 'AnimatedDefaultTextStyle', | ||
228 | + 'Material', | ||
229 | + 'AnimatedDefaultTextStyle', | ||
230 | + 'Builder', | ||
231 | + ]), | ||
232 | + 'ListView': _ShortWidgetConfig([ | ||
233 | + 'Scrollable', | ||
234 | + 'RawGestureDetector', | ||
235 | + 'KeyedSubtree', | ||
236 | + 'AutomaticKeepAlive', | ||
237 | + ], indexWidget: 'KeyedSubtree'), | ||
238 | + 'PageView': _ShortWidgetConfig([ | ||
239 | + 'Scrollable', | ||
240 | + 'RawGestureDetector', | ||
241 | + 'SliverFillViewport', | ||
242 | + 'KeyedSubtree', | ||
243 | + 'AutomaticKeepAlive', | ||
244 | + ], indexWidget: 'KeyedSubtree'), | ||
245 | + 'Card': _ShortWidgetConfig([ | ||
246 | + 'Container', | ||
247 | + 'Material', | ||
248 | + 'AnimatedDefaultTextStyle', | ||
249 | + ]), | ||
250 | + 'IconButton': _ShortWidgetConfig([ | ||
251 | + 'InkResponse', | ||
252 | + 'Actions', | ||
253 | + 'Focus', | ||
254 | + 'GestureDetector', | ||
255 | + 'RawGestureDetector', | ||
256 | + ]), | ||
257 | + 'InkResponse': _ShortWidgetConfig([ | ||
258 | + 'Actions', | ||
259 | + 'Focus', | ||
260 | + 'GestureDetector', | ||
261 | + ]), | ||
262 | + }; | ||
263 | +} | ||
264 | + | ||
265 | +class _ShortWidgetConfig { | ||
266 | + _ShortWidgetConfig(this.internalWidgets, { this.indexWidget }); | ||
267 | + final List<String> internalWidgets; | ||
268 | + final String? indexWidget; | ||
269 | +} |
lib/auto_track/config/config.dart
0 → 100644
1 | +import 'dart:convert'; | ||
2 | + | ||
3 | +import 'package:crypto/crypto.dart'; | ||
4 | +import 'package:flutter/widgets.dart'; | ||
5 | +import 'package:uuid/uuid.dart'; | ||
6 | + | ||
7 | +class AutoTrackConfig { | ||
8 | + AutoTrackConfig({ | ||
9 | + this.host, | ||
10 | + this.appKey = '', | ||
11 | + this.appSecret = '', | ||
12 | + this.trackId, | ||
13 | + this.userId, | ||
14 | + this.signature, | ||
15 | + this.pageConfigs = const [], | ||
16 | + this.useCustomRoute = false, | ||
17 | + this.ignoreElementKeys = const [], | ||
18 | + this.ignoreElementStringKeys = const [], | ||
19 | + this.enablePageView = true, | ||
20 | + this.enablePageLeave = false, | ||
21 | + this.enableClick = true, | ||
22 | + this.enableUpload = false | ||
23 | + }) { | ||
24 | + trackId ??= const Uuid().v4().replaceAll('-', ''); | ||
25 | + signature ??= (t) => sha256.convert(utf8.encode('$appKey$t$appSecret')).toString(); | ||
26 | + } | ||
27 | + | ||
28 | + String? host; | ||
29 | + String? appKey; | ||
30 | + String? appSecret; | ||
31 | + String? trackId; | ||
32 | + String? userId; | ||
33 | + Function? signature; | ||
34 | + | ||
35 | + List<AutoTrackPageConfig> pageConfigs; | ||
36 | + | ||
37 | + /// 如果使用 MaterialPageRoute/PageRoute/ModalRoute 之外的 Route, | ||
38 | + /// 请打开该开关,并保证所有页面都配置在 pageConfigs 中 | ||
39 | + bool useCustomRoute; | ||
40 | + | ||
41 | + /// 推荐使用 [ElementKey] | ||
42 | + List<Key> ignoreElementKeys; | ||
43 | + | ||
44 | + List<String> ignoreElementStringKeys; | ||
45 | + | ||
46 | + Set<Key> getIgnoreElementKeySet() => Set.from(ignoreElementKeys); | ||
47 | + | ||
48 | + Set<String> getIgnoreElementStringKeySet() => | ||
49 | + Set.from(ignoreElementStringKeys); | ||
50 | + | ||
51 | + bool enablePageView; | ||
52 | + | ||
53 | + bool enablePageLeave; | ||
54 | + | ||
55 | + bool enableClick; | ||
56 | + | ||
57 | + bool enableUpload; | ||
58 | +} | ||
59 | + | ||
60 | +typedef PageWidgetFunc = bool Function(Widget); | ||
61 | + | ||
62 | +class AutoTrackPageConfig<T extends Widget> { | ||
63 | + AutoTrackPageConfig({ | ||
64 | + this.pageID, | ||
65 | + this.pagePath, | ||
66 | + this.ignore = false, | ||
67 | + this.pageTitle, | ||
68 | + this.isPageWidget | ||
69 | + }) { | ||
70 | + isPageWidget ??= (pageWidget) => pageWidget is T; | ||
71 | + } | ||
72 | + | ||
73 | + String? pageID; | ||
74 | + String? pagePath; | ||
75 | + bool ignore; | ||
76 | + String? pageTitle; | ||
77 | + PageWidgetFunc? isPageWidget; | ||
78 | + | ||
79 | +// bool isPageWidget(Widget pageWidget) => pageWidget is T; | ||
80 | +} |
lib/auto_track/config/manager.dart
0 → 100644
1 | +import 'package:auto_track/auto_track/config/queue.dart'; | ||
2 | +import 'package:flutter/widgets.dart'; | ||
3 | + | ||
4 | +import 'config.dart'; | ||
5 | + | ||
6 | +class AutoTrackConfigManager { | ||
7 | + static final AutoTrackConfigManager instance = AutoTrackConfigManager._(); | ||
8 | + AutoTrackConfigManager._(); | ||
9 | + | ||
10 | + AutoTrackConfig _config = AutoTrackConfig(); | ||
11 | + AutoTrackConfig get config => _config; | ||
12 | + | ||
13 | + bool _autoTrackEnable = false; | ||
14 | + bool get autoTrackEnable => _autoTrackEnable; | ||
15 | + | ||
16 | + void updateConfig(AutoTrackConfig config) { | ||
17 | + _config = config; | ||
18 | + if (config.enableUpload) { | ||
19 | + AutoTrackQueue.instance.start(); | ||
20 | + } else { | ||
21 | + AutoTrackQueue.instance.stop(); | ||
22 | + } | ||
23 | + } | ||
24 | + | ||
25 | + void updateUserId(String userId) { | ||
26 | + _config.userId = userId; | ||
27 | + } | ||
28 | + | ||
29 | + void updatePageConfigs(List<AutoTrackPageConfig> pageConfigs) { | ||
30 | + _config.pageConfigs = pageConfigs; | ||
31 | + } | ||
32 | + | ||
33 | + void updateIgnoreElementKeys(List<Key> ignoreElementKeys) { | ||
34 | + _config.ignoreElementKeys = ignoreElementKeys; | ||
35 | + } | ||
36 | + | ||
37 | + void updateIgnoreElementStringKeys(List<String> ignoreElementStringKeys) { | ||
38 | + _config.ignoreElementStringKeys = ignoreElementStringKeys; | ||
39 | + } | ||
40 | + | ||
41 | + void enablePageView(bool enable) { | ||
42 | + _config.enablePageView = enable; | ||
43 | + } | ||
44 | + | ||
45 | + void enablePageLeave(bool enable) { | ||
46 | + _config.enablePageLeave = enable; | ||
47 | + } | ||
48 | + | ||
49 | + void enableClick(bool enable) { | ||
50 | + _config.enableClick = enable; | ||
51 | + } | ||
52 | + | ||
53 | + void enableAutoTrack(bool enable) { | ||
54 | + _autoTrackEnable = enable; | ||
55 | + } | ||
56 | + | ||
57 | + void enableUpload(bool enable) { | ||
58 | + _config.enableUpload = enable; | ||
59 | + if (enable) { | ||
60 | + AutoTrackQueue.instance.start(); | ||
61 | + } else { | ||
62 | + AutoTrackQueue.instance.stop(); | ||
63 | + } | ||
64 | + } | ||
65 | + | ||
66 | + List<AutoTrackPageConfig> get pageConfigs => _config.pageConfigs; | ||
67 | + | ||
68 | + bool get useCustomRoute => _config.useCustomRoute; | ||
69 | + | ||
70 | + AutoTrackPageConfig getPageConfig(Widget pageWidget) { | ||
71 | + return _config.pageConfigs.firstWhere( | ||
72 | + (pageConfig) => pageConfig.isPageWidget!(pageWidget), | ||
73 | + orElse: () => AutoTrackPageConfig() | ||
74 | + ); | ||
75 | + } | ||
76 | + | ||
77 | + Set<Key> getIgnoreElementKeySet() => _config.getIgnoreElementKeySet(); | ||
78 | + | ||
79 | + Set<String> getIgnoreElementStringKeySet() => _config.getIgnoreElementStringKeySet(); | ||
80 | + | ||
81 | + bool isIgnoreElement(Key? key) { | ||
82 | + if (key == null) { | ||
83 | + return false; | ||
84 | + } | ||
85 | + if (getIgnoreElementKeySet().contains(key)) { | ||
86 | + return true; | ||
87 | + } | ||
88 | + if (getIgnoreElementStringKeySet().contains(key.toString())) { | ||
89 | + return true; | ||
90 | + } | ||
91 | + return false; | ||
92 | + } | ||
93 | + | ||
94 | + bool get pageViewEnabled => _config.enablePageView; | ||
95 | + | ||
96 | + bool get pageLeaveEnable => _config.enablePageLeave; | ||
97 | + | ||
98 | + bool get clickEnable => _config.enableClick; | ||
99 | +} |
lib/auto_track/config/queue.dart
0 → 100644
1 | +import 'dart:async'; | ||
2 | + | ||
3 | +import 'package:auto_track/auto_track/config/manager.dart'; | ||
4 | +import 'package:auto_track/auto_track/utils/track_model.dart'; | ||
5 | +import 'package:dio/dio.dart'; | ||
6 | + | ||
7 | +import '../log/logger.dart'; | ||
8 | + | ||
9 | + | ||
10 | + | ||
11 | +class AutoTrackQueue { | ||
12 | + static final AutoTrackQueue instance = AutoTrackQueue._(); | ||
13 | + AutoTrackQueue._(); | ||
14 | + | ||
15 | + Timer? _timer; | ||
16 | + final List<TrackModel> _queue = []; | ||
17 | + final dio = Dio(); | ||
18 | + | ||
19 | + void appendQueue(TrackModel model) { | ||
20 | + if (_timer == null) return; | ||
21 | + _queue.add(model); | ||
22 | + } | ||
23 | + | ||
24 | + void start() { | ||
25 | + if (_timer != null) return; | ||
26 | + _timer = Timer.periodic(const Duration(seconds: 10), (timer) { | ||
27 | + flush(); | ||
28 | + }); | ||
29 | + } | ||
30 | + | ||
31 | + void stop() { | ||
32 | + _timer?.cancel(); | ||
33 | + _timer = null; | ||
34 | + } | ||
35 | + | ||
36 | + void flush() { | ||
37 | + if (_queue.isEmpty) return; | ||
38 | + final uploadList = List.from(_queue); | ||
39 | + _queue.clear(); | ||
40 | + final config = AutoTrackConfigManager.instance.config; | ||
41 | + final host = config.host; | ||
42 | + if (host != null) { | ||
43 | + final t = DateTime.now().millisecond; | ||
44 | + dio.post(host, data: { | ||
45 | + 'app_key': config.appKey ?? '', | ||
46 | + 'signature': config.signature!(t), | ||
47 | + 't': t, | ||
48 | + 'user_id': config.userId ?? '', | ||
49 | + 'track_id': config.trackId ?? '', | ||
50 | + 'data_list': uploadList.map((e) => e.toMap()).toList(), | ||
51 | + }).onError((error, stackTrace) { | ||
52 | + AutoTrackLogger.getInstance().error(error!); | ||
53 | + return Future.value(Response(statusCode: 500, requestOptions: RequestOptions(path: host))); | ||
54 | + }); | ||
55 | + } | ||
56 | + } | ||
57 | +} |
lib/auto_track/index.dart
0 → 100644
1 | +import 'package:flutter/foundation.dart'; | ||
2 | + | ||
3 | +import 'click/pointer_event_listener.dart'; | ||
4 | +import 'config/config.dart'; | ||
5 | +import 'config/manager.dart'; | ||
6 | +import 'log/logger.dart'; | ||
7 | + | ||
8 | +class AutoTrack { | ||
9 | + static final AutoTrack _instance = AutoTrack._(); | ||
10 | + AutoTrack._(); | ||
11 | + | ||
12 | + factory AutoTrack({ AutoTrackConfig? config }) { | ||
13 | + _instance.config(config); | ||
14 | + return _instance; | ||
15 | + } | ||
16 | + | ||
17 | + void updateUserId(String id) { | ||
18 | + AutoTrackConfigManager.instance.updateUserId(id); | ||
19 | + } | ||
20 | + | ||
21 | + AutoTrack config(AutoTrackConfig? config) { | ||
22 | + if (config != null) { | ||
23 | + AutoTrackConfigManager.instance.updateConfig(config); | ||
24 | + } | ||
25 | + return _instance; | ||
26 | + } | ||
27 | + | ||
28 | + AutoTrack pageConfigs(List<AutoTrackPageConfig>? pageConfigs) { | ||
29 | + if (pageConfigs != null) { | ||
30 | + AutoTrackConfigManager.instance.updatePageConfigs(pageConfigs); | ||
31 | + } | ||
32 | + return _instance; | ||
33 | + } | ||
34 | + | ||
35 | + AutoTrack ignoreElementKeys(List<Key>? ignoreElementKeys) { | ||
36 | + if (ignoreElementKeys != null) { | ||
37 | + AutoTrackConfigManager.instance.updateIgnoreElementKeys(ignoreElementKeys); | ||
38 | + } | ||
39 | + return _instance; | ||
40 | + } | ||
41 | + | ||
42 | + AutoTrack ignoreElementStringKeys(List<String>? ignoreElementStringKeys) { | ||
43 | + if (ignoreElementStringKeys != null) { | ||
44 | + AutoTrackConfigManager.instance.updateIgnoreElementStringKeys(ignoreElementStringKeys); | ||
45 | + } | ||
46 | + return _instance; | ||
47 | + } | ||
48 | + | ||
49 | + AutoTrack enablePageView() { | ||
50 | + AutoTrackConfigManager.instance.enablePageView(true); | ||
51 | + return _instance; | ||
52 | + } | ||
53 | + | ||
54 | + AutoTrack disablePageView() { | ||
55 | + AutoTrackConfigManager.instance.enablePageView(false); | ||
56 | + return _instance; | ||
57 | + } | ||
58 | + | ||
59 | + AutoTrack enablePageLeave() { | ||
60 | + AutoTrackConfigManager.instance.enablePageLeave(true); | ||
61 | + return _instance; | ||
62 | + } | ||
63 | + | ||
64 | + AutoTrack disablePageLeave() { | ||
65 | + AutoTrackConfigManager.instance.enablePageLeave(false); | ||
66 | + return _instance; | ||
67 | + } | ||
68 | + | ||
69 | + AutoTrack enableUpload() { | ||
70 | + AutoTrackConfigManager.instance.enableUpload(true); | ||
71 | + return _instance; | ||
72 | + } | ||
73 | + | ||
74 | + AutoTrack disableUpload() { | ||
75 | + AutoTrackConfigManager.instance.enableUpload(false); | ||
76 | + return _instance; | ||
77 | + } | ||
78 | + | ||
79 | + AutoTrack enableClick() { | ||
80 | + AutoTrackConfigManager.instance.enableClick(true); | ||
81 | + return _instance; | ||
82 | + } | ||
83 | + | ||
84 | + AutoTrack disableClick() { | ||
85 | + AutoTrackConfigManager.instance.enableClick(false); | ||
86 | + return _instance; | ||
87 | + } | ||
88 | + | ||
89 | + AutoTrack enable() { | ||
90 | + AutoTrackConfigManager.instance.enableAutoTrack(true); | ||
91 | + PointerEventListener.instance.start(); | ||
92 | + return _instance; | ||
93 | + } | ||
94 | + | ||
95 | + AutoTrack disable() { | ||
96 | + AutoTrackConfigManager.instance.enableAutoTrack(false); | ||
97 | + PointerEventListener.instance.stop(); | ||
98 | + return _instance; | ||
99 | + } | ||
100 | + | ||
101 | + AutoTrack enableLog() { | ||
102 | + final logger = AutoTrackLogger.getInstance(); | ||
103 | + if (!logger.hasHandler) { | ||
104 | + logger.setHandler((level, message) { | ||
105 | + if (kDebugMode) { | ||
106 | + print('AutoTrack [$level] - $message'); | ||
107 | + } | ||
108 | + }); | ||
109 | + } | ||
110 | + return _instance; | ||
111 | + } | ||
112 | +} |
lib/auto_track/log/logger.dart
0 → 100644
1 | +import 'package:dio/dio.dart'; | ||
2 | +import 'package:flutter/widgets.dart'; | ||
3 | + | ||
4 | +typedef AutoTrackLoggerHandler = void Function(AutoTrackLoggerLevel level, String message); | ||
5 | + | ||
6 | +class AutoTrackLogger { | ||
7 | + static final AutoTrackLogger _instance = AutoTrackLogger(); | ||
8 | + static AutoTrackLogger getInstance() => _instance; | ||
9 | + | ||
10 | + List<_LoggerData> _data = []; | ||
11 | + AutoTrackLoggerHandler? _handler; | ||
12 | + bool _isPrinting = false; | ||
13 | + bool get hasHandler => _handler != null; | ||
14 | + | ||
15 | + void info(String message) { | ||
16 | + _print(AutoTrackLoggerLevel.Info, message); | ||
17 | + } | ||
18 | + | ||
19 | + void debug(String message) { | ||
20 | + _print(AutoTrackLoggerLevel.Debug, message); | ||
21 | + } | ||
22 | + | ||
23 | + void error(Object e) { | ||
24 | + String message = Error.safeToString(e); | ||
25 | + if (e is FlutterError) { | ||
26 | + message = e.message; | ||
27 | + } else if (e is Error) { | ||
28 | + message = e.stackTrace.toString(); | ||
29 | + } else if (e is DioException) { | ||
30 | + message = (e).message ?? 'dio exception with unknown message'; | ||
31 | + } | ||
32 | + _print(AutoTrackLoggerLevel.Error, message); | ||
33 | + } | ||
34 | + | ||
35 | + void setHandler(AutoTrackLoggerHandler handler) { | ||
36 | + _handler = handler; | ||
37 | + } | ||
38 | + | ||
39 | + void _print(AutoTrackLoggerLevel level, String message) { | ||
40 | + if (_handler == null) { | ||
41 | + return; | ||
42 | + } | ||
43 | + | ||
44 | + _data.add(_LoggerData(level, message)); | ||
45 | + if (_isPrinting) { | ||
46 | + return; | ||
47 | + } | ||
48 | + | ||
49 | + _isPrinting = true; | ||
50 | + Future.delayed(const Duration(milliseconds: 300)).then((_) { | ||
51 | + List<_LoggerData> data = _data; | ||
52 | + _data = []; | ||
53 | + if (_handler != null) { | ||
54 | + for (var log in data) { | ||
55 | + _handler!(log.level, log.message); | ||
56 | + } | ||
57 | + } | ||
58 | + _isPrinting = false; | ||
59 | + }); | ||
60 | + } | ||
61 | +} | ||
62 | + | ||
63 | +enum AutoTrackLoggerLevel { | ||
64 | + Info, | ||
65 | + Debug, | ||
66 | + Error, | ||
67 | +} | ||
68 | + | ||
69 | +class _LoggerData { | ||
70 | + const _LoggerData(this.level, this.message); | ||
71 | + final AutoTrackLoggerLevel level; | ||
72 | + final String message; | ||
73 | +} |
1 | +import 'package:flutter/material.dart'; | ||
2 | +import 'package:flutter/scheduler.dart'; | ||
3 | + | ||
4 | +import '../config/config.dart'; | ||
5 | +import '../config/manager.dart'; | ||
6 | +import '../log/logger.dart'; | ||
7 | +import '../utils/element_util.dart'; | ||
8 | +import 'page_stack.dart'; | ||
9 | + | ||
10 | +class AutoTrackNavigationObserver extends NavigatorObserver { | ||
11 | + static List<NavigatorObserver> wrap(List<NavigatorObserver>? navigatorObservers) { | ||
12 | + if (navigatorObservers == null) { | ||
13 | + return [AutoTrackNavigationObserver()]; | ||
14 | + } | ||
15 | + | ||
16 | + bool found = false; | ||
17 | + List<NavigatorObserver> removeList = []; | ||
18 | + for (NavigatorObserver observer in navigatorObservers) { | ||
19 | + if (observer is AutoTrackNavigationObserver) { | ||
20 | + if (found) { | ||
21 | + removeList.add(observer); | ||
22 | + } | ||
23 | + found = true; | ||
24 | + } | ||
25 | + } | ||
26 | + for (NavigatorObserver observer in removeList) { | ||
27 | + navigatorObservers.remove(observer); | ||
28 | + } | ||
29 | + if (!found) { | ||
30 | + navigatorObservers.insert(0, AutoTrackNavigationObserver()); | ||
31 | + } | ||
32 | + return navigatorObservers; | ||
33 | + } | ||
34 | + | ||
35 | + static List<NavigatorObserver> defaultNavigatorObservers() => [AutoTrackNavigationObserver()]; | ||
36 | + | ||
37 | + @override | ||
38 | + void didPop(Route route, Route? previousRoute) { | ||
39 | + super.didPop(route, previousRoute); | ||
40 | + // print('didPop -> $route, previousRoute -> $previousRoute'); | ||
41 | + try { | ||
42 | + PageStack.instance.pop(route, previousRoute); | ||
43 | + } catch (e) { | ||
44 | + AutoTrackLogger.getInstance().error(e); | ||
45 | + } | ||
46 | + } | ||
47 | + | ||
48 | + @override | ||
49 | + void didPush(Route route, Route? previousRoute) { | ||
50 | + super.didPush(route, previousRoute); | ||
51 | + // print('didPush -> $route, previousRoute -> $previousRoute'); | ||
52 | + try { | ||
53 | + _findElement(route, (element) { | ||
54 | + PageStack.instance.push(route, element, previousRoute); | ||
55 | + }); | ||
56 | + } catch (e) { | ||
57 | + AutoTrackLogger.getInstance().error(e); | ||
58 | + } | ||
59 | + } | ||
60 | + | ||
61 | + @override | ||
62 | + void didRemove(Route route, Route? previousRoute) { | ||
63 | + super.didRemove(route, previousRoute); | ||
64 | + // print('didRemove -> $route, previousRoute -> $previousRoute'); | ||
65 | + try { | ||
66 | + PageStack.instance.remove(route, previousRoute); | ||
67 | + } catch (e) { | ||
68 | + AutoTrackLogger.getInstance().error(e); | ||
69 | + } | ||
70 | + } | ||
71 | + | ||
72 | + @override | ||
73 | + void didReplace({Route? newRoute, Route? oldRoute}) { | ||
74 | + super.didReplace(newRoute: newRoute, oldRoute: oldRoute); | ||
75 | + // print('didReplace -> $newRoute, oldRoute -> $oldRoute'); | ||
76 | + try { | ||
77 | + if (newRoute != null) { | ||
78 | + _findElement(newRoute, (element) { | ||
79 | + PageStack.instance.replace(newRoute, element, oldRoute); | ||
80 | + }); | ||
81 | + } | ||
82 | + } catch (e) { | ||
83 | + AutoTrackLogger.getInstance().error(e); | ||
84 | + } | ||
85 | + } | ||
86 | + | ||
87 | + void _findElement(Route route, Function(Element) callback) { | ||
88 | + SchedulerBinding.instance?.addPostFrameCallback((_) { | ||
89 | + if (route is ModalRoute) { | ||
90 | + ModalRoute pageRoute = route; | ||
91 | + ElementUtil.walk(pageRoute.subtreeContext, (element, parent) { | ||
92 | + if (parent != null && parent.widget is Semantics) { | ||
93 | + callback(element); | ||
94 | + return false; | ||
95 | + } | ||
96 | + return true; | ||
97 | + }); | ||
98 | + } else if (AutoTrackConfigManager.instance.useCustomRoute) { | ||
99 | + List<AutoTrackPageConfig> pageConfigs = AutoTrackConfigManager.instance.pageConfigs; | ||
100 | + if (pageConfigs.isEmpty) { | ||
101 | + return; | ||
102 | + } | ||
103 | + | ||
104 | + Element? lastPageElement; | ||
105 | + ElementUtil.walk(route.navigator?.context, (element, parent) { | ||
106 | + if (pageConfigs.last.isPageWidget!(element.widget)) { | ||
107 | + lastPageElement = element; | ||
108 | + return false; | ||
109 | + } | ||
110 | + return true; | ||
111 | + }); | ||
112 | + if (lastPageElement != null) { | ||
113 | + callback(lastPageElement!); | ||
114 | + } | ||
115 | + } | ||
116 | + }); | ||
117 | + } | ||
118 | +} |
lib/auto_track/page_view/page_info.dart
0 → 100644
1 | +import 'package:flutter/material.dart'; | ||
2 | + | ||
3 | +import '../config/config.dart'; | ||
4 | +import '../config/manager.dart'; | ||
5 | +import '../utils/element_util.dart'; | ||
6 | + | ||
7 | +class PageInfo { | ||
8 | + PageInfo._(this.timer); | ||
9 | + | ||
10 | + factory PageInfo.fromElement(Element element, Route route) { | ||
11 | + AutoTrackPageConfig pageConfig = AutoTrackConfigManager.instance.getPageConfig(element.widget); | ||
12 | + PageInfo pageInfo = PageInfo._(PageTimer()); | ||
13 | + pageInfo._pageKey = element.widget.runtimeType.toString(); | ||
14 | + pageInfo._pagePath = pageConfig.pagePath ?? route.settings.name ?? ''; | ||
15 | + pageInfo._pageManualKey = pageConfig.pageID ?? ''; | ||
16 | + pageInfo._pageTitle = pageConfig.pageTitle ?? pageInfo._findTitle(element) ?? ''; | ||
17 | + pageInfo.ignore = pageConfig.ignore; | ||
18 | + return pageInfo; | ||
19 | + } | ||
20 | + | ||
21 | + final PageTimer timer; | ||
22 | + bool isBack = false; | ||
23 | + bool ignore = false; | ||
24 | + | ||
25 | + String _pageKey = ''; | ||
26 | + String get pageKey => _pageKey; | ||
27 | + | ||
28 | + String _pageTitle = ''; | ||
29 | + String get pageTitle => _pageTitle; | ||
30 | + | ||
31 | + String _pageManualKey = ''; | ||
32 | + String get pageManualKey => _pageManualKey; | ||
33 | + | ||
34 | + String _pagePath = ''; | ||
35 | + String get pagePath => _pagePath; | ||
36 | + | ||
37 | + String? _findTitle(Element element) { | ||
38 | + String? title; | ||
39 | + ElementUtil.walkElement(element, (child, _) { | ||
40 | + if (child.widget is NavigationToolbar) { | ||
41 | + NavigationToolbar toolBar = child.widget as NavigationToolbar; | ||
42 | + if (toolBar.middle == null) { | ||
43 | + return false; | ||
44 | + } | ||
45 | + | ||
46 | + if (toolBar.middle is Text) { | ||
47 | + title = (toolBar.middle as Text).data; | ||
48 | + return false; | ||
49 | + } | ||
50 | + | ||
51 | + int layoutIndex = 0; | ||
52 | + if (toolBar.leading != null) { | ||
53 | + layoutIndex += 1; | ||
54 | + } | ||
55 | + title = _findTextsInMiddle(child, layoutIndex); | ||
56 | + return false; | ||
57 | + } | ||
58 | + return true; | ||
59 | + }); | ||
60 | + return title; | ||
61 | + } | ||
62 | + String? _findTextsInMiddle(Element element, int layoutIndex) { | ||
63 | + String? text; | ||
64 | + int index = 0; | ||
65 | + ElementUtil.walkElement(element, ((child, _) { | ||
66 | + if (child.widget is LayoutId) { | ||
67 | + if (index == layoutIndex) { | ||
68 | + text = ElementUtil.findTexts(child).join(''); | ||
69 | + return false; | ||
70 | + } | ||
71 | + index += 1; | ||
72 | + } | ||
73 | + return true; | ||
74 | + })); | ||
75 | + return text; | ||
76 | + } | ||
77 | + | ||
78 | + @override | ||
79 | + String toString() => '{ pageKey: $pageKey, pageTitle: $pageTitle, pageManualKey: $pageManualKey, pagePath: $pagePath, isBack: $isBack }'; | ||
80 | +} | ||
81 | + | ||
82 | +enum PageTimerState { | ||
83 | + Init, | ||
84 | + Start, | ||
85 | + Pause, | ||
86 | + Resume, | ||
87 | + End, | ||
88 | +} | ||
89 | + | ||
90 | +class PageTimer { | ||
91 | + PageTimer(); | ||
92 | + | ||
93 | + PageTimerState _state = PageTimerState.Init; | ||
94 | + PageTimerState get state => _state; | ||
95 | + | ||
96 | + int _lastTimeStamp = 0; | ||
97 | + | ||
98 | + Duration _duration = const Duration(); | ||
99 | + Duration get duration => _duration; | ||
100 | + | ||
101 | + int _computeMilliseconds() { | ||
102 | + return DateTime.now().millisecondsSinceEpoch - _lastTimeStamp; | ||
103 | + } | ||
104 | + | ||
105 | + start() { | ||
106 | + if (_state != PageTimerState.Init && _state != PageTimerState.End) { | ||
107 | + return; | ||
108 | + } | ||
109 | + | ||
110 | + _state = PageTimerState.Start; | ||
111 | + _lastTimeStamp = DateTime.now().millisecondsSinceEpoch; | ||
112 | + _duration = const Duration(); | ||
113 | + } | ||
114 | + | ||
115 | + pause() { | ||
116 | + if (_state != PageTimerState.Start && _state != PageTimerState.Resume) { | ||
117 | + return; | ||
118 | + } | ||
119 | + | ||
120 | + _state = PageTimerState.Pause; | ||
121 | + _duration = Duration(milliseconds: _duration.inMilliseconds + _computeMilliseconds()); | ||
122 | + } | ||
123 | + | ||
124 | + resume() { | ||
125 | + if (_state != PageTimerState.Pause) { | ||
126 | + return; | ||
127 | + } | ||
128 | + | ||
129 | + _state = PageTimerState.Resume; | ||
130 | + _lastTimeStamp = DateTime.now().millisecondsSinceEpoch; | ||
131 | + } | ||
132 | + | ||
133 | + end() { | ||
134 | + if (_state == PageTimerState.Pause) { | ||
135 | + _state = PageTimerState.End; | ||
136 | + return; | ||
137 | + } | ||
138 | + | ||
139 | + if (_state == PageTimerState.Start || _state == PageTimerState.Resume) { | ||
140 | + _state = PageTimerState.End; | ||
141 | + _duration = Duration(milliseconds: _duration.inMilliseconds + _computeMilliseconds()); | ||
142 | + } | ||
143 | + } | ||
144 | +} |
lib/auto_track/page_view/page_stack.dart
0 → 100644
1 | +import 'dart:collection'; | ||
2 | +import 'dart:core'; | ||
3 | + | ||
4 | +import 'package:flutter/widgets.dart'; | ||
5 | + | ||
6 | +import '../track/track.dart'; | ||
7 | +import 'page_info.dart'; | ||
8 | + | ||
9 | +class PageStack with WidgetsBindingObserver { | ||
10 | + static final PageStack instance = PageStack._(); | ||
11 | + PageStack._() { | ||
12 | + WidgetsBinding.instance?.addObserver(this); | ||
13 | + } | ||
14 | + | ||
15 | + final LinkedList<Page> _stack = LinkedList<Page>(); | ||
16 | + final _PageTask _task = _PageTask(); | ||
17 | + | ||
18 | + @override | ||
19 | + void didChangeAppLifecycleState(AppLifecycleState state) { | ||
20 | + if (state == AppLifecycleState.resumed) { | ||
21 | + for (var page in _stack) { | ||
22 | + page.pageInfo.timer.resume(); | ||
23 | + } | ||
24 | + } else if (state == AppLifecycleState.paused) { | ||
25 | + for (var page in _stack) { | ||
26 | + page.pageInfo.timer.pause(); | ||
27 | + } | ||
28 | + } | ||
29 | + } | ||
30 | + | ||
31 | + push(Route route, Element element, Route? previousRoute) { | ||
32 | + Page page = Page(route, element); | ||
33 | + _stack.add(page); | ||
34 | + _task.addPush(page, page.previous); | ||
35 | + } | ||
36 | + | ||
37 | + pop(Route route, Route? previousRoute) { | ||
38 | + if (_stack.isEmpty) { | ||
39 | + return; | ||
40 | + } | ||
41 | + | ||
42 | + Page? page = _findPage(route); | ||
43 | + if (page != null) { | ||
44 | + _task.addPop(page, page.previous); | ||
45 | + } | ||
46 | + _removeAllAfter(page); | ||
47 | + } | ||
48 | + | ||
49 | + remove(Route route, Route? previousRoute) { | ||
50 | + if (_stack.isEmpty) { | ||
51 | + return; | ||
52 | + } | ||
53 | + | ||
54 | + Page? page = _findPage(route); | ||
55 | + if (page != null) { | ||
56 | + _stack.remove(page); | ||
57 | + } | ||
58 | + } | ||
59 | + | ||
60 | + replace(Route newRoute, Element newElement, Route? oldRoute) { | ||
61 | + Page newPage = Page(newRoute, newElement); | ||
62 | + Page? oldPage; | ||
63 | + if (oldRoute != null) { | ||
64 | + oldPage = _findPage(oldRoute); | ||
65 | + _removeAllAfter(oldPage); | ||
66 | + } | ||
67 | + _stack.add(newPage); | ||
68 | + _task.addReplace(newPage, oldPage); | ||
69 | + } | ||
70 | + | ||
71 | + Page? _findPage(Route route) { | ||
72 | + if (_stack.isEmpty) { | ||
73 | + return null; | ||
74 | + } | ||
75 | + | ||
76 | + Page? page = _stack.last; | ||
77 | + while (page != null) { | ||
78 | + if (page.route == route) { | ||
79 | + return page; | ||
80 | + } | ||
81 | + page = page.previous; | ||
82 | + } | ||
83 | + return null; | ||
84 | + } | ||
85 | + | ||
86 | + _removeAllAfter(Page? page) { | ||
87 | + while (page != null) { | ||
88 | + _stack.remove(page); | ||
89 | + page = page.next; | ||
90 | + } | ||
91 | + } | ||
92 | + | ||
93 | + Page? getCurrentPage() { | ||
94 | + if (_stack.isEmpty) { | ||
95 | + return null; | ||
96 | + } | ||
97 | + return _stack.last; | ||
98 | + } | ||
99 | +} | ||
100 | + | ||
101 | +final class Page extends LinkedListEntry<Page> { | ||
102 | + Page._({ | ||
103 | + required this.pageInfo, | ||
104 | + required this.route, | ||
105 | + required this.element, | ||
106 | + }); | ||
107 | + factory Page(Route route, Element element) { | ||
108 | + return Page._( | ||
109 | + pageInfo: PageInfo.fromElement(element, route), | ||
110 | + route: route, | ||
111 | + element: element, | ||
112 | + ); | ||
113 | + } | ||
114 | + | ||
115 | + final PageInfo pageInfo; | ||
116 | + final Route route; | ||
117 | + final Element element; | ||
118 | + | ||
119 | + @override | ||
120 | + String toString() => 'pageInfo: $pageInfo, route: $route'; | ||
121 | +} | ||
122 | + | ||
123 | +class _PageTask { | ||
124 | + final List<_PageTaskData> _list = []; | ||
125 | + bool _taskRunning = false; | ||
126 | + | ||
127 | + addPush(Page page, Page? prevPage) { | ||
128 | + _PageTaskData taskData = _PageTaskData(_PageTaskType.Push, page); | ||
129 | + taskData.prevPage = prevPage; | ||
130 | + _list.add(taskData); | ||
131 | + _triggerTask(); | ||
132 | + } | ||
133 | + | ||
134 | + addPop(Page page, Page? prevPage) { | ||
135 | + _PageTaskData taskData = _PageTaskData(_PageTaskType.Pop, page); | ||
136 | + taskData.prevPage = prevPage; | ||
137 | + _list.add(taskData); | ||
138 | + _triggerTask(); | ||
139 | + } | ||
140 | + | ||
141 | + addReplace(Page page, Page? prevPage) { | ||
142 | + _PageTaskData taskData = _PageTaskData(_PageTaskType.Replace, page); | ||
143 | + taskData.prevPage = prevPage; | ||
144 | + _list.add(taskData); | ||
145 | + _triggerTask(); | ||
146 | + } | ||
147 | + | ||
148 | + _triggerTask() { | ||
149 | + if (_taskRunning) { | ||
150 | + return; | ||
151 | + } | ||
152 | + | ||
153 | + _taskRunning = true; | ||
154 | + | ||
155 | + Future.delayed(const Duration(milliseconds: 30)).then((_) => _executeTask()); | ||
156 | + } | ||
157 | + | ||
158 | + _executeTask() { | ||
159 | + if (_list.isEmpty) { | ||
160 | + _taskRunning = false; | ||
161 | + return; | ||
162 | + } | ||
163 | + | ||
164 | + List list = _list.sublist(0); | ||
165 | + Page? enterPage, leavePage; | ||
166 | + _list.clear(); | ||
167 | + for (_PageTaskData taskData in list as List<_PageTaskData>) { | ||
168 | + if (taskData.type == _PageTaskType.Push) { | ||
169 | + leavePage ??= taskData.prevPage; | ||
170 | + enterPage = taskData.page; | ||
171 | + } else if (taskData.type == _PageTaskType.Pop) { | ||
172 | + leavePage ??= taskData.page; | ||
173 | + if (enterPage == null || enterPage == taskData.page) { | ||
174 | + enterPage = taskData.prevPage; | ||
175 | + enterPage?.pageInfo.isBack = true; | ||
176 | + } | ||
177 | + } else if (taskData.type == _PageTaskType.Replace) { | ||
178 | + leavePage ??= taskData.prevPage; | ||
179 | + if (enterPage == null || enterPage == taskData.prevPage) { | ||
180 | + enterPage = taskData.page; | ||
181 | + } | ||
182 | + } | ||
183 | + } | ||
184 | + if (enterPage != leavePage) { | ||
185 | + if (enterPage != null && !enterPage.pageInfo.ignore) { | ||
186 | + enterPage.pageInfo.timer.start(); | ||
187 | + Track.instance.pageView(enterPage.pageInfo); | ||
188 | + } | ||
189 | + if (leavePage != null && !leavePage.pageInfo.ignore) { | ||
190 | + leavePage.pageInfo.timer.end(); | ||
191 | + Track.instance.pageLeave(leavePage.pageInfo); | ||
192 | + } | ||
193 | + } | ||
194 | + _taskRunning = false; | ||
195 | + } | ||
196 | +} | ||
197 | + | ||
198 | +class _PageTaskData { | ||
199 | + _PageTaskData(this.type, this.page); | ||
200 | + final _PageTaskType type; | ||
201 | + final Page page; | ||
202 | + Page? prevPage; | ||
203 | +} | ||
204 | + | ||
205 | +enum _PageTaskType { | ||
206 | + Push, | ||
207 | + Pop, | ||
208 | + Replace, | ||
209 | +} |
-
Please register or login to post a comment