|
1 | | -# Project Coding Standards for AI Assistance |
| 1 | +# Mixpanel Flutter SDK - Copilot Agent Guide |
2 | 2 |
|
3 | | -> These instructions are automatically included in every GitHub Copilot interaction. They represent our most critical patterns and conventions for the Mixpanel Flutter SDK. |
| 3 | +> Complete operational guide for working efficiently with the Mixpanel Flutter SDK codebase. |
4 | 4 |
|
5 | | -## Core Principles |
| 5 | +## Repository Overview |
6 | 6 |
|
7 | | -1. **Input Validation First**: All user inputs must be validated before platform channel calls |
8 | | - - String inputs validated with `_MixpanelHelper.isValidString()` |
9 | | - - Prevent crashes at the API boundary |
| 7 | +**What**: Official Flutter SDK for Mixpanel Analytics - wraps native iOS/Android/Web SDKs with unified Dart API |
| 8 | +**Size**: ~25 source files, 1.4GB total (includes dependencies and build artifacts) |
| 9 | +**Languages**: Dart (SDK), Java (Android), Swift (iOS), JavaScript (Web) |
| 10 | +**Runtime**: Flutter 3.16.0, Dart 3.2.0, Java 17, Swift 5.0 |
10 | 11 |
|
11 | | -2. **Fail Silently with Logging**: Never throw exceptions to calling code |
12 | | - - Log errors using `developer.log()` with 'Mixpanel' name |
13 | | - - Return gracefully on validation failures |
| 12 | +## Quick Start - Essential Commands |
14 | 13 |
|
15 | | -3. **Platform Channel Consistency**: All native calls follow exact same pattern |
16 | | - - Method name must match across Dart and native code |
17 | | - - Arguments always in `Map<String, dynamic>` format |
| 14 | +**ALWAYS run these commands in order. Commands must succeed before proceeding.** |
18 | 15 |
|
19 | | -4. **Type Safety**: Handle cross-platform type differences explicitly |
20 | | - - Mobile: MixpanelMessageCodec handles DateTime/Uri |
21 | | - - Web: Use `safeJsify()` for JavaScript compatibility |
| 16 | +### 1. Install Dependencies (REQUIRED FIRST) |
| 17 | +```bash |
| 18 | +# From project root - ALWAYS run this first |
| 19 | +flutter pub get |
22 | 20 |
|
23 | | -## Flutter SDK Guidelines |
| 21 | +# For example app (if working with integration tests) |
| 22 | +cd example && flutter pub get && cd .. |
| 23 | +``` |
| 24 | +**Time**: ~30 seconds. **Must complete** before any other command. |
24 | 25 |
|
25 | | -### Method Patterns |
26 | | -All public methods MUST follow this exact structure: |
27 | | -```dart |
28 | | -Future<void> methodName(String requiredParam, [Map<String, dynamic>? optionalParam]) async { |
29 | | - if (!_MixpanelHelper.isValidString(requiredParam)) { |
30 | | - developer.log('`methodName` failed: requiredParam cannot be blank', name: 'Mixpanel'); |
31 | | - return; |
32 | | - } |
33 | | - |
34 | | - await _channel.invokeMethod<void>('methodName', <String, dynamic>{ |
35 | | - 'requiredParam': requiredParam, |
36 | | - 'optionalParam': optionalParam ?? {}, |
37 | | - }); |
38 | | -} |
| 26 | +### 2. Run Tests |
| 27 | +```bash |
| 28 | +# Run all unit tests |
| 29 | +flutter test |
39 | 30 | ``` |
| 31 | +**Time**: ~5 seconds. **Expected**: All tests pass. The SDK has comprehensive test coverage. |
40 | 32 |
|
41 | | -### Naming Conventions |
42 | | -- **Methods**: camelCase with action verbs (`track`, `registerSuperProperties`, `getPeople`) |
43 | | -- **Parameters**: Descriptive names (`eventName`, `distinctId`, `properties`) |
44 | | -- **Maps**: Always named `properties` or `superProperties` for consistency |
45 | | - |
46 | | -### Platform Channel Rules |
47 | | -When invoking platform methods, you MUST: |
48 | | -1. Use exact method name matching between Dart and native |
49 | | - ```dart |
50 | | - await _channel.invokeMethod<void>('track', args); // 'track' must exist in native |
51 | | - ``` |
52 | | - |
53 | | -2. Structure arguments as flat maps |
54 | | - ```dart |
55 | | - <String, dynamic>{ |
56 | | - 'eventName': eventName, |
57 | | - 'properties': properties ?? {}, |
58 | | - } |
59 | | - ``` |
60 | | - |
61 | | -3. Handle optional parameters with `?? {}` |
62 | | - ```dart |
63 | | - 'properties': properties ?? {}, // Never pass null |
64 | | - ``` |
65 | | - |
66 | | -## Code Generation Rules |
67 | | - |
68 | | -When generating code, you MUST: |
69 | | - |
70 | | -1. Validate all string inputs before use |
71 | | - ```dart |
72 | | - if (!_MixpanelHelper.isValidString(input)) { |
73 | | - developer.log('`method` failed: input cannot be blank', name: 'Mixpanel'); |
74 | | - return; |
75 | | - } |
76 | | - ``` |
77 | | - |
78 | | -2. Return Future<void> for all public methods |
79 | | - ```dart |
80 | | - Future<void> methodName() async { |
81 | | - // All methods async for platform consistency |
82 | | - } |
83 | | - ``` |
84 | | - |
85 | | -3. Include library metadata in tracking calls |
86 | | - ```dart |
87 | | - properties['\$lib_version'] = '2.4.4'; |
88 | | - properties['mp_lib'] = 'flutter'; |
89 | | - ``` |
90 | | - |
91 | | -When generating code, NEVER: |
92 | | -- Throw exceptions from public methods |
93 | | -- Pass null to platform channels (use `?? {}`) |
94 | | -- Create synchronous public methods |
95 | | -- Skip input validation |
96 | | - |
97 | | -## Testing Requirements |
98 | | - |
99 | | -Every test must: |
100 | | -- Use descriptive test names: `test('should fail silently when eventName is empty')` |
101 | | -- Verify platform channel calls with `isMethodCall` matcher |
102 | | -- Test both success and validation failure cases |
| 33 | +### 3. Lint/Analyze Code |
| 34 | +```bash |
| 35 | +# Analyze Dart code (will show ~70 info-level warnings - this is normal) |
| 36 | +flutter analyze --no-pub --no-current-package --no-fatal-infos lib |
| 37 | +``` |
| 38 | +**Expected warnings**: Style suggestions (unnecessary_this, prefer_const, etc.) - NOT errors. |
| 39 | +**Time**: <1 second |
103 | 40 |
|
104 | | -```dart |
105 | | -test('tracks event with properties', () async { |
106 | | - await mixpanel.track('Event', properties: {'key': 'value'}); |
107 | | - expect( |
108 | | - methodCall, |
109 | | - isMethodCall( |
110 | | - 'track', |
111 | | - arguments: <String, dynamic>{ |
112 | | - 'eventName': 'Event', |
113 | | - 'properties': {'key': 'value'}, |
114 | | - }, |
115 | | - ), |
116 | | - ); |
117 | | -}); |
| 41 | +### 4. Build Integration Tests |
| 42 | +```bash |
| 43 | +# Android (from example directory) |
| 44 | +cd example && flutter build apk --debug |
| 45 | + |
| 46 | +# iOS (from example directory, macOS only) |
| 47 | +cd example && flutter build ios --debug --simulator --no-codesign |
118 | 48 | ``` |
| 49 | +**Android Time**: ~3 minutes first run, ~1 minute incremental |
| 50 | +**iOS Time**: ~5 minutes (requires CocoaPods: `cd example/ios && pod repo update`) |
119 | 51 |
|
120 | | -## Documentation Standards |
| 52 | +## Project Structure |
121 | 53 |
|
122 | | -- Public methods need dartdoc with parameter descriptions |
123 | | -- Use `///` for public API documentation |
124 | | -- Include parameter constraints in docs |
125 | | -- No redundant comments in implementation |
| 54 | +### Core SDK Files |
| 55 | +``` |
| 56 | +lib/ |
| 57 | +├── mixpanel_flutter.dart # Main SDK - Primary API |
| 58 | +├── mixpanel_flutter_web.dart # Web implementation |
| 59 | +├── codec/ |
| 60 | +│ └── mixpanel_message_codec.dart # Custom type serialization |
| 61 | +└── web/ |
| 62 | + └── mixpanel_js_bindings.dart # JavaScript interop |
| 63 | +``` |
126 | 64 |
|
127 | | -```dart |
128 | | -/// Tracks an event with optional properties. |
129 | | -/// |
130 | | -/// * [eventName] The name of the event to track. Cannot be empty. |
131 | | -/// * [properties] Optional properties to include with the event. |
132 | | -Future<void> track(String eventName, [Map<String, dynamic>? properties]) async { |
| 65 | +### Native Platform Code |
| 66 | +``` |
| 67 | +android/ |
| 68 | +├── build.gradle # Android config (SDK 34, Java 17) |
| 69 | +└── src/main/java/com/mixpanel/mixpanel_flutter/ |
| 70 | + ├── MixpanelFlutterPlugin.java # Android platform channel |
| 71 | + ├── MixpanelMessageCodec.java # Type serialization |
| 72 | + └── MixpanelFlutterHelper.java # Validation helpers |
| 73 | +
|
| 74 | +ios/ |
| 75 | +├── mixpanel_flutter.podspec # iOS package (Mixpanel-swift 5.1.0) |
| 76 | +└── Classes/ |
| 77 | + ├── SwiftMixpanelFlutterPlugin.swift # iOS platform channel |
| 78 | + └── MixpanelTypeHandler.swift # Type serialization |
| 79 | +``` |
| 80 | + |
| 81 | +### Tests |
| 82 | +``` |
| 83 | +test/ |
| 84 | +├── mixpanel_flutter_test.dart # Main test suite |
| 85 | +└── mixpanel_flutter_web_unit_test.dart # Web-specific tests |
| 86 | +``` |
| 87 | + |
| 88 | +### Configuration Files |
| 89 | +- `pubspec.yaml` - Flutter package config, dependencies |
| 90 | +- `analysis_options.yaml` - Lints (uses flutter_lints package) |
| 91 | +- `.github/workflows/flutter.yml` - CI pipeline (see below) |
| 92 | + |
| 93 | +## Continuous Integration |
| 94 | + |
| 95 | +The GitHub Actions workflow runs **3 jobs** on every PR: |
| 96 | + |
| 97 | +### 1. test-main-code (macOS, ~2 min) |
| 98 | +```bash |
| 99 | +flutter pub get |
| 100 | +flutter test |
| 101 | +flutter analyze --no-pub --no-current-package --no-fatal-infos lib |
| 102 | +``` |
| 103 | + |
| 104 | +### 2. test-android-integration (macOS, ~3 min) |
| 105 | +```bash |
| 106 | +cd example && flutter build apk |
133 | 107 | ``` |
134 | 108 |
|
135 | | -## Security and Performance |
| 109 | +### 3. test-ios-integration (macOS, ~5 min) |
| 110 | +```bash |
| 111 | +cd example |
| 112 | +flutter clean |
| 113 | +flutter pub get |
| 114 | +cd ios && pod repo update && cd .. |
| 115 | +flutter build ios --debug --simulator --no-codesign |
| 116 | +``` |
| 117 | + |
| 118 | +**To replicate CI locally**, run all three command sequences above in order. |
| 119 | + |
| 120 | +## Common Build Issues & Solutions |
136 | 121 |
|
137 | | -ALWAYS: |
138 | | -- Validate inputs at SDK boundaries |
139 | | -- Sanitize data before sending to native platforms |
140 | | -- Log errors without exposing sensitive data |
| 122 | +### Issue: "flutter: command not found" |
| 123 | +- **Fix**: Install Flutter 3.16.0 from https://flutter.dev/docs/get-started/install |
141 | 124 |
|
142 | | -NEVER: |
143 | | -- Log user data or event properties in error messages |
144 | | -- Trust client inputs without validation |
145 | | -- Make synchronous platform channel calls |
| 125 | +### Issue: Android build fails with SDK version error |
| 126 | +- **Expected**: Warning about SDK 34 is normal (plugin requires it, example uses 33) |
| 127 | +- **Fix**: The build will auto-download SDK 33 and succeed |
146 | 128 |
|
147 | | -## Type Handling Matrix |
| 129 | +### Issue: iOS build fails with CocoaPods error |
| 130 | +- **Fix**: Run `cd example/ios && pod repo update` before building |
148 | 131 |
|
149 | | -| Type | Mobile | Web | |
150 | | -|------|---------|-----| |
151 | | -| String | Direct pass | Direct pass | |
152 | | -| num/bool | Direct pass | Direct pass | |
153 | | -| DateTime | MixpanelMessageCodec | Convert to ISO string | |
154 | | -| Uri | MixpanelMessageCodec | Convert to string | |
155 | | -| Map | Direct pass | `safeJsify()` | |
156 | | -| List | Direct pass | `safeJsify()` | |
| 132 | +### Issue: "Gradle task assembleDebug" timeout |
| 133 | +- **Expected**: First Android build takes 2-3 minutes |
| 134 | +- **Solution**: Use `initial_wait: 180` for async commands |
157 | 135 |
|
158 | | -## Platform-Specific Patterns |
| 136 | +### Issue: Tests fail after code changes |
| 137 | +- **Cause**: Platform channel method name mismatch |
| 138 | +- **Fix**: Ensure method names match exactly in Dart + Java/Swift + Web |
159 | 139 |
|
160 | | -### Web Implementation |
| 140 | +## Critical Coding Patterns |
| 141 | + |
| 142 | +**ALWAYS follow these patterns when modifying SDK code:** |
| 143 | + |
| 144 | +### 1. Input Validation Pattern (MANDATORY) |
161 | 145 | ```dart |
162 | | -if (kIsWeb) { |
163 | | - return WebImplementation.method(safeJsify(properties)); |
| 146 | +Future<void> methodName(String param) async { |
| 147 | + if (!_MixpanelHelper.isValidString(param)) { |
| 148 | + developer.log('`methodName` failed: param cannot be blank', name: 'Mixpanel'); |
| 149 | + return; // Fail silently - NEVER throw exceptions |
| 150 | + } |
| 151 | + await _channel.invokeMethod<void>('methodName', {'param': param}); |
164 | 152 | } |
165 | 153 | ``` |
166 | 154 |
|
167 | | -### Mobile Implementation |
| 155 | +### 2. Platform Channel Rules |
| 156 | +- Method names MUST match exactly: Dart ↔ Java/Swift/Web |
| 157 | +- Arguments ALWAYS as `Map<String, dynamic>` |
| 158 | +- Optional maps use `?? {}` - NEVER pass null |
| 159 | +- All methods return `Future<void>` for consistency |
| 160 | + |
| 161 | +### 3. Type Handling |
| 162 | +- **Mobile**: MixpanelMessageCodec auto-handles DateTime/Uri |
| 163 | +- **Web**: Use `safeJsify()` for complex types |
| 164 | + |
| 165 | +### 4. Testing Requirements |
| 166 | +Every method MUST have tests: |
168 | 167 | ```dart |
169 | | -return await _channel.invokeMethod<void>('method', args); |
| 168 | +test('methodName should invoke platform method', () async { |
| 169 | + await mixpanel.methodName('param'); |
| 170 | + expect(methodCalls, hasLength(1)); |
| 171 | + expect(methodCalls[0], isMethodCall('methodName', |
| 172 | + arguments: {'param': 'param'})); |
| 173 | +}); |
| 174 | +
|
| 175 | +test('methodName should fail silently on invalid input', () async { |
| 176 | + await mixpanel.methodName(''); // Empty string |
| 177 | + expect(methodCalls, isEmpty); // No platform call made |
| 178 | +}); |
170 | 179 | ``` |
171 | 180 |
|
172 | | -## Additional Resources |
| 181 | +## Adding New Features - Checklist |
| 182 | + |
| 183 | +When adding a new SDK method: |
| 184 | +1. ✅ Add to `lib/mixpanel_flutter.dart` with validation |
| 185 | +2. ✅ Implement in `android/.../MixpanelFlutterPlugin.java` |
| 186 | +3. ✅ Implement in `ios/.../SwiftMixpanelFlutterPlugin.swift` |
| 187 | +4. ✅ Implement in `lib/mixpanel_flutter_web.dart` |
| 188 | +5. ✅ Add tests to `test/mixpanel_flutter_test.dart` |
| 189 | +6. ✅ Run `flutter test` - MUST pass all tests |
| 190 | +7. ✅ Run `flutter analyze` - check for new errors |
| 191 | +8. ✅ Build example app to verify integration |
| 192 | + |
| 193 | +## File Organization |
| 194 | + |
| 195 | +**NEVER modify**: |
| 196 | +- `build/` - Build artifacts (gitignored) |
| 197 | +- `.dart_tool/` - Flutter tooling cache |
| 198 | +- `example/pubspec.lock` - Auto-generated |
| 199 | + |
| 200 | +**Config files to update when**: |
| 201 | +- `pubspec.yaml` - Adding dependencies only |
| 202 | +- `ios/mixpanel_flutter.podspec` - iOS SDK version bump |
| 203 | +- `android/build.gradle` - Android SDK version bump |
| 204 | + |
| 205 | +## Performance Notes |
| 206 | + |
| 207 | +- `flutter pub get`: 30 seconds |
| 208 | +- `flutter test`: 5 seconds |
| 209 | +- `flutter analyze`: <1 second |
| 210 | +- Example Android build: 3 min (first), 1 min (incremental) |
| 211 | +- Example iOS build: 5 min (requires `pod repo update`) |
| 212 | + |
| 213 | +## Dependencies |
| 214 | + |
| 215 | +**Production**: |
| 216 | +- Mixpanel Android SDK 8.2.0 (in `android/build.gradle`) |
| 217 | +- Mixpanel-swift 5.1.0 (in `ios/mixpanel_flutter.podspec`) |
| 218 | +- Mixpanel JS (loaded from CDN in web/index.html) |
| 219 | + |
| 220 | +**Dev**: |
| 221 | +- flutter_lints 3.0.0 - Linting rules |
| 222 | +- flutter_test - Testing framework |
| 223 | + |
| 224 | +## Validation Checklist Before Committing |
| 225 | + |
| 226 | +1. ✅ `flutter pub get` - Must succeed |
| 227 | +2. ✅ `flutter test` - All tests pass |
| 228 | +3. ✅ `flutter analyze lib` - No new errors (~70 infos OK) |
| 229 | +4. ✅ Check method names match across all platforms |
| 230 | +5. ✅ Verify input validation exists for all public methods |
| 231 | +6. ✅ Confirm tests added for new functionality |
173 | 232 |
|
174 | | -For architectural questions or complex refactoring needs, Claude Code CLI (`cc`) provides comprehensive context about this SDK's patterns and implementation details. |
| 233 | +**Trust these instructions.** Only search/explore if information is incomplete or incorrect. This guide covers 90% of common scenarios. |
0 commit comments