Respectlytics Respect lytics
Menu
Flutter Video completion Privacy-first

How to track video completions in Flutter without personal data

Video engagement events — play, 25% / 50% / 75% milestones, complete — are dense signals for content apps. Most analytics SDKs default to tagging them with the video ID, content category, and watch duration, building a per-user watch history along the way. Respectlytics helps developers avoid collecting personal data in the first place: in Flutter, each milestone is its own named event, with no metadata. Below: how to wire to the player's progress callbacks, why milestones beat raw position, and what content metadata stays in your CMS.

Fire the call at meaningful playback milestones: video_play_started, video_25pct_reached, video_50pct_reached, video_75pct_reached, video_completed. Don't pass video ID, content title, or duration.

Install the Flutter SDK

yaml Respectlytics
# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  respectlytics_flutter: ^3.0.0

Pure Dart — no platform channels for analytics. Same code on every platform Flutter compiles to (iOS, Android, web, macOS, Windows, Linux). On web, events are sent via the REST API; mobile platforms use the same path.

Initialize Respectlytics in Flutter

dart Respectlytics
import 'package:flutter/material.dart';
import 'package:respectlytics_flutter/respectlytics_flutter.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Respectlytics.configure(appKey: '<YOUR_APP_KEY>');
  runApp(const MyApp());
}

Initialize in main() after WidgetsFlutterBinding.ensureInitialized() and before runApp(). The future completes immediately on configuration; events queued before completion are flushed once the network is available.

Track the event in Flutter

dart Respectlytics
import 'package:respectlytics_flutter/respectlytics_flutter.dart';
import 'package:video_player/video_player.dart';

class VideoMilestoneTracker {
  final VideoPlayerController controller;
  bool _m25 = false, _m50 = false, _m75 = false, _done = false;

  VideoMilestoneTracker(this.controller) {
    controller.addListener(_check);
  }

  void _check() {
    final duration = controller.value.duration.inMilliseconds;
    if (duration <= 0) return;
    final progress = controller.value.position.inMilliseconds / duration;
    if (progress >= 0.25 && !_m25) { _m25 = true; Respectlytics.track('video_25pct_reached'); }
    if (progress >= 0.50 && !_m50) { _m50 = true; Respectlytics.track('video_50pct_reached'); }
    if (progress >= 0.75 && !_m75) { _m75 = true; Respectlytics.track('video_75pct_reached'); }
    if (progress >= 0.99 && !_done) { _done = true; Respectlytics.track('video_completed'); }
  }
}

Construct one VideoMilestoneTracker per video — disposing one and constructing another resets the flags. Call controller.removeListener(_check) in dispose.

Privacy & implementation notes

Tracking exact playback position generates 30+ events per minute of viewing and dilutes every other signal in your analytics pipeline. Four milestones (25, 50, 75, 100%) per video carry essentially the same product signal at 1/100th the volume.

Your CMS already stores video titles, descriptions, durations, and content categories. Joining video_completed_movie events to your CMS's view-count-per-video data answers most product questions; storing the title as analytics metadata duplicates a system of record without adding signal.

The Flutter SDK is pure Dart. No MethodChannel, no platform-specific iOS or Android plugin code. The same code runs on every platform Flutter supports — including web and desktop targets. This eliminates one common audit surface ("what's the Android implementation doing?").

Always initialize after WidgetsFlutterBinding.ensureInitialized() and before runApp(). If you skip the binding step, the configure call will throw on platforms that need a binding for asynchronous I/O. The SDK documentation example uses this pattern by default.

How this compares to other analytics SDKs

Video completion eventFirebase AnalyticsMixpanelRespectlytics
Video ID as parameterRecommendedRecommendedForbidden (use top-N event_names)
Content category / genreRecommendedRecommendedUse distinct event_name (top-N)
Watch duration in secondsRecommendedRecommendedOut of scope (use milestones)
Per-user watch historyYesYesOut of scope (use content backend)
Completion *rate* by country / platformYesYesYes (default)

Frequently asked questions

How do we know which videos are most-watched?

Your content backend already has play counts per video — that's where video-level engagement lives. Respectlytics is for product engagement at the feature level ("is the video player surface working?"). Conflating them duplicates a system of record.

Should we track exact playback position?

No — milestones (25 / 50 / 75 / 100%) carry the signal. Exact position produces a flood of low-value events that drown the milestone signal. Player-side analytics (your video player vendor's SDK) has its own per-second tracking if you genuinely need it.

What about content category breakdowns (movies, shows, shorts)?

If you have a small fixed set, distinct event names: video_completed_movie, video_completed_show. For high-cardinality categorization, use your content backend's analytics — it has the metadata you need.

Does this work for live streams?

Conceptually different. Use distinct event names — livestream_joined, livestream_left — and fire on join and leave. Milestones don't apply to live; duration is computed from join → leave timestamp difference.

Related guides

Track what matters. Collect nothing you don't.

Five-field event schema, RAM-only event queue, no IDFA, no AAID, no persistent user IDs. Helps developers avoid collecting personal data in the first place.