Skip to main content

debug_panel 0.1.0 copy "debug_panel: ^0.1.0" to clipboard
debug_panel: ^0.1.0 copied to clipboard

A reusable draggable floating ball with edge-snap spring animation, badge slots, boundary clamping and debug-only mode. Great for debug bubbles and floating menus.

debug_panel #

A reusable draggable floating ball with edge-snap animation for Flutter.

Drag the ball anywhere, release it and it springs to the nearest left/right edge. Perfect for debug bubbles, floating action menus and customer-service buttons — the interactive shell is generic, while all business content is injected by the caller.

Features #

  • 🎯 Draggable — move freely within the boundaries (clamped to screen / container edges).
  • 🪀 Edge snap — release and it glides to the nearest left/right edge with a bouncy spring animation.
  • 🛡️ Boundary clamping — keep the ball below a navbar and above a tab bar via topBoundary / bottomBoundary.
  • 🔘 Badge slots — plug in any badge widget (e.g. a red failure counter or a ⏸ pause icon) at topRightBadge / bottomLeftBadge.
  • 🎨 Configurable — size, colors, edge margin, initial position, snap duration via FloatBallConfig.
  • 🔌 Zero state-management deps — pure Flutter, based on ChangeNotifier. No GetX / Provider / Bloc required.
  • 🧪 Debug-only mode — optional onlyShowInDebug to hide in release builds.

Getting started #

Add the dependency:

dependencies:
  debug_panel: ^0.1.0

Then run flutter pub get.

Usage #

Place DebugFloatBall as a direct child of a Stack:

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

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  bool _paused = false;
  int _failedCount = 0;
  int _cacheCount = 3;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // ... your page content ...

          // 浮球:可拖动、松手吸附左右边缘
          DebugFloatBall(
            // 暂停态 → 外壳变橙
            paused: _paused,
            // 球心内容
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text('📊', style: TextStyle(fontSize: 16)),
                if (_cacheCount > 0)
                  Text(
                    '$_cacheCount',
                    style: const TextStyle(
                      fontSize: 10,
                      color: Colors.white,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
              ],
            ),
            // 右上角标(失败红点)
            topRightBadge: _failedCount > 0
                ? Container(
                    padding: const EdgeInsets.symmetric(
                        horizontal: 5, vertical: 1),
                    decoration: BoxDecoration(
                      color: Colors.red,
                      borderRadius: BorderRadius.circular(10),
                      border: Border.all(color: Colors.white, width: 1.5),
                    ),
                    child: Text(
                      _failedCount > 99 ? '99+' : '$_failedCount',
                      style: const TextStyle(
                        fontSize: 9,
                        color: Colors.white,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  )
                : null,
            // 左下角标(暂停 ⏸)
            bottomLeftBadge: _paused
                ? Container(
                    width: 18,
                    height: 18,
                    alignment: Alignment.center,
                    decoration: BoxDecoration(
                      color: Colors.orange,
                      shape: BoxShape.circle,
                      border: Border.all(color: Colors.white, width: 1.5),
                    ),
                    child:
                        const Icon(Icons.pause, size: 11, color: Colors.white),
                  )
                : null,
            // 行为回调
            onTap: () => debugPrint('ball tapped'),
            onDoubleTap: () => setState(() => _paused = !_paused),
            onLongPress: () => setState(() => _failedCount = 0),
            // 垂直边界(可选):限制在导航栏下、tabbar 上
            topBoundary: MediaQuery.of(context).padding.top +
                kToolbarHeight,
            bottomBoundary: MediaQuery.of(context).size.height -
                MediaQuery.of(context).padding.bottom -
                kBottomNavigationBarHeight,
          ),
        ],
      ),
    );
  }
}

For an external controller (e.g. to show/hide the ball from elsewhere):

final controller = FloatBallController(vsync: this); // 在 State 中创建

DebugFloatBall(controller: controller, ...);

// 其它地方
controller.hide();
controller.show();

API #

DebugFloatBall #

Parameter Type Description
controller FloatBallController? External controller; if omitted one is created internally.
paused bool Switch shell to disabledColor.
child Widget? Content inside the ball.
topRightBadge Widget? Badge at the top-right corner.
bottomLeftBadge Widget? Badge at the bottom-left corner.
topBoundary double? Upper drag boundary (default: top + status bar).
bottomBoundary double? Lower drag boundary (default: bottom - safe area).
onTap / onDoubleTap / onLongPress VoidCallback? Gesture callbacks.

FloatBallController #

Extends ChangeNotifier. Manages position, visibility and the snap animation.

Method / Getter Description
offset Current position (Offset, left/top).
visible Whether the ball is visible.
isSnapping Whether the snap animation is running.
show() / hide() Show or hide the ball.
setOffset(...) Jump to a position (clamped).
updateOffset(delta, ...) Apply a drag delta (clamped).
snapToEdge(...) Snap to the nearest left/right edge.

FloatBallConfig #

Field Default Description
size 44.0 Ball size (round).
edgeMargin 16.0 Gap to the left/right edge when snapped.
initialBottomMargin 120.0 Initial distance from the bottom.
color 0xCC4A90D9 Shell color.
disabledColor 0xCCF5A623 Shell color when paused.
snapDuration 520ms Snap animation duration.
onlyShowInDebug false Show only in debug builds.

Additional information #

The package is a pure Flutter (Dart) library — no native code, no state management dependency. See the example/ directory for a runnable demo. Report issues at the package repository.

1
likes
160
points
81
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A reusable draggable floating ball with edge-snap spring animation, badge slots, boundary clamping and debug-only mode. Great for debug bubbles and floating menus.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on debug_panel