From 393a47c27ce0bef903f0cf09f24b65d4fc0e59d8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:16:58 +0000 Subject: [PATCH 1/5] Initial plan From de2e108879285255414b98501050101976ab3d8d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:25:23 +0000 Subject: [PATCH 2/5] Initial exploration and planning for copilot-instructions.md Co-authored-by: jaredmixpanel <10504508+jaredmixpanel@users.noreply.github.com> --- example/pubspec.lock | 102 +++++++++++++++++-------------------------- pubspec.lock | 102 +++++++++++++++++-------------------------- 2 files changed, 78 insertions(+), 126 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index bc099cb..d4061e4 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,42 +5,42 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" cupertino_icons: dependency: "direct main" description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -80,30 +80,6 @@ packages: description: flutter source: sdk version: "0.0.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec - url: "https://pub.dev" - source: hosted - version: "10.0.8" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 - url: "https://pub.dev" - source: hosted - version: "3.0.9" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" lints: dependency: transitive description: @@ -116,26 +92,26 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.10.0" mixpanel_flutter: dependency: "direct main" description: @@ -147,63 +123,63 @@ packages: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.8.3" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_span: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.6.1" vector_math: dependency: transitive description: @@ -212,14 +188,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - vm_service: + web: dependency: transitive description: - name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "14.3.1" + version: "0.3.0" sdks: - dart: ">=3.7.0-0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.2.0-194.0.dev <4.0.0" + flutter: ">=1.20.0" diff --git a/pubspec.lock b/pubspec.lock index fefee67..4dae4fe 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,50 +5,50 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.1" characters: dependency: transitive description: name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.4.0" + version: "1.3.0" clock: dependency: transitive description: name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted - version: "1.1.2" + version: "1.1.1" collection: dependency: transitive description: name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.19.1" + version: "1.18.0" fake_async: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.1" flutter: dependency: "direct main" description: flutter @@ -72,30 +72,6 @@ packages: description: flutter source: sdk version: "0.0.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec - url: "https://pub.dev" - source: hosted - version: "10.0.8" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 - url: "https://pub.dev" - source: hosted - version: "3.0.9" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" lints: dependency: transitive description: @@ -108,87 +84,87 @@ packages: dependency: transitive description: name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.17" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.11.1" + version: "0.5.0" meta: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.10.0" path: dependency: transitive description: name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.8.3" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.0" + version: "0.0.99" source_span: dependency: transitive description: name: source_span - sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.10.1" + version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace - sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.12.1" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.4" + version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner - sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted - version: "1.4.1" + version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph - sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted - version: "1.2.2" + version: "1.2.1" test_api: dependency: transitive description: name: test_api - sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.7.4" + version: "0.6.1" vector_math: dependency: transitive description: @@ -197,14 +173,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - vm_service: + web: dependency: transitive description: - name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + name: web + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "14.3.1" + version: "0.3.0" sdks: - dart: ">=3.7.0-0 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + dart: ">=3.2.0-194.0.dev <4.0.0" + flutter: ">=1.20.0" From 44b70607ff4be77fb578c38dc3f56d3fa9a54c56 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:28:05 +0000 Subject: [PATCH 3/5] Add comprehensive copilot-instructions.md with build/test/layout info Co-authored-by: jaredmixpanel <10504508+jaredmixpanel@users.noreply.github.com> --- .github/copilot-instructions.md | 345 +++++++++++++++++++------------- 1 file changed, 202 insertions(+), 143 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c93d673..8a52fc0 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,174 +1,233 @@ -# Project Coding Standards for AI Assistance +# Mixpanel Flutter SDK - Copilot Agent Guide -> These instructions are automatically included in every GitHub Copilot interaction. They represent our most critical patterns and conventions for the Mixpanel Flutter SDK. +> Complete operational guide for working efficiently with the Mixpanel Flutter SDK codebase. -## Core Principles +## Repository Overview -1. **Input Validation First**: All user inputs must be validated before platform channel calls - - String inputs validated with `_MixpanelHelper.isValidString()` - - Prevent crashes at the API boundary +**What**: Official Flutter SDK for Mixpanel Analytics - wraps native iOS/Android/Web SDKs with unified Dart API +**Size**: ~25 source files, 1.4GB total (includes dependencies and build artifacts) +**Languages**: Dart (SDK), Java (Android), Swift (iOS), JavaScript (Web) +**Runtime**: Flutter 3.16.0, Dart 3.2.0, Java 17, Swift 5.0 -2. **Fail Silently with Logging**: Never throw exceptions to calling code - - Log errors using `developer.log()` with 'Mixpanel' name - - Return gracefully on validation failures +## Quick Start - Essential Commands -3. **Platform Channel Consistency**: All native calls follow exact same pattern - - Method name must match across Dart and native code - - Arguments always in `Map` format +**ALWAYS run these commands in order. Commands must succeed before proceeding.** -4. **Type Safety**: Handle cross-platform type differences explicitly - - Mobile: MixpanelMessageCodec handles DateTime/Uri - - Web: Use `safeJsify()` for JavaScript compatibility +### 1. Install Dependencies (REQUIRED FIRST) +```bash +# From project root - ALWAYS run this first +flutter pub get -## Flutter SDK Guidelines +# For example app (if working with integration tests) +cd example && flutter pub get && cd .. +``` +**Time**: ~30 seconds. **Must complete** before any other command. -### Method Patterns -All public methods MUST follow this exact structure: -```dart -Future methodName(String requiredParam, [Map? optionalParam]) async { - if (!_MixpanelHelper.isValidString(requiredParam)) { - developer.log('`methodName` failed: requiredParam cannot be blank', name: 'Mixpanel'); - return; - } - - await _channel.invokeMethod('methodName', { - 'requiredParam': requiredParam, - 'optionalParam': optionalParam ?? {}, - }); -} +### 2. Run Tests +```bash +# Run all unit tests (68 tests) +flutter test ``` +**Time**: ~5 seconds. **Expected**: All tests pass. The SDK has comprehensive test coverage. -### Naming Conventions -- **Methods**: camelCase with action verbs (`track`, `registerSuperProperties`, `getPeople`) -- **Parameters**: Descriptive names (`eventName`, `distinctId`, `properties`) -- **Maps**: Always named `properties` or `superProperties` for consistency - -### Platform Channel Rules -When invoking platform methods, you MUST: -1. Use exact method name matching between Dart and native - ```dart - await _channel.invokeMethod('track', args); // 'track' must exist in native - ``` - -2. Structure arguments as flat maps - ```dart - { - 'eventName': eventName, - 'properties': properties ?? {}, - } - ``` - -3. Handle optional parameters with `?? {}` - ```dart - 'properties': properties ?? {}, // Never pass null - ``` - -## Code Generation Rules - -When generating code, you MUST: - -1. Validate all string inputs before use - ```dart - if (!_MixpanelHelper.isValidString(input)) { - developer.log('`method` failed: input cannot be blank', name: 'Mixpanel'); - return; - } - ``` - -2. Return Future for all public methods - ```dart - Future methodName() async { - // All methods async for platform consistency - } - ``` - -3. Include library metadata in tracking calls - ```dart - properties['\$lib_version'] = '2.4.4'; - properties['mp_lib'] = 'flutter'; - ``` - -When generating code, NEVER: -- Throw exceptions from public methods -- Pass null to platform channels (use `?? {}`) -- Create synchronous public methods -- Skip input validation - -## Testing Requirements - -Every test must: -- Use descriptive test names: `test('should fail silently when eventName is empty')` -- Verify platform channel calls with `isMethodCall` matcher -- Test both success and validation failure cases +### 3. Lint/Analyze Code +```bash +# Analyze Dart code (will show ~70 info-level warnings - this is normal) +flutter analyze --no-pub --no-current-package --no-fatal-infos lib +``` +**Expected warnings**: Style suggestions (unnecessary_this, prefer_const, etc.) - NOT errors. +**Time**: <1 second -```dart -test('tracks event with properties', () async { - await mixpanel.track('Event', properties: {'key': 'value'}); - expect( - methodCall, - isMethodCall( - 'track', - arguments: { - 'eventName': 'Event', - 'properties': {'key': 'value'}, - }, - ), - ); -}); +### 4. Build Integration Tests +```bash +# Android (from example directory) +cd example && flutter build apk --debug + +# iOS (from example directory, macOS only) +cd example && flutter build ios --debug --simulator --no-codesign ``` +**Android Time**: ~3 minutes first run, ~1 minute incremental +**iOS Time**: ~5 minutes (requires CocoaPods: `cd example/ios && pod repo update`) -## Documentation Standards +## Project Structure -- Public methods need dartdoc with parameter descriptions -- Use `///` for public API documentation -- Include parameter constraints in docs -- No redundant comments in implementation +### Core SDK Files +``` +lib/ +├── mixpanel_flutter.dart # Main SDK (794 lines) - Primary API +├── mixpanel_flutter_web.dart # Web implementation (426 lines) +├── codec/ +│ └── mixpanel_message_codec.dart # Custom type serialization +└── web/ + └── mixpanel_js_bindings.dart # JavaScript interop +``` -```dart -/// Tracks an event with optional properties. -/// -/// * [eventName] The name of the event to track. Cannot be empty. -/// * [properties] Optional properties to include with the event. -Future track(String eventName, [Map? properties]) async { +### Native Platform Code +``` +android/ +├── build.gradle # Android config (SDK 34, Java 17) +└── src/main/java/com/mixpanel/mixpanel_flutter/ + ├── MixpanelFlutterPlugin.java # Android platform channel + ├── MixpanelMessageCodec.java # Type serialization + └── MixpanelFlutterHelper.java # Validation helpers + +ios/ +├── mixpanel_flutter.podspec # iOS package (Mixpanel-swift 5.1.0) +└── Classes/ + ├── SwiftMixpanelFlutterPlugin.swift # iOS platform channel + └── MixpanelTypeHandler.swift # Type serialization +``` + +### Tests +``` +test/ +├── mixpanel_flutter_test.dart # Main test suite (60+ tests) +└── mixpanel_flutter_web_unit_test.dart # Web-specific tests +``` + +### Configuration Files +- `pubspec.yaml` - Flutter package config, dependencies +- `analysis_options.yaml` - Lints (uses flutter_lints package) +- `.github/workflows/flutter.yml` - CI pipeline (see below) + +## Continuous Integration + +The GitHub Actions workflow runs **3 jobs** on every PR: + +### 1. test-main-code (macOS, ~2 min) +```bash +flutter pub get +flutter test +flutter analyze --no-pub --no-current-package --no-fatal-infos lib +``` + +### 2. test-android-integration (macOS, ~3 min) +```bash +cd example && flutter build apk ``` -## Security and Performance +### 3. test-ios-integration (macOS, ~5 min) +```bash +cd example +flutter clean +flutter pub get +cd ios && pod repo update && cd .. +flutter build ios --debug --simulator --no-codesign +``` + +**To replicate CI locally**, run all three command sequences above in order. + +## Common Build Issues & Solutions -ALWAYS: -- Validate inputs at SDK boundaries -- Sanitize data before sending to native platforms -- Log errors without exposing sensitive data +### Issue: "flutter: command not found" +- **Fix**: Install Flutter 3.16.0 from https://flutter.dev/docs/get-started/install -NEVER: -- Log user data or event properties in error messages -- Trust client inputs without validation -- Make synchronous platform channel calls +### Issue: Android build fails with SDK version error +- **Expected**: Warning about SDK 34 is normal (plugin requires it, example uses 33) +- **Fix**: The build will auto-download SDK 33 and succeed -## Type Handling Matrix +### Issue: iOS build fails with CocoaPods error +- **Fix**: Run `cd example/ios && pod repo update` before building -| Type | Mobile | Web | -|------|---------|-----| -| String | Direct pass | Direct pass | -| num/bool | Direct pass | Direct pass | -| DateTime | MixpanelMessageCodec | Convert to ISO string | -| Uri | MixpanelMessageCodec | Convert to string | -| Map | Direct pass | `safeJsify()` | -| List | Direct pass | `safeJsify()` | +### Issue: "Gradle task assembleDebug" timeout +- **Expected**: First Android build takes 2-3 minutes +- **Solution**: Use `initial_wait: 180` for async commands -## Platform-Specific Patterns +### Issue: Tests fail after code changes +- **Cause**: Platform channel method name mismatch +- **Fix**: Ensure method names match exactly in Dart + Java/Swift + Web -### Web Implementation +## Critical Coding Patterns + +**ALWAYS follow these patterns when modifying SDK code:** + +### 1. Input Validation Pattern (MANDATORY) ```dart -if (kIsWeb) { - return WebImplementation.method(safeJsify(properties)); +Future methodName(String param) async { + if (!_MixpanelHelper.isValidString(param)) { + developer.log('`methodName` failed: param cannot be blank', name: 'Mixpanel'); + return; // Fail silently - NEVER throw exceptions + } + await _channel.invokeMethod('methodName', {'param': param}); } ``` -### Mobile Implementation +### 2. Platform Channel Rules +- Method names MUST match exactly: Dart ↔ Java/Swift/Web +- Arguments ALWAYS as `Map` +- Optional maps use `?? {}` - NEVER pass null +- All methods return `Future` for consistency + +### 3. Type Handling +- **Mobile**: MixpanelMessageCodec auto-handles DateTime/Uri +- **Web**: Use `safeJsify()` for complex types + +### 4. Testing Requirements +Every method MUST have tests: ```dart -return await _channel.invokeMethod('method', args); +test('methodName should invoke platform method', () async { + await mixpanel.methodName('param'); + expect(methodCalls, hasLength(1)); + expect(methodCalls[0], isMethodCall('methodName', + arguments: {'param': 'param'})); +}); + +test('methodName should fail silently on invalid input', () async { + await mixpanel.methodName(''); // Empty string + expect(methodCalls, isEmpty); // No platform call made +}); ``` -## Additional Resources +## Adding New Features - Checklist + +When adding a new SDK method: +1. ✅ Add to `lib/mixpanel_flutter.dart` with validation +2. ✅ Implement in `android/.../MixpanelFlutterPlugin.java` +3. ✅ Implement in `ios/.../SwiftMixpanelFlutterPlugin.swift` +4. ✅ Implement in `lib/mixpanel_flutter_web.dart` +5. ✅ Add tests to `test/mixpanel_flutter_test.dart` +6. ✅ Run `flutter test` - MUST pass all tests +7. ✅ Run `flutter analyze` - check for new errors +8. ✅ Build example app to verify integration + +## File Organization + +**NEVER modify**: +- `build/` - Build artifacts (gitignored) +- `.dart_tool/` - Flutter tooling cache +- `example/pubspec.lock` - Auto-generated + +**Config files to update when**: +- `pubspec.yaml` - Adding dependencies only +- `ios/mixpanel_flutter.podspec` - iOS SDK version bump +- `android/build.gradle` - Android SDK version bump + +## Performance Notes + +- `flutter pub get`: 30 seconds +- `flutter test`: 5 seconds (68 tests) +- `flutter analyze`: <1 second +- Example Android build: 3 min (first), 1 min (incremental) +- Example iOS build: 5 min (requires `pod repo update`) + +## Dependencies + +**Production**: +- Mixpanel Android SDK 8.2.0 (in `android/build.gradle`) +- Mixpanel-swift 5.1.0 (in `ios/mixpanel_flutter.podspec`) +- Mixpanel JS (loaded from CDN in web/index.html) + +**Dev**: +- flutter_lints 3.0.0 - Linting rules +- flutter_test - Testing framework + +## Validation Checklist Before Committing + +1. ✅ `flutter pub get` - Must succeed +2. ✅ `flutter test` - All 68 tests pass +3. ✅ `flutter analyze lib` - No new errors (70 infos OK) +4. ✅ Check method names match across all platforms +5. ✅ Verify input validation exists for all public methods +6. ✅ Confirm tests added for new functionality -For architectural questions or complex refactoring needs, Claude Code CLI (`cc`) provides comprehensive context about this SDK's patterns and implementation details. \ No newline at end of file +**Trust these instructions.** Only search/explore if information is incomplete or incorrect. This guide covers 90% of common scenarios. \ No newline at end of file From a1411b612bb3f167c423c5be2207f929b86574d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:30:17 +0000 Subject: [PATCH 4/5] Remove hard-coded counts from copilot-instructions.md for maintainability Co-authored-by: jaredmixpanel <10504508+jaredmixpanel@users.noreply.github.com> --- .github/copilot-instructions.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 8a52fc0..c96ed91 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -25,7 +25,7 @@ cd example && flutter pub get && cd .. ### 2. Run Tests ```bash -# Run all unit tests (68 tests) +# Run all unit tests flutter test ``` **Time**: ~5 seconds. **Expected**: All tests pass. The SDK has comprehensive test coverage. @@ -54,8 +54,8 @@ cd example && flutter build ios --debug --simulator --no-codesign ### Core SDK Files ``` lib/ -├── mixpanel_flutter.dart # Main SDK (794 lines) - Primary API -├── mixpanel_flutter_web.dart # Web implementation (426 lines) +├── mixpanel_flutter.dart # Main SDK - Primary API +├── mixpanel_flutter_web.dart # Web implementation ├── codec/ │ └── mixpanel_message_codec.dart # Custom type serialization └── web/ @@ -81,7 +81,7 @@ ios/ ### Tests ``` test/ -├── mixpanel_flutter_test.dart # Main test suite (60+ tests) +├── mixpanel_flutter_test.dart # Main test suite └── mixpanel_flutter_web_unit_test.dart # Web-specific tests ``` @@ -205,7 +205,7 @@ When adding a new SDK method: ## Performance Notes - `flutter pub get`: 30 seconds -- `flutter test`: 5 seconds (68 tests) +- `flutter test`: 5 seconds - `flutter analyze`: <1 second - Example Android build: 3 min (first), 1 min (incremental) - Example iOS build: 5 min (requires `pod repo update`) @@ -224,8 +224,8 @@ When adding a new SDK method: ## Validation Checklist Before Committing 1. ✅ `flutter pub get` - Must succeed -2. ✅ `flutter test` - All 68 tests pass -3. ✅ `flutter analyze lib` - No new errors (70 infos OK) +2. ✅ `flutter test` - All tests pass +3. ✅ `flutter analyze lib` - No new errors (~70 infos OK) 4. ✅ Check method names match across all platforms 5. ✅ Verify input validation exists for all public methods 6. ✅ Confirm tests added for new functionality From 3f9c33c8dd897c09c719b7945ba1021ee48df6c9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:37:47 +0000 Subject: [PATCH 5/5] Revert pubspec.lock files - only copilot-instructions.md should be modified Co-authored-by: jaredmixpanel <10504508+jaredmixpanel@users.noreply.github.com> --- example/pubspec.lock | 102 ++++++++++++++++++++++++++----------------- pubspec.lock | 102 ++++++++++++++++++++++++++----------------- 2 files changed, 126 insertions(+), 78 deletions(-) diff --git a/example/pubspec.lock b/example/pubspec.lock index d4061e4..bc099cb 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -5,42 +5,42 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" cupertino_icons: dependency: "direct main" description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" flutter: dependency: "direct main" description: flutter @@ -80,6 +80,30 @@ packages: description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" lints: dependency: transitive description: @@ -92,26 +116,26 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.16.0" mixpanel_flutter: dependency: "direct main" description: @@ -123,63 +147,63 @@ packages: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.1" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.4" vector_math: dependency: transitive description: @@ -188,14 +212,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "14.3.1" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" - flutter: ">=1.20.0" + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.lock b/pubspec.lock index 4dae4fe..fefee67 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,50 +5,50 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" fake_async: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" flutter: dependency: "direct main" description: flutter @@ -72,6 +72,30 @@ packages: description: flutter source: sdk version: "0.0.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + url: "https://pub.dev" + source: hosted + version: "10.0.8" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 + url: "https://pub.dev" + source: hosted + version: "3.0.9" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" lints: dependency: transitive description: @@ -84,87 +108,87 @@ packages: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.16.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.1" sky_engine: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.4" vector_math: dependency: transitive description: @@ -173,14 +197,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + name: vm_service + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "14.3.1" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" - flutter: ">=1.20.0" + dart: ">=3.7.0-0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54"