Skip to content

Add New Widgets #57

@IM-001

Description

@IM-001

Hi,

thanks for the great libraries. I have been trying for a couple of days to add and remove widgets in the docking workspace, but the best that I had is the code below. So my problems are:

  • adding or removing widgets always resets the layout

  • at some point when removing and adding widgets I get errors like:
    Exception has occurred.
    ArgumentError (Invalid argument(s): DockingParentArea cannot have disposed child)

  • there are no example of how to add widgets dynamically (like with a + button or something), but when I try to use addItemOn() sometimes the layout root is null or disappeared, and addItemOnRoot() which seemed ideal doesn't seem to work at all.

I simply want to add a new tab on the right of the workspace when I add a new widget, I will dock it after. I could really use some help please, I've been stuck on this for 2-3 days. Thanks


import 'package:flutter/material.dart';
import 'package:docking/docking.dart';
import 'nav_bar.dart';

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

  @override
  State<DesktopWorkspace> createState() => _DesktopWorkspaceState();
}

/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
//// Layout and Widget Management, manages the docking layout and widget list (add/remove functionality)
/////////////////////////////////////////////////////////////////////////////////

class _DesktopWorkspaceState extends State<DesktopWorkspace> {

  late DockingLayout _layout; // The docking layout structure
  int _widgetCounter = 3; // Counter for generating unique widget IDs
  
  // List of tab items, each representing a widget in the tabbed area
  final List<DockingItem> _tabItems = [
    DockingItem(
      id: 'chart_1',
      name: 'Chart 1',
      widget: const PlaceholderWidget('Chart'),
    ),
    DockingItem(
      id: 'dom_2',
      name: 'DOM Ladder 2',
      widget: const PlaceholderWidget('DOM'),
    ),
  ];

  // Initialize the layout with a tabbed area
  @override
  void initState() {
    super.initState();
    _layout = DockingLayout(
      root: DockingTabs(_tabItems), // Use DockingTabs for tabbed layout
    );
  }

  // Add a new widget to the tabbed area
  void _addWidget(String type) {
    setState(() {
      _widgetCounter++;
      final newId = '${type.toLowerCase()}_$_widgetCounter';
      final newName = '$type $_widgetCounter';
      final newItem = DockingItem(
        id: newId,
        name: newName,
        widget: PlaceholderWidget(newName),
      );
      _tabItems.add(newItem);
      _layout = DockingLayout(root: DockingTabs([..._tabItems]));
    });
  }

  // Remove a widget from the tabbed area by ID
  void _removeWidget(String id) {
    setState(() {
      _tabItems.removeWhere((item) => item.id == id);
      _layout = DockingLayout(
        root: DockingTabs(_tabItems.isNotEmpty ? [..._tabItems] : []),
      );
    });
  }

  ///////////////////////////////////////////////////////////////////////////////////////
  /// Build Method, combines the navigation bar and themed docking area
  ///////////////////////////////////////////////////////////////////////////////////////

  @override
  Widget build(BuildContext context) {
    // Get theme data from the dedicated theme section
    final (tabTheme, dividerTheme) = _buildTheme();

    return Row(
      children: [
        // Navigation bar for adding/removing widgets
        LeftNavBar(
          onAddWidget: _addWidget,
          onRemoveWidget: _removeWidget,
        ),
        // Main docking area with themed tabs and dividers
        Expanded(
          child: TabbedViewTheme(
            data: tabTheme,
            child: MultiSplitViewTheme(
              data: dividerTheme,
              child: Docking(
                layout: _layout,
                onItemSelection: (item) {
                  print('Selected: ${item.name}');
                },
                onItemClose: (item) {
                  print('Closed: ${item.name}');
                },
              ),
            ),
          ),
        ),
      ],
    );
  }

  ///////////////////////////////////////////////////////////////////////////////////////
  /// Theme Setup
  ///////////////////////////////////////////////////////////////////////////////////////
  
  (TabbedViewThemeData, MultiSplitViewThemeData) _buildTheme() {
    // Tab Theme (TabbedViewThemeData)
    final tabTheme = TabbedViewThemeData();

    // Tabs Bar: The container holding all tabs
    tabTheme.tabsArea
      ..border = const Border(bottom: BorderSide(color: Colors.transparent, width: 0)) // No bottom border
      ..middleGap = 0; // No gap between tabs

    // Tab Appearance
    const borderRadius = BorderRadius.only(
      topLeft: Radius.circular(3),
      topRight: Radius.circular(3),
    );

    tabTheme.tab
      ..normalButtonColor = const Color.fromARGB(255, 225, 225, 225) // Unselected tab background
      ..padding = const EdgeInsets.symmetric(horizontal: 12, vertical: 4) // Tab size
      ..textStyle = const TextStyle(fontSize: 10, color: Colors.white) // Tab text (affects dragged tab)
      ..decoration = const BoxDecoration(
          color: Color.fromARGB(255, 25, 25, 25), // Unselected tab background (affects dragged tab)
          borderRadius: borderRadius,
        )
      ..selectedStatus.decoration = const BoxDecoration(
          color: Color.fromARGB(255, 34, 34, 34), // Selected tab background
          border: Border(
            bottom: BorderSide(color: Color.fromARGB(0, 0, 195, 255), width: 1), // Blue underline for selected tab
          ),
          borderRadius: borderRadius,
        )
      ;



    // Divider Theme (MultiSplitViewThemeData)
    final dividerTheme = MultiSplitViewThemeData(
      dividerThickness: 3,
      dividerPainter: DividerPainters.grooved1(
        backgroundColor: const Color.fromARGB(0, 85, 140, 186), // Transparent background
        color: Colors.transparent, // Transparent divider
        highlightedColor: Colors.blue, // Blue when dragged
      ),
    );

    return (tabTheme, dividerTheme);
  }
}

Metadata

Metadata

Assignees

Labels

questionFurther information is requested

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions