diff --git a/CHANGELOG.md b/CHANGELOG.md index 35d827e..4a11ef4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## Next -- Allow tracking non `PageRoute` screens using the `PosthogObserver` ([#95](https://github.com/PostHog/posthog-flutter/pull/95)) +- Allow overriding the route filtering usint a ctor param `routeFilter` ([#95](https://github.com/PostHog/posthog-flutter/pull/95)) ## 4.3.0 diff --git a/lib/src/posthog_observer.dart b/lib/src/posthog_observer.dart index 135f935..00b85de 100644 --- a/lib/src/posthog_observer.dart +++ b/lib/src/posthog_observer.dart @@ -4,14 +4,26 @@ import 'posthog.dart'; typedef ScreenNameExtractor = String? Function(RouteSettings settings); +/// [PostHogRouteFilter] allows to filter out routes that should not be tracked. +/// +/// By default, only [PageRoute]s are tracked. +typedef PostHogRouteFilter = bool Function(Route? route); + String? defaultNameExtractor(RouteSettings settings) => settings.name; +bool defaultPostHogRouteFilter(Route? route) => route is PageRoute; + class PosthogObserver extends RouteObserver> { - PosthogObserver({ScreenNameExtractor nameExtractor = defaultNameExtractor}) - : _nameExtractor = nameExtractor; + PosthogObserver( + {ScreenNameExtractor nameExtractor = defaultNameExtractor, + PostHogRouteFilter routeFilter = defaultPostHogRouteFilter}) + : _nameExtractor = nameExtractor, + _routeFilter = routeFilter; final ScreenNameExtractor _nameExtractor; + final PostHogRouteFilter _routeFilter; + bool _isTrackeableRoute(String? name) { return name != null && name.trim().isNotEmpty; } @@ -36,6 +48,10 @@ class PosthogObserver extends RouteObserver> { void didPush(Route route, Route? previousRoute) { super.didPush(route, previousRoute); + if (!_routeFilter(route)) { + return; + } + _sendScreenView(route); } @@ -43,6 +59,10 @@ class PosthogObserver extends RouteObserver> { void didReplace({Route? newRoute, Route? oldRoute}) { super.didReplace(newRoute: newRoute, oldRoute: oldRoute); + if (!_routeFilter(newRoute)) { + return; + } + _sendScreenView(newRoute); } @@ -50,6 +70,10 @@ class PosthogObserver extends RouteObserver> { void didPop(Route route, Route? previousRoute) { super.didPop(route, previousRoute); + if (!_routeFilter(previousRoute)) { + return; + } + _sendScreenView(previousRoute); } } diff --git a/test/posthog_observer_test.dart b/test/posthog_observer_test.dart index 1725be0..c51dd03 100644 --- a/test/posthog_observer_test.dart +++ b/test/posthog_observer_test.dart @@ -1,8 +1,5 @@ -import 'dart:math'; - import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:posthog_flutter/src/posthog.dart'; import 'package:posthog_flutter/src/posthog_flutter_io.dart'; import 'package:posthog_flutter/src/posthog_flutter_platform_interface.dart'; import 'package:posthog_flutter/src/posthog_observer.dart'; @@ -28,8 +25,10 @@ void main() { }); PosthogObserver getSut( - {ScreenNameExtractor nameExtractor = defaultNameExtractor}) { - return PosthogObserver(nameExtractor: nameExtractor); + {ScreenNameExtractor nameExtractor = defaultNameExtractor, + PostHogRouteFilter routeFilter = defaultPostHogRouteFilter}) { + return PosthogObserver( + nameExtractor: nameExtractor, routeFilter: routeFilter); } test('returns current route name', () { @@ -78,4 +77,39 @@ void main() { expect(fake.screenName, null); }); + + test('does not capture filtered routes', () { + // CustomOverlawRoute isn't a PageRoute + final overlayRoute = CustomOverlawRoute( + settings: const RouteSettings(name: 'Overlay Route'), + ); + + final sut = getSut(); + sut.didPush(overlayRoute, null); + + expect(fake.screenName, null); + }); + + test('allows overriding the route filter', () { + final overlayRoute = CustomOverlawRoute( + settings: const RouteSettings(name: 'Overlay Route'), + ); + + bool defaultPostHogRouteFilter(Route? route) => + route is PageRoute || route is OverlayRoute; + + final sut = getSut(routeFilter: defaultPostHogRouteFilter); + sut.didPush(overlayRoute, null); + + expect(fake.screenName, 'Overlay Route'); + }); +} + +class CustomOverlawRoute extends OverlayRoute { + CustomOverlawRoute({super.settings}); + + @override + Iterable createOverlayEntries() { + return []; + } }