Getting Started
Please refer to our Quickstart Guide. The Full API Reference, Library Source Code, and an Example Application is documented in our GitHub repo.Installing the Library
To install the library in your Flutter development environment, declare it as a dependency in thepubspec.yaml file of your package:
Mixpanel.init() using your project token.
mixpanel throughout the rest of your application.
Flutter Web Support
Add the following snippet to your web/index.html inside the <head></head> tag in your project:
Library Configuration
The Mixpanel instance can be customized with different configurations. TheMixpanel.init() method accepts arguments that sets some configurations on your instance upon initialization.
After initialization, you can further customize the instance using the configuration options here.
Example Usage
Sending Events
Use.track() to send an event by providing the event name and any event properties. This will trigger a request to the /track API endpoint to ingest the event into your project.
The /track endpoint will only validate events with timestamps within the last 5 days of the request. Events with timestamps older than 5 days will not be ingested. See below on best practices for historical imports.
Timing Events
You can track the time it took for an action to occur, such as an image upload or a comment post, using.timeEvent(). This will mark the “start” of your action, which will be timed until you finish with a track call. The time duration is then recorded in the “Duration” property.
Flushing Events
To preserve battery life and customer bandwidth, the Mixpanel library doesn’t send the events you record immediately. Instead, it sends batches to the Mixpanel servers every 60 seconds while your application is running, as well as when the application transitions to the background. Call.flush() manually if you want to force a flush at a particular moment.
Example Usage
Importing Historical Events
The Flutter SDK is a tracking SDK designed for real-time tracking in a client-side environment. Calling.track() triggers a request to our /track API endpoint, which will validate for events with a timestamp that is within the last 5 days of the request. Events older than 5 days will not be ingested.
For bulk import of historical events older than 5 days, we will need to use the /import API endpoint which is optimized for scripting and supports ingesting historical data. We recommend the Python SDK (see the .import_data() function) and mixpanel-utils module (see the import_events() function) which both leverages the /import API for event ingestion.
Setting Super Properties
Super properties are global event properties that you define once and apply to all events. To register super properties, call.registerSuperProperties().
Use .registerSuperPropertiesOnce() to register super properties without overwriting existing values.
Example Usage
Managing User Identity
You can handle the identity of a user using the.identify() and .reset() methods. Learn more about identity management and identifying users.
Identify
Call.identify() when you know the identity of the current user, passing in their user ID as an argument. This is typically at account registration and at log in.
Example Usage
Call Reset at Logout
Call.reset() to clear data attributed to a user when they logout. This will clear the local storage and allows you to handle multiple users on a single device.
Example Usage
Storing User Profiles
Once your users are identified, create user profiles by setting profile properties to describe them. Example profile properties include “name”, “email”, “company”, and any other demographic details about the user. The Flutter SDK provides a few methods for setting profile properties under thePeople class accessible via .getPeople(). These methods will trigger requests to the /engage API endpoint.
Setting Profile Properties
You must call
.identify() before setting profile properties in order to associate the profile properties you set with the target user. If identify is not called, the profile update will be queued for ingestion until an identify call is made..getPeople().set() method.
If a profile property already exists, it will be overwritten with the latest value provided in the method. If a profile property does not exist, it will be added to the profile.
Example Usage
Other Types of Profile Updates
There are a few other methods for setting profile properties. See a complete reference of the available methods here A few commonly used people methods are highlighted below:- .setOnce()
- .append()
- .union()
- .increment()
The
.getPeople().setOnce() method set profile properties only if they do not exist yet. If it is setting a profile property that already exists, it will be ignored.Use this method if you want to set profile properties without the risk of overwriting existing data.Example UsageGroup Analytics
Read more about Group Analytics before proceeding. You will need to have the group key defined in your project settings first.
group_key and group_id.
group_keyis the event property that connects event data to a group. (e.g.company)group_idis the identifier for a specific group. (e.g.mixpanel,company_a,company_b, etc.)
Adding Users to a Group
All events must have the group key as an event property in order to be attributed to a group. Without the group key, an event cannot be attributed to a group. Call the.setGroup() method to register the current user to a group, which would add the group_key as an event property set to the group_id value to all events moving forward.
Example Usage
group_key value as a list of multiple group_id values.
Call .addGroup() to add additional group_ids to an existing list.
Example Usage
Adding Group Identifiers to User Profiles
To connect group information to a user profile, include thegroup_key and group_id as a user profile property using the .getPeople().set() call.
Example Usage
Setting Group Profile Properties
Create a group profiles by setting group properties, similar to a user profile. For example, you may want to describe a company group with properties such as “ARR”, “employee_count”, and “subscription”. To set group profile properties, specify the group that needs to be updated by calling.getGroup(), then set the group properties by chaining the .set() method, which will trigger a request to the /groups API endpoint.
Example Usage
Other Group Profile Methods
See all of the methods under the Group class here. A few commonly used group methods are highlighted below:- .setOnce()
- .unset()
- .union()
- .remove()
The
.getGroup().setOnce() method set group profile properties only if they do not exist yet. If it is setting a profile property that already exists, it will be ignored.Use this method if you want to set group profile properties without the risk of overwriting existing data.Example UsageAutocapture
The Mixpanel Flutter SDK provides methods on theautocapture object to help you track common user interactions with minimal instrumentation. These methods fire standardized events that are recognized by the Mixpanel platform.
Screen View & Screen Leave
Flutter apps use widget-based routing withNavigator, GoRouter, or other packages, each handling screen transitions differently. This makes it impractical for the SDK to automatically detect screen boundaries. Instead, you hook trackScreenView and trackScreenLeave into your app’s navigation, giving you full control over what counts as a screen and when transitions happen.
Track screen navigations using trackScreenView and trackScreenLeave. These methods fire the following events:
Both events automatically include
current_page_title (the screen name) and $mp_autocapture: true.
Passing Custom Properties
Both methods accept an optionalproperties parameter. For example, forwarding UTM params from a deep link:
Click Events
Beta. Click autocapture is in beta. Its events and properties may change before general availability. It needs Flutter SDK v2.15.0+ (Flutter 3.19+ / Dart 3.3+) and is disabled by default. See Enabling Click Autocapture to turn it on.
Each event includes
$mp_autocapture: true and the Tracked Properties.
Capture runs in Dart, in your widget tree, not in the native iOS and Android SDKs. That is why it needs the root MixpanelAutocaptureWidget as well as the init option.
Click
$mp_click fires for every tap that resolves to a widget, whether or not that widget handles the tap. The observer listens passively and never joins Flutter’s gesture arena, so it does not compete with your gesture handlers.
A tap counts only if it is:
- a primary touch or mouse press
- the only pointer down at the time
- released within 500 ms (inclusive)
- inside the platform hit slop
- A tap on a non-interactive child resolves to the nearest interactive ancestor. A tap on the
Textinside anElevatedButtonreports the button, not the label. - A nested
InkWellinside anotherInkWellkeeps its own identity. - A button’s internal
InkWellis reported as the public button (ElevatedButton, notInkWell). - If there is no interactive ancestor, the tapped
Text,RichText,ImageorIconis reported.
Dead Click
$mp_dead_click fires when a tap on an interactive widget produces no visible change. The SDK snapshots the widget tree at pointer down, before your handler runs, and compares it after DeadClickOptions.timeWindow (default 500 ms, configurable). If nothing changed, the event fires. Every dead click also has a $mp_click for the same tap.
A scroll, focus change, navigation, or another tap before the time window ends counts as a response, so no dead click is sent.
Never dead-click candidates:
- non-interactive widgets, such as a plain
Textor a decorativeIcon - disabled buttons (
onPressed: null) - controls whose feedback the detector cannot observe:
TextField,EditableText,CupertinoTextField,Switch,CupertinoSwitch,Checkbox,Radio,Slider,RangeSlider,CupertinoSlider,CupertinoPicker,CupertinoDatePicker,DropdownButton,SwitchListTile,CheckboxListTile,RadioListTile
Detection looks only at UI changes, not network activity. A handler that sends a request but leaves the screen unchanged for the whole window is reported as dead, which usually matches what the user saw. Only the state at the deadline is compared, so a change that appears and reverts before then is missed. Material’s own ripple and border feedback does not count as a response.
Surfaces the SDK cannot inspect turn dead clicks off. They never cause one. Automatic dead click detection is disabled for the whole view when:
- a visible
AndroidView,UiKitView,HtmlElementView,PlatformViewLink,Textureor app-ownedCustomPaintis present, or - a snapshot cannot be taken: the tree is detached, the geometry is invalid, or the tree has more than 2,000 elements or is deeper than 512.
trackDeadClick for dead clicks your app detects on those surfaces.Rage Click
$mp_rage_click fires on the 4th tap that lands within 1000 ms and 44 logical pixels of the earlier taps (defaults for clickThreshold, timeWindow and radius on RageClickOptions, configurable).
- The individual
$mp_clickevents still fire, so 4 rapid taps send 4 clicks and 1 rage click. - Tap history resets after each rage click, so 8 rapid taps send 2 rage clicks, not 5.
- Taps spread out in time or across the screen do not count.
$el_id to find the problem controls, or use them to pick sessions to watch in Session Replay.
One-Time Setup: Element IDs
Do this once, before you enable click autocapture. It is optional (every tap is captured either way), but without it widgets report an unreadable hash fallback such asElevatedButton_a3f21c as their $el_id.
Wrap each widget you want to measure in Semantics(identifier: ...). Put the wrapper on or above the widget that handles the tap, not on the Text or Icon inside it.
Example Usage
Enabling Click Autocapture
You need both steps. If either is missing, nothing is captured. 1. PassAutocaptureOptions to Mixpanel.init.
Example Usage
MixpanelAutocaptureWidget, above MaterialApp or CupertinoApp and their navigators. Anything outside its subtree is not captured. Use exactly one. A second, nested wrapper does nothing and asserts in debug builds.
Example Usage
instance can be null while async init finishes. Mount the wrapper right away and set the instance once init resolves. Your child is never remounted when capture starts.
Each signal can be configured or turned off on its own:
Example Usage
timeWindow values take a Duration from 1 ms to 1 minute (microsecond precision). Out-of-range values assert in debug builds and are clamped in release builds. A non-finite radius falls back to 44.
Platforms: Android, iOS, macOS and web. On Windows and Linux the wrapper does nothing and no events are sent.
Disabling Click Autocapture
Autocapture is set at init and cannot be toggled at runtime. If you omitautocaptureOptions, the click detectors are not set up, and a wrapper left in place just passes pointer events through. trackScreenView and trackScreenLeave still work. To turn it off in an app that already has it on, stop passing the options or disable each signal.
Example Usage
optInTracking().
Tracked Properties
Autocaptured click, dead click and rage click events include:$el_tag_name comes from a fixed list, not runtimeType. The SDK knows the Material and Cupertino buttons, InkWell, GestureDetector, Text, Icon, Image, common layout widgets and form controls. Anything else, including your own custom widgets, is reported as Widget. This keeps the value stable under release-mode obfuscation. Group reports by $el_id and treat $el_tag_name as a rough hint.Element text is not captured, so there is no
$el_text and no $attr-aria-label. Semantics.label is never reported (see Element ID Resolution), and editable and secure field values are never read.Element ID Resolution
The SDK resolves$el_id with fixed rules that you cannot configure:
Semantics(identifier: '...'), read from the reported widget (see Click) or aSemanticswrapper above it. The lookup never reads a child and stops at the next interactive ancestor, so an identifier on theTextinside a button is ignored. Blank identifiers and identifiers over 256 characters are skipped.<WidgetType>_<hash>, the fallback.
Semantics.label. Unlikeidentifier, which TalkBack and VoiceOver never read out,labelis read aloud, localized, and can contain user data. The SDK never reads or reports it, so a widget with only a label falls back to the hash.Key,ValueKeyandruntimeType.
Manual Click Tracking
trackClick, trackRageClick and trackDeadClick on the autocapture object send the same three events with element metadata you provide. Use them where automatic capture cannot reach: Windows and Linux, WebViews and platform views, or custom gesture handlers the hit test resolves to the wrong widget.
Example Usage
- Each method accepts an optional
propertiesmap. The typed fields and$mp_autocapture: truetake precedence over keys with the same name. - A blank
elementIdor a non-finite coordinate is ignored and nothing is sent. - Each call sends exactly one event and never starts the automatic detectors. For example,
trackClickdoes not trigger dead click detection. Your app decides whether the signal happened.
Best Practices
- Mount exactly one
MixpanelAutocaptureWidget, above your navigators. See Enabling Click Autocapture. - Give every widget you want to measure a stable
Semantics(identifier: ...)on or above the widget that handles the tap. See One-Time Setup: Element IDs. - Keep identifiers static across rebuilds. One built from a list index, timestamp or product name gets a new
$el_idon each build and spreads one control across many rows. - Use
trackDeadClickaround WebViews, platform views and custom painting, where automatic detection is off. - Verify and check for PII before relying on the data. Call
setLoggingEnabled(true), tap through your key flows, and confirm each$el_idis what you expect and contains no personal data.
Debug Mode
To enable debug mode, call.setLoggingEnabled() with true.
Example Usage
Privacy-Friendly Tracking
You have control over the data you send to Mixpanel. The Flutter SDK provide methods to help you protect user data. Learn more about Privacy.Opt Out of Tracking
The Android SDK is initialized with tracking enabled by default. Use the.optOutTracking() method to opt the user out of data tracking and local storage for the current Mixpanel instance.
Example Usage
optOutTrackingDefault configuration. Once the user is ready to be tracked, call .optInTracking() to start tracking.
Example Usage
EU Data Residency
Route data to Mixpanel’s EU servers by passing theserverURL parameter when initializing the client. This ensures every request is routed to the correct region from the very first event.
Example Usage
serverURL property after initializing the client.
Example Usage
India Data Residency
Route data to Mixpanel’s India servers by passing theserverURL parameter when initializing the client. This ensures every request is routed to the correct region from the very first event.
Example Usage
serverURL property after initializing the client.
Example Usage
Disable Geolocation
The Flutter SDK parse the request IP address to generate geolocation properties for events and profiles. To disable geolocation, call.setUseIpAddressForGeolocation() with false.
Example Usage
Legacy Automatically Tracked Events
Mixpanel’s SDKs have a legacy feature to automatically collect common mobile events. We don’t recommend enabling this, as these events rely on client-side state and can be unreliable compared to tracking server-side. You can still enable this feature by turning the flagtrackAutomaticEvents: true when initializing Mixpanel. More details here.
You can see a list of events tracked automatically for iOS here and Android here.
Tracking Via Proxy
This guide demonstrates how to route events from Mixpanel’s Flutter SDKs via a proxy in your own domain. This is useful to reduce the likelihood of ad-blockers impacting your tracking. There are two steps: setting up a proxy server and pointing the SDK at your server. Step 1: Set up a proxy server The simplest way is to use our sample nginx config. This config redirects any calls made to your proxy server to Mixpanel. Step 2: Point Flutter SDK at your server Pass theserverURL parameter when initializing the client, replacing YOUR_PROXY_DOMAIN with your proxy server’s domain. This ensures every request is routed to your proxy from the very first event.
Example Usage
serverURL property after initializing the client.
Example Usage
mixpanel.init call:
Example Usage