xtal_dashboard 0.3.1 copy "xtal_dashboard: ^0.3.1" to clipboard
xtal_dashboard: ^0.3.1 copied to clipboard

A grid-based dashboard layout library with drag-and-drop reordering, resizing, and physics-based animations.

example/lib/main.dart

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

void main() {
  runApp(const DemoApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'xtal_dashboard Demo',
      theme: ThemeData.light(useMaterial3: true),
      debugShowCheckedModeBanner: false,
      home: const DemoPage(),
    );
  }
}

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

  @override
  State<DemoPage> createState() => _DemoPageState();
}

class _DemoPageState extends State<DemoPage> {
  bool _collisionEnabled = true;
  bool _editingEnabled = true;
  bool _shelfEnabled = true;
  bool _resetSizeOnShelf = false;
  int _frozenRows = 0;
  late List<DashboardItemData> _items;
  late List<DashboardItemData> _shelfItems;
  final ScrollController _scrollController = ScrollController();

  @override
  void initState() {
    super.initState();
    _items = _createInitialItems();
    _shelfItems = _createInitialShelfItems();
  }

  // ---- Initial shelf items ----

  static const _shelfItemConfigs = <(String, int, int, int, String, String)>[
    // (id, columnSpan, rowSpan, colorValue, title, description)
    (
      'S1',
      1,
      1,
      0xFFEF5350,
      'CPU',
      'Current load average across all cores is 2.4 with peak utilization at 87%.',
    ), // Red 400
    (
      'S2',
      2,
      1,
      0xFF7E57C2,
      'Recent Activity Log',
      'User admin@example.com deployed v3.2.1 to production at 14:32 UTC. Database migration completed successfully with 12 tables altered.',
    ), // Deep Purple 400
    (
      'S3',
      1,
      1,
      0xFF29B6F6,
      'System Performance Monitor',
      'Monitoring 48 services across 3 regions. All health checks passing. Memory usage trending upward — consider scaling.',
    ), // Light Blue 400
  ];

  List<DashboardItemData> _createInitialShelfItems() {
    return _shelfItemConfigs.map((config) {
      final (id, colSpan, rowSpan, colorValue, title, description) = config;
      final color = Color(colorValue);
      return DashboardItemData(
        gridItem: GridItem(
          id: id,
          column: 0,
          row: 0,
          columnSpan: colSpan,
          rowSpan: rowSpan,
        ),
        builder: (context, isBeingDragged) =>
            _buildCard(color, id, description),
        title: title,
      );
    }).toList();
  }

  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

  // ---- Initial grid layout (4 columns, 8 items, mixed spans) ----
  //
  //   Col:  0     1     2     3
  // Row 0: [ A  (2x2)   ] [ B (2x1)  ]
  // Row 1: [             ] [ C  ] [ D  ]
  // Row 2: [ E  ] [ F (2x1)  ] [ G  ]
  // Row 3: [ H (3x1)         ] [    ]

  static const _itemConfigs = <(String, int, int, int, int, int, String, String)>[
    // (id, column, row, columnSpan, rowSpan, colorValue, title, description)
    (
      'A',
      0,
      0,
      2,
      2,
      0xFF42A5F5,
      'Overview Dashboard',
      'System uptime: 99.97% over the last 30 days. Active users: 1,284 concurrent sessions. Total API calls today: 3.2M with an average response time of 142ms. No critical incidents reported in the past 72 hours.',
    ), // Blue 400
    (
      'B',
      2,
      0,
      2,
      1,
      0xFF26A69A,
      'Network',
      'Inbound traffic: 847 Mbps, Outbound: 1.2 Gbps. Packet loss rate is 0.003% across all edge nodes. DNS resolution latency averaging 12ms globally.',
    ), // Teal 400
    (
      'C',
      2,
      1,
      1,
      1,
      0xFFAB47BC,
      'Mem',
      'Heap: 4.2 GB / 8 GB used. GC pauses under 15ms. RSS stable at 5.1 GB.',
    ), // Purple 300
    (
      'D',
      3,
      1,
      1,
      1,
      0xFFEC407A,
      'IO',
      'Disk IOPS: 12.4k read, 8.7k write. Queue depth: 3. Avg latency: 0.8ms.',
    ), // Pink 300
    (
      'E',
      0,
      2,
      1,
      1,
      0xFFFFCA28,
      'Alerts',
      '3 warnings, 0 critical. Last alert: high CPU on node-7 at 09:41 UTC.',
    ), // Amber 400
    (
      'F',
      1,
      2,
      2,
      1,
      0xFFFFA726,
      'Request Throughput',
      'Current: 24.8k req/s (peak: 31.2k at 14:00 UTC). Error rate: 0.12%. Top endpoint: /api/v2/users accounting for 38% of total traffic. P95 latency: 89ms.',
    ), // Orange 400
    (
      'G',
      3,
      2,
      1,
      1,
      0xFF66BB6A,
      'Up',
      'All 48 services healthy. Last restart: 6d ago. Zero downtime deploys: 142.',
    ), // Green 400
    (
      'H',
      0,
      3,
      3,
      1,
      0xFF5C6BC0,
      'Latency Distribution (p50 / p95 / p99)',
      'p50: 23ms — p95: 89ms — p99: 214ms. Tail latency improved 18% after connection pool tuning last Thursday. Outliers traced to cold-start Lambda invocations in us-west-2.',
    ), // Indigo 300
  ];

  List<DashboardItemData> _createInitialItems() {
    return _itemConfigs.map((config) {
      final (id, col, row, colSpan, rowSpan, colorValue, title, description) =
          config;
      final color = Color(colorValue);
      return DashboardItemData(
        gridItem: GridItem(
          id: id,
          column: col,
          row: row,
          columnSpan: colSpan,
          rowSpan: rowSpan,
        ),
        builder: (context, isBeingDragged) =>
            _buildCard(color, id, description),
        title: title,
      );
    }).toList();
  }

  // ---- Card widget with visual patterns for FittedBox verification ----

  static Widget _buildCard(Color color, String label, String description) {
    final darker = Color.lerp(color, const Color(0xFF000000), 0.3)!;

    return DecoratedBox(
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [color, darker],
        ),
        borderRadius: BorderRadius.circular(12),
        border: Border.all(color: const Color(0x30FFFFFF), width: 1.5),
      ),
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              label,
              style: const TextStyle(
                color: Color(0xFFFFFFFF),
                fontSize: 22,
                fontWeight: FontWeight.bold,
                decoration: TextDecoration.none,
              ),
            ),
            const SizedBox(height: 4),
            const DecoratedBox(
              decoration: BoxDecoration(color: Color(0x30FFFFFF)),
              child: SizedBox(height: 1, width: double.infinity),
            ),
            const SizedBox(height: 8),
            Expanded(
              child: Text(
                description,
                style: const TextStyle(
                  color: Color(0xCCFFFFFF),
                  fontSize: 11,
                  fontWeight: FontWeight.w400,
                  height: 1.4,
                  decoration: TextDecoration.none,
                ),
                overflow: TextOverflow.fade,
              ),
            ),
          ],
        ),
      ),
    );
  }

  // ---- Custom ghost: blue border + glow ----

  static Widget _buildGhost(BuildContext context, Widget child, Size size) {
    return DecoratedBox(
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(12),
        border: Border.all(color: const Color(0xFF2196F3), width: 3),
        boxShadow: const [
          BoxShadow(color: Color(0x402196F3), blurRadius: 16, spreadRadius: 4),
        ],
      ),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(12),
        child: Opacity(opacity: 0.85, child: child),
      ),
    );
  }

  // ---- Dashboard widget (shared between frozen and non-frozen modes) ----

  Widget _buildDashboard() {
    return XtalDashboard(
      scrollController: _scrollController,
      columns: 4,
      items: _items,
      gap: 12,
      padding: const EdgeInsets.all(12),
      frozenRows: _frozenRows,
      collisionDetectionEnabled: _collisionEnabled,
      editingEnabled: _editingEnabled,
      animationConfig: const AnimationConfig(stiffness: 350, damping: 22),
      gestureConfig: const GestureConfig(
        resizeHandleSize: 28,
        autoScrollConfig: AutoScrollConfig(),
      ),
      resizeConstraints: const ResizeConstraints(
        maxColumnSpan: 4,
        maxRowSpan: 4,
      ),
      shelfConfig: _shelfEnabled
          ? ShelfConfig(
              position: ShelfPosition.right,
              showTitles: true,
              resetSizeOnShelf: _resetSizeOnShelf,
              borderRadius: BorderRadius.circular(12),
              backgroundColor: const Color(0x0D000000),
              dropTargetColor: const Color(0x262196F3),
              gapFromGrid: 16,
              hoverOpenDelay: Duration.zero,
              autoCloseDelay: const Duration(seconds: 3),
            )
          : null,
      initialShelfItems: _shelfItems,
      ghostBuilder: _buildGhost,
      onItemsChanged: (items) {
        setState(() => _items = items);
      },
      onShelfChanged: (shelfItems) {
        setState(() => _shelfItems = shelfItems);
      },
    );
  }

  // ---- Build ----

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('xtal_dashboard (shelf: ${_shelfItems.length})'),
        actions: [
          Padding(
            padding: const EdgeInsets.only(right: 8),
            child: Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                const Text('Edit'),
                Switch.adaptive(
                  value: _editingEnabled,
                  onChanged: (value) {
                    setState(() => _editingEnabled = value);
                  },
                ),
                const SizedBox(width: 8),
                const Text('Shelf'),
                Switch.adaptive(
                  value: _shelfEnabled,
                  onChanged: (value) {
                    setState(() => _shelfEnabled = value);
                  },
                ),
                const SizedBox(width: 8),
                const Text('Collision'),
                Switch.adaptive(
                  value: _collisionEnabled,
                  onChanged: (value) {
                    setState(() => _collisionEnabled = value);
                  },
                ),
                const SizedBox(width: 8),
                const Text('Reset'),
                Switch.adaptive(
                  value: _resetSizeOnShelf,
                  onChanged: (value) {
                    setState(() => _resetSizeOnShelf = value);
                  },
                ),
                const SizedBox(width: 8),
                const Text('Frozen'),
                Switch.adaptive(
                  value: _frozenRows > 0,
                  onChanged: (value) {
                    setState(() => _frozenRows = value ? 2 : 0);
                  },
                ),
              ],
            ),
          ),
        ],
      ),
      body: SafeArea(
        child: _frozenRows > 0
            ? Padding(
                padding: const EdgeInsets.all(12),
                child: _buildDashboard(),
              )
            : SingleChildScrollView(
                controller: _scrollController,
                padding: const EdgeInsets.all(12),
                child: _buildDashboard(),
              ),
      ),
    );
  }
}
0
likes
160
points
310
downloads

Publisher

unverified uploader

Weekly Downloads

A grid-based dashboard layout library with drag-and-drop reordering, resizing, and physics-based animations.

Repository (GitHub)
View/report issues

Topics

#dashboard #drag-and-drop #grid #layout #widget

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on xtal_dashboard