draggable_button_panel 1.0.0-dev.7 copy "draggable_button_panel: ^1.0.0-dev.7" to clipboard
draggable_button_panel: ^1.0.0-dev.7 copied to clipboard

The DraggableButtonPanel is a widget displaying a button with a movable panel of options. When clicked, it animates the panel's appearance. Customizable in color, size, and opacity. Implements Statefu [...]

Draggable Button Panel #

Vertical draggable and dockable panel (left/right) composed of rows of buttons. Each row (PanelButton) can either expand to display horizontal options, or function as a toggle (on/off) if it has no options.

Overview #

Key Features #

  • Vertical drag with auto-dock to the left or right edge of the screen.
  • Per-button animation: only the clicked row expands (others remain unchanged).
  • Options (OptionButton) slide out from behind the PanelButton.
  • Toggle mode for rows without options, with single or multiple selection.
  • onTogglesChanged event emits the list of active items (index + optional id).
  • Contextual rounded corners: parent rounded on the free side; only the first/last PanelButton have rounded corners; for options, only the furthest element is vertically rounded.
  • Visual feedback during drag accurately reflects the current state (orientation, colors, expansions, toggles).

Installation #

Add draggable_button_panel to your pubspec.yaml then run flutter pub get.

dependencies:
  draggable_button_panel: ^1.0.0-dev.3

Then import the package:

import 'package:draggable_button_panel/draggable_button_panel.dart';

Quick API #

ToggleSelectionMode #

  • single: only one toggleable button can be active at a time.
  • multiple: several can be active.

OptionButton #

Represents an option that unfolds horizontally.

  • icon (Icon) required
  • label (String?) optional (informal)
  • tooltip (String?) tooltip shown on hover (desktop/web) or long press (mobile)
  • onPressed (VoidCallback?)
  • color, backgroundColor (Color?)
  • width (double?) horizontal size (default 50)

PanelButton #

Main row of the panel; two uses:

  • with options: the row expands to show OptionButtons.
  • without options + toggleable: true: acts as an on/off button.

Main properties:

  • icon, label, tooltip, onPressed, color, backgroundColor
  • width, height (default 50)
  • options (List
  • toggleable (bool, default false)
  • initiallyToggled (bool, default false)
  • id (Object?) free identifier (int/String recommended) used in events.

DraggableButtonPanel #

  • children (List
  • width (double) square size of a row (also used as default for options)
  • buttonColor (Color) default color for buttons
  • collapseOpacity (double) opacity of inactive rows (0–1)
  • toggleMode (ToggleSelectionMode)
  • onTogglesChanged (ValueChanged<List
  • onPositionChanged (ValueChanged
  • onMenuExpand (ValueChanged
  • top (double) vertical position of the panel (mutable to persist position)
  • left (double) [DEPRECATED] horizontal position is no longer used for layout; side is determined by docking (left/right)

ToggleEntry #

Structure emitted in onTogglesChanged:

  • index (int): position of the row in children.
  • id (Object?): optional identifier provided on the PanelButton.

PanelPosition #

Value object used to read or set the panel position in one go:

  • isDockedLeft (bool): whether the panel is docked to the left.
  • top (double): vertical offset from the top.

Usage Example #

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

enum PanelBtnId { todo, add, menu }

class Demo extends StatelessWidget {
  const Demo({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.deepPurpleAccent,
      body: Center(
        child: DraggableButtonPanel(
          width: 50,
          buttonColor: Colors.blue,
          collapseOpacity: 0.5,
          toggleMode: ToggleSelectionMode.multiple,
          onTogglesChanged: (entries) {
            // entries: list of ToggleEntry (index + optional id)
            debugPrint(entries.toString());
          },
          children: [
            // 1) Row with options that unfold
            PanelButton(
              id: PanelBtnId.menu,
              icon: const Icon(Icons.menu_open_rounded, color: Colors.white),
              backgroundColor: Colors.redAccent,
              tooltip: 'Menu',
              options: const [
                OptionButton(icon: Icon(Icons.checklist), tooltip: 'My checklist'),
                OptionButton(icon: Icon(Icons.add), tooltip: 'Add'),
              ],
            ),
            // 2) Toggleable row (no options)
            const PanelButton(
              id: PanelBtnId.add,
              toggleable: true,
              initiallyToggled: false,
              icon: Icon(Icons.add, color: Colors.white),
              backgroundColor: Colors.green,
            ),
          ],
        ),
      ),
    );
  }
}

Integration Tips #

  • If you want to maintain the position between frequent rebuilds, use a GlobalKey<DraggableButtonPanelState> to read/write the panelPosition from the current state (or call setPanelPosition).
  • The visual width of the panel is fixed (based on the max width of the options) to avoid shifts when a row expands; only the clicked row is animated.
  • The rounded corners adjust automatically according to the docking side.

Persisting and Restoring Position #

You can listen to the position via onPositionChanged and reapply it later using the public methods of the state:

class MyPageState extends State<MyPage> {
  final panelKey = GlobalKey<DraggableButtonPanelState>();
  PanelPosition? savedPosition;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        DraggableButtonPanel(
          key: panelKey,
          onPositionChanged: (pos) {
            // Save (e.g. in your state, provider, prefs...)
            savedPosition = pos;
          },
          children: const [ /* ... */ ],
        ),
        Positioned(
          bottom: 24, left: 24,
          child: FilledButton(
            onPressed: () {
              final state = panelKey.currentState;
              if (state == null) return;
              // Restore the last known position/docking
              if (savedPosition != null) {
                state.panelPosition = savedPosition!;
              }
            },
            child: const Text('Restore position'),
          ),
        ),
      ],
    );
  }
}

Useful API in the state:

  • panelOffset -> Current Offset(left, top)
  • isDockedLeft -> bool indicating the side
  • panelPosition -> get/set the current PanelPosition (top + docking side)
  • setPanelPosition({double? top, bool? dockLeft, bool clampToScreen = true}) -> programmatic positioning (legacy-compatible).

License #

BSD 3-Clause License

Copyright (c) 2023–2025, KSɅRKΞV

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
8
likes
70
points
25
downloads

Publisher

unverified uploader

Weekly Downloads

The DraggableButtonPanel is a widget displaying a button with a movable panel of options. When clicked, it animates the panel's appearance. Customizable in color, size, and opacity. Implements StatefulWidget, AnimationController, AnimatedOpacity, Stack, and Positioned. Ideal for Flutter apps.

Homepage
Repository (GitHub)
View/report issues

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on draggable_button_panel