assistive_touch_overlay

Assistive Touch-style draggable floating bubble overlay for Flutter (snap-to-edge + optional pulse), plus an optional Dio API inspector with a dark-theme UI. Co-authored and inspired by Safal Shrestha.

  • AssistiveTouchOverlay — reusable draggable bubble primitive (no Dio dependency)
  • API inspector — ApiInspectorService + ApiInspectorInterceptor + ApiInspectorController + ApiInspectorOverlay

Automatically disabled in release mode — zero overhead in production builds.

Preview

Install

dependencies:
  assistive_touch_overlay: ^0.0.5

API Inspector (Dio)

1. Create the service and controller

import 'package:assistive_touch_overlay/api_inspector.dart';
import 'package:flutter/material.dart';

final _navigatorKey = GlobalKey<NavigatorState>();

final _inspectorService = ApiInspectorService();
final _inspectorController = ApiInspectorController(
  service: _inspectorService,
  navigatorKey: _navigatorKey, // required when overlay is inside MaterialApp.builder
  overlayVisible: true,
);

2. Add the interceptor to Dio

import 'package:dio/dio.dart';
import 'package:assistive_touch_overlay/api_inspector.dart';

final dio = Dio()
  ..interceptors.add(ApiInspectorInterceptor(_inspectorService));

3. Render the overlay above your app

Pass navigatorKey to both MaterialApp and ApiInspectorController. The overlay is placed inside MaterialApp.builder, whose context sits above the Navigator — without the key the inspector cannot push routes.

import 'package:assistive_touch_overlay/api_inspector.dart';
import 'package:flutter/material.dart';

MaterialApp(
  navigatorKey: _navigatorKey,
  builder: (context, child) {
    return Stack(
      children: [
        child ?? const SizedBox.shrink(),
        ApiInspectorOverlay(controller: _inspectorController),
      ],
    );
  },
)

How it works

  1. Tap the floating bubble to start capture — it pulses red and shows a countdown + request count.
  2. Make your API calls normally.
  3. When the capture window ends (default 20 s) or you tap again to stop, the inspector automatically opens the captured call list.
  4. Tap any row to see full request/response detail, headers, body, cURL command, and stack trace.
  5. Tap the bubble again from the done state to re-open the list.

Bubble states

State Appearance
Idle Dark bubble with wifi icon
Recording Red pulsing bubble — seconds left + request count
Done (no errors) Green bubble — checkmark + total count
Done (client errors) Orange bubble — error count / total
Done (server errors) Red bubble — error count / total

ApiInspectorController options

Parameter Default Description
service new instance Shared ApiInspectorService store
navigatorKey null Required when overlay is in MaterialApp.builder
recordingDurationSeconds 20 How long a capture session lasts
overlayVisible false Whether the bubble starts visible
initialPosition Offset(-20, 200) Starting position of the bubble

Release mode

ApiInspectorOverlay returns an empty widget and ApiInspectorInterceptor becomes a transparent pass-through when kReleaseMode is true. No recording, no UI, no overhead — safe to leave wired up for production.


AssistiveTouchOverlay (standalone)

AssistiveTouchOverlay must be placed under a Stack (it uses AnimatedPositioned internally).

import 'package:assistive_touch_overlay/assistive_touch_overlay.dart';
import 'package:flutter/material.dart';

Stack(
  children: [
    const MyScreen(),
    AssistiveTouchOverlay(
      isPulsing: true,
      onTap: () => debugPrint('tapped'),
      builder: (context, pulseAnimation) {
        return ScaleTransition(
          scale: pulseAnimation,
          child: const MyBubbleWidget(),
        );
      },
    ),
  ],
)

AssistiveTouchOverlay options

Parameter Default Description
builder required Builds the bubble content; receives the pulse animation
onTap null Called on tap (not drag)
bubbleSize 60.0 Bubble diameter in logical pixels
edgePadding -20.0 Snap/clamp padding — negative = flush against edge (iOS-style)
initialPosition Offset(edgePadding, 200) Starting position
isPulsing false Starts/stops the scale pulse animation
snapDuration 300ms Duration of the snap-to-edge animation
snapCurve easeOutCubic Curve for the snap-to-edge animation
pulseDuration 900ms Duration of one pulse cycle
pulseBegin / pulseEnd 1.0 / 1.15 Scale range of the pulse

Example app

cd example
flutter pub get
flutter run

Notes

  • All storage is in-memory — records are cleared when capture restarts or reset() is called.
  • Stack traces are captured only when Dio provides them on DioException.
  • The package requires Dart >=3.1.0 and dio ^5.9.2.

Credits

A huge thank you to Safal Shrestha for his invaluable guidance, inspiration, and co-authoring contributions that helped bring this library to life!

Libraries

api_inspector
Dio API Inspector overlay library for Flutter. Co-authored and inspired by Safal Shrestha.
assistive_touch_overlay
Assistive Touch Overlay library for Flutter. Co-authored and inspired by Safal Shrestha.