nrb 3.0.1 copy "nrb: ^3.0.1" to clipboard
nrb: ^3.0.1 copied to clipboard

A highly responsive Flutter table and report builder for complex nested headers, editable data grids, and premium Excel/PDF exports.

example/lib/main.dart

import 'dart:io' show Directory, File, Platform;
import 'dart:typed_data';

import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:nrb/nrb.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:device_info_plus/device_info_plus.dart';

void main() {
  runApp(
    const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: EnterpriseDashboardScreen(),
    ),
  );
}

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

  @override
  State<EnterpriseDashboardScreen> createState() => _EnterpriseDashboardScreenState();
}

class _EnterpriseDashboardScreenState extends State<EnterpriseDashboardScreen> {
  // --- BRAND COLORS ---
  final Color primaryBrandColor = const Color(0xFF0B7A3E);
  final Color secondaryAccent = const Color(0xFF1E88E5);
  final Color warningAccent = const Color(0xFFFFB300);

  // ===========================================================================
  // DATA FOR GRID 1: Financial Report (Complex Nested Headers)
  // ===========================================================================
  final _financialHeaders = [
    const HeaderCell(text: "Q4 Financials (USD)", span: 3, backgroundColor: Color(0xFF0B7A3E)),
    const HeaderCell(text: "Operations", span: 2, backgroundColor: Color(0xFF0B7A3E)),
  ];

  final _financialSubHeaders = [
    const SubHeaderCell(text: "Target Rev.", backgroundColor: Color(0xFF0B7A3E), foregroundColor: Colors.white),
    const SubHeaderCell(text: "Actual Rev.", backgroundColor: Color(0xFF0B7A3E), foregroundColor: Colors.white),
    const SubHeaderCell(text: "Margin %", backgroundColor: Color(0xFF0B7A3E), foregroundColor: Colors.white),
    const SubHeaderCell(text: "Active Clients", backgroundColor: Color(0xFF0B7A3E), foregroundColor: Colors.white),
    const SubHeaderCell(text: "SLA Uptime", backgroundColor: Color(0xFF0B7A3E), foregroundColor: Colors.white),
  ];

  final _financialLeftColumn = [
    const TextCell(itemContent: "North America", textAlignment: Alignment.centerLeft),
    const TextCell(itemContent: "Europe", textAlignment: Alignment.centerLeft),
    const TextCell(itemContent: "Asia Pacific", textAlignment: Alignment.centerLeft),
    const TextCell(itemContent: "Latin America", textAlignment: Alignment.centerLeft),
    const TextCell(itemContent: "Middle East", textAlignment: Alignment.centerLeft),
    const TextCell(itemContent: "Africa", textAlignment: Alignment.centerLeft),
    const TextCell(itemContent: "Australia", textAlignment: Alignment.centerLeft),
    const TextCell(itemContent: "Global Ops", textAlignment: Alignment.centerLeft),
  ];

  final _financialData = [
    [
      const TextCell(itemContent: "1200000", isAmount: true),
      const TextCell(itemContent: "1350000", isAmount: true),
      const TextCell(itemContent: "28%"),
      const TextCell(itemContent: "145"),
      const TextCell(itemContent: "99.99%"),
    ],
    [
      const TextCell(itemContent: "950000", isAmount: true),
      const TextCell(itemContent: "920000", isAmount: true),
      const TextCell(itemContent: "24%"),
      const TextCell(itemContent: "112"),
      const TextCell(itemContent: "99.95%"),
    ],
    [
      const TextCell(itemContent: "600000", isAmount: true),
      const TextCell(itemContent: "780000", isAmount: true),
      const TextCell(itemContent: "31%"),
      const TextCell(itemContent: "88"),
      const TextCell(itemContent: "99.90%"),
    ],
    [
      const TextCell(itemContent: "450000", isAmount: true),
      const TextCell(itemContent: "410000", isAmount: true),
      const TextCell(itemContent: "18%"),
      const TextCell(itemContent: "55"),
      const TextCell(itemContent: "99.80%"),
    ],
    [
      const TextCell(itemContent: "550000", isAmount: true),
      const TextCell(itemContent: "620000", isAmount: true),
      const TextCell(itemContent: "22%"),
      const TextCell(itemContent: "70"),
      const TextCell(itemContent: "99.99%"),
    ],
    [
      const TextCell(itemContent: "250000", isAmount: true),
      const TextCell(itemContent: "280000", isAmount: true),
      const TextCell(itemContent: "15%"),
      const TextCell(itemContent: "30"),
      const TextCell(itemContent: "99.50%"),
    ],
    [
      const TextCell(itemContent: "320000", isAmount: true),
      const TextCell(itemContent: "350000", isAmount: true),
      const TextCell(itemContent: "26%"),
      const TextCell(itemContent: "42"),
      const TextCell(itemContent: "99.99%"),
    ],
    [
      const TextCell(itemContent: "4320000", isAmount: true),
      const TextCell(itemContent: "4710000", isAmount: true),
      const TextCell(itemContent: "25%"),
      const TextCell(itemContent: "542"),
      const TextCell(itemContent: "99.95%"),
    ],
  ];

  // ===========================================================================
  // DATA FOR GRID 2: Employee Performance (Editable Data Grid)
  // ===========================================================================
  final _employeeHeaders = [
    const HeaderCell(text: "Core Metrics", span: 2, backgroundColor: Color(0xFF1E88E5)),
    const HeaderCell(text: "Manager Review (Editable)", span: 1, backgroundColor: Color(0xFF1E88E5)),
  ];

  final _employeeSubHeaders = [
    const SubHeaderCell(text: "Tasks Completed", backgroundColor: Color(0xFF1E88E5), foregroundColor: Colors.white),
    const SubHeaderCell(text: "Efficiency Score", backgroundColor: Color(0xFF1E88E5), foregroundColor: Colors.white),
    const SubHeaderCell(text: "Bonus Allocation", backgroundColor: Color(0xFF1E88E5), foregroundColor: Colors.white),
  ];

  final _employeeLeftColumn = [
    const TextCell(itemContent: "Alice Smith", textAlignment: Alignment.centerLeft),
    const TextCell(itemContent: "Bob Jones", textAlignment: Alignment.centerLeft),
    const TextCell(itemContent: "Charlie Brown", textAlignment: Alignment.centerLeft),
    const TextCell(itemContent: "Diana Prince", textAlignment: Alignment.centerLeft),
    const TextCell(itemContent: "Evan Wright", textAlignment: Alignment.centerLeft),
    const TextCell(itemContent: "Fiona Gallagher", textAlignment: Alignment.centerLeft),
  ];

  final _employeeData = [
    [
      const TextCell(itemContent: "124"),
      const TextCell(itemContent: "94%"),
      TextFieldCell(initialValue: "\$5,000", keyboardType: TextInputType.number),
    ],
    [
      const TextCell(itemContent: "98"),
      const TextCell(itemContent: "88%"),
      TextFieldCell(initialValue: "\$3,200", keyboardType: TextInputType.number),
    ],
    [
      const TextCell(itemContent: "145"),
      const TextCell(itemContent: "98%"),
      TextFieldCell(initialValue: "\$7,500", keyboardType: TextInputType.number),
    ],
    [
      const TextCell(itemContent: "110"),
      const TextCell(itemContent: "91%"),
      TextFieldCell(initialValue: "\$4,000", keyboardType: TextInputType.number),
    ],
    [
      const TextCell(itemContent: "85"),
      const TextCell(itemContent: "82%"),
      TextFieldCell(initialValue: "\$2,000", keyboardType: TextInputType.number),
    ],
    [
      const TextCell(itemContent: "132"),
      const TextCell(itemContent: "96%"),
      TextFieldCell(initialValue: "\$6,000", keyboardType: TextInputType.number),
    ],
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF5F7FA), // Light enterprise gray background
      appBar: AppBar(
        title: const Text(
          'NRB Enterprise Dashboard',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        backgroundColor: Colors.white,
        foregroundColor: Colors.black87,
        elevation: 1,
        centerTitle: false,
      ),
      body: LayoutBuilder(
        builder: (context, constraints) {
          final bool isDesktop = constraints.maxWidth > 1000;

          return SingleChildScrollView(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: isDesktop ? CrossAxisAlignment.start : CrossAxisAlignment.center,
              children: [

                // --- TOP ROW: KPI SUMMARY & GAUGES ---
                if (isDesktop)
                  Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      // Circular Charts (Donut + Gauge)
                      Expanded(
                        flex: 5,
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            const _SectionTitle("Key Performance Indicators"),
                            const SizedBox(height: 12),
                            Wrap(
                              spacing: 16.0,
                              runSpacing: 16.0,
                              children: [
                                _buildKpiCard("Project Completion", 75.0, primaryBrandColor),
                                _buildKpiCard("Server Uptime", 99.9, secondaryAccent),
                                _buildGaugeCard("Collection Ach%", 88.0),
                              ],
                            ),
                          ],
                        ),
                      ),
                      // Solid Metric Cards
                      Expanded(
                        flex: 6,
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            const _SectionTitle("Today's Logistics"),
                            const SizedBox(height: 12),
                            Wrap(
                              spacing: 16.0,
                              runSpacing: 16.0,
                              children: const [
                                NrbMetricCard(
                                  title: "Made",
                                  value: "142",
                                  backgroundColor: Color(0xFF1976D2), // Blue
                                ),
                                NrbMetricCard(
                                  title: "Pushed",
                                  value: "89",
                                  backgroundColor: Color(0xFF455A64), // Blue Grey
                                ),
                                NrbMetricCard(
                                  title: "Failed",
                                  value: "56",
                                  backgroundColor: Color(0xFF00796B), // Teal
                                ),
                              ],
                            ),
                          ],
                        ),
                      ),
                    ],
                  )
                else
                // Mobile Layout for Top Row
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      const _SectionTitle("Today's Logistics"),
                      const SizedBox(height: 12),
                      Wrap(
                        spacing: 12.0,
                        runSpacing: 12.0,
                        alignment: WrapAlignment.center,
                        children: const [
                          NrbMetricCard(title: "Scheduled", value: "142", backgroundColor: Color(0xFF1976D2)),
                          NrbMetricCard(title: "Created", value: "89", backgroundColor: Color(0xFF455A64)),
                          NrbMetricCard(title: "Synced", value: "56", backgroundColor: Color(0xFF00796B)),
                          NrbMetricCard(title: "Backlog", value: "12", backgroundColor: Color(0xFFD32F2F)),
                        ],
                      ),
                      const SizedBox(height: 32),
                      const _SectionTitle("Key Performance Indicators"),
                      const SizedBox(height: 12),
                      Wrap(
                        spacing: 16.0,
                        runSpacing: 16.0,
                        alignment: WrapAlignment.center,
                        children: [
                          _buildKpiCard("Project Completion", 75.0, primaryBrandColor),
                          _buildKpiCard("Server Uptime", 99.9, secondaryAccent),
                          _buildGaugeCard("Collection Ach%", 88.0),
                        ],
                      ),
                    ],
                  ),

                const SizedBox(height: 32),

                // --- SECTION 2: RESPONSIVE DASHBOARD LAYOUT (Charts + Grids) ---
                if (isDesktop)
                  Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      // Left Column: Analytics Charts
                      Expanded(
                        flex: 4,
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            const _SectionTitle("Growth & Revenue Trends"),
                            const SizedBox(height: 12),
                            // Simple Bar Chart
                            _buildChartCard(
                              title: "Monthly Revenue (Last 6 Months)",
                              child: const NrbBarChart(
                                data: [120, 150, 180, 220, 300, 280],
                                labels: ["Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
                                height: 180,
                                width: double.infinity,
                                barColor: Color(0xFF0B7A3E),
                                barSpacing: 12.0,
                                barRadius: 6.0,
                              ),
                            ),
                            const SizedBox(height: 16),
                            // Simple Line Chart
                            _buildChartCard(
                              title: "Active Users Trend (10k+)",
                              child: const NrbLineChart(
                                data: [50, 55, 60, 80, 120, 110, 140, 180, 175, 210],
                                xAxisLabels: ["M1", "M2", "M3", "M4", "M5", "M6", "M7", "M8", "M9", "M10"],
                                height: 180,
                                width: double.infinity,
                                lineColor: Color(0xFF1E88E5),
                                showGrid: true,
                                lineWidth: 3.0,
                              ),
                            ),
                            const SizedBox(height: 16),
                            // Advanced Multi-Line Chart (Day Mode Card)
                            Container(
                              padding: const EdgeInsets.all(16),
                              decoration: BoxDecoration(
                                color: Colors.white,
                                borderRadius: BorderRadius.circular(12),
                                boxShadow: [
                                  BoxShadow(
                                    color: Colors.black.withValues(alpha: 0.05),
                                    blurRadius: 10,
                                    offset: const Offset(0, 4),
                                  ),
                                ],
                              ),
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  const Text(
                                    "Averages (Last 7 Days)",
                                    style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87),
                                  ),
                                  const SizedBox(height: 24),
                                  NrbMultiLineChart(
                                    series: [
                                      NrbLineSeries(
                                        data: [64000, 60000, 55000, 50000, 45000, 40000, 35000],
                                        color: Colors.blueAccent,
                                        label: "Qty",
                                      ),
                                      NrbLineSeries(
                                        data: [33000, 31000, 29000, 27000, 24000, 22000, 19000],
                                        color: Colors.greenAccent,
                                        label: "Lines",
                                      ),
                                    ],
                                    xAxisLabels: const ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
                                    height: 220,
                                    width: double.infinity,
                                  ),
                                ],
                              ),
                            ),
                            const SizedBox(height: 16),
                            // Row for Pie Chart and Scatter Plot
                            Row(
                              children: [
                                Expanded(
                                  child: _buildChartCard(
                                    title: "Sales Distribution",
                                    child: Center(
                                      child: NrbPieChart(
                                        size: 160,
                                        slices: [
                                          NrbPieSlice(value: 45, color: const Color(0xFF5C6BC0), label: "Lighting"),
                                          NrbPieSlice(value: 30, color: const Color(0xFF26A69A), label: "Accessories"),
                                          NrbPieSlice(value: 25, color: const Color(0xFFFFA726), label: "Others"),
                                        ],
                                      ),
                                    ),
                                  ),
                                ),
                                const SizedBox(width: 16),
                                Expanded(
                                  child: _buildChartCard(
                                    title: "Defect Correlation",
                                    child: NrbScatterPlot(
                                      height: 160,
                                      points: [
                                        NrbScatterPoint(1, 10), NrbScatterPoint(2, 12),
                                        NrbScatterPoint(3, 15), NrbScatterPoint(4, 14),
                                        NrbScatterPoint(5, 20), NrbScatterPoint(6, 18),
                                        NrbScatterPoint(7, 25), NrbScatterPoint(8, 22),
                                        NrbScatterPoint(9, 30),
                                      ],
                                      pointColor: Colors.deepPurpleAccent,
                                    ),
                                  ),
                                ),
                              ],
                            ),
                            const SizedBox(height: 16),
                            // Histogram
                            _buildChartCard(
                              title: "Age Distribution of Users",
                              child: NrbHistogram(
                                height: 160,
                                color: Colors.teal.shade400,
                                bins: [
                                  NrbHistogramBin(label: "18-24", frequency: 150),
                                  NrbHistogramBin(label: "25-34", frequency: 320),
                                  NrbHistogramBin(label: "35-44", frequency: 280),
                                  NrbHistogramBin(label: "45-54", frequency: 190),
                                  NrbHistogramBin(label: "55+", frequency: 90),
                                ],
                              ),
                            ),
                          ],
                        ),
                      ),
                      const SizedBox(width: 24),

                      // Right Column: Data Grids
                      Expanded(
                        flex: 6,
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            const _SectionTitle("Regional Financial Report (Exportable)"),
                            const SizedBox(height: 12),
                            _buildGridContainer(
                              height: 380,
                              child: ReportMaker(
                                headers: _financialHeaders,
                                subHeaders: _financialSubHeaders,
                                leftColumn: _financialLeftColumn,
                                tableData: _financialData,
                                stickyHeaderLabel: "Region",
                                stickyHeaderBackgroundColor: primaryBrandColor,
                                stickyHeaderForegroundColor: Colors.white,
                                packageName: "com.inl.testapp",
                                apiKey: "fe2b22a6-fd19-4466-b2fa-ff7262a5993a",
                                enableDownload: true,
                                showDownloadFloatingButton: true,
                                reportName: "Financial_Report",
                                onDownloadCompleted: _handleDownloadSuccess,
                              ),
                            ),
                            const SizedBox(height: 24),
                            const _SectionTitle("Employee Performance (Editable Form)"),
                            const SizedBox(height: 12),
                            _buildGridContainer(
                              height: 300,
                              child: ReportMaker(
                                headers: _employeeHeaders,
                                subHeaders: _employeeSubHeaders,
                                leftColumn: _employeeLeftColumn,
                                tableData: _employeeData,
                                stickyHeaderLabel: "Employee",
                                stickyHeaderBackgroundColor: secondaryAccent,
                                stickyHeaderForegroundColor: Colors.white,
                                enableDownload: false,
                                showDownloadFloatingButton: false,
                              ),
                            ),
                          ],
                        ),
                      ),
                    ],
                  )
                else
                // Mobile Layout for Charts and Grids
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      const _SectionTitle("Growth & Revenue Trends"),
                      const SizedBox(height: 12),
                      _buildChartCard(
                        title: "Monthly Revenue",
                        child: const NrbBarChart(
                          data: [120, 150, 180, 220, 300, 280],
                          labels: ["Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
                          height: 150,
                          width: double.infinity,
                          barColor: Color(0xFF0B7A3E),
                        ),
                      ),
                      const SizedBox(height: 16),
                      _buildChartCard(
                        title: "Active Users Trend",
                        child: const NrbLineChart(
                          data: [50, 55, 60, 80, 120, 110, 140, 180, 175, 210],
                          height: 150,
                          width: double.infinity,
                          showGrid: true,
                          lineColor: Color(0xFF1E88E5),
                        ),
                      ),
                      const SizedBox(height: 16),
                      // Multi-line chart for mobile (Day Mode Card)
                      Container(
                        padding: const EdgeInsets.all(16),
                        decoration: BoxDecoration(
                          color: Colors.white,
                          borderRadius: BorderRadius.circular(12),
                          boxShadow: [
                            BoxShadow(
                              color: Colors.black.withValues(alpha: 0.05),
                              blurRadius: 10,
                              offset: const Offset(0, 4),
                            ),
                          ],
                        ),
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: [
                            const Text(
                              "Averages (Last 7 Days)",
                              style: TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87),
                            ),
                            const SizedBox(height: 24),
                            NrbMultiLineChart(
                              series: [
                                NrbLineSeries(
                                  data: [64000, 60000, 55000, 50000, 45000, 40000, 35000],
                                  color: Colors.blueAccent,
                                  label: "Qty",
                                ),
                                NrbLineSeries(
                                  data: [33000, 31000, 29000, 27000, 24000, 22000, 19000],
                                  color: Colors.greenAccent,
                                  label: "Lines",
                                ),
                              ],
                              xAxisLabels: const ["M", "T", "W", "T", "F", "S", "S"],
                              height: 180,
                              width: double.infinity,
                            ),
                          ],
                        ),
                      ),
                      const SizedBox(height: 16),
                      _buildChartCard(
                        title: "Sales Distribution",
                        child: Center(
                          child: NrbPieChart(
                            size: 160,
                            slices: [
                              NrbPieSlice(value: 45, color: const Color(0xFF5C6BC0), label: "Lighting"),
                              NrbPieSlice(value: 30, color: const Color(0xFF26A69A), label: "Accessories"),
                              NrbPieSlice(value: 25, color: const Color(0xFFFFA726), label: "Others"),
                            ],
                          ),
                        ),
                      ),
                      const SizedBox(height: 16),
                      _buildChartCard(
                        title: "Defect Correlation",
                        child: NrbScatterPlot(
                          height: 180,
                          points: [
                            NrbScatterPoint(1, 10), NrbScatterPoint(2, 12),
                            NrbScatterPoint(3, 15), NrbScatterPoint(4, 14),
                            NrbScatterPoint(5, 20), NrbScatterPoint(6, 18),
                            NrbScatterPoint(7, 25), NrbScatterPoint(8, 22),
                            NrbScatterPoint(9, 30),
                          ],
                          pointColor: Colors.deepPurpleAccent,
                        ),
                      ),
                      const SizedBox(height: 16),
                      _buildChartCard(
                        title: "Age Distribution of Users",
                        child: NrbHistogram(
                          height: 160,
                          color: Colors.teal.shade400,
                          bins: [
                            NrbHistogramBin(label: "18-24", frequency: 150),
                            NrbHistogramBin(label: "25-34", frequency: 320),
                            NrbHistogramBin(label: "35-44", frequency: 280),
                            NrbHistogramBin(label: "45-54", frequency: 190),
                            NrbHistogramBin(label: "55+", frequency: 90),
                          ],
                        ),
                      ),

                      const SizedBox(height: 32),

                      const Align(
                        alignment: Alignment.centerLeft,
                        child: _SectionTitle("Regional Financial Report (Exportable)"),
                      ),
                      const SizedBox(height: 12),
                      _buildGridContainer(
                        height: 400,
                        child: ReportMaker(
                          headers: _financialHeaders,
                          subHeaders: _financialSubHeaders,
                          leftColumn: _financialLeftColumn,
                          tableData: _financialData,
                          stickyHeaderLabel: "Region",
                          stickyHeaderBackgroundColor: primaryBrandColor,
                          stickyHeaderForegroundColor: Colors.white,
                          packageName: "com.inl.testapp",
                          apiKey: "fe2b22a6-fd19-4466-b2fa-ff7262a5993a",
                          enableDownload: true,
                          showDownloadFloatingButton: true,
                          reportName: "Financial_Report",
                          onDownloadCompleted: _handleDownloadSuccess,
                        ),
                      ),
                      const SizedBox(height: 24),
                      const Align(
                        alignment: Alignment.centerLeft,
                        child: _SectionTitle("Employee Performance (Editable Form)"),
                      ),
                      const SizedBox(height: 12),
                      _buildGridContainer(
                        height: 350,
                        child: ReportMaker(
                          headers: _employeeHeaders,
                          subHeaders: _employeeSubHeaders,
                          leftColumn: _employeeLeftColumn,
                          tableData: _employeeData,
                          stickyHeaderLabel: "Employee",
                          stickyHeaderBackgroundColor: secondaryAccent,
                          stickyHeaderForegroundColor: Colors.white,
                          enableDownload: false,
                        ),
                      ),
                    ],
                  ),

                const SizedBox(height: 40), // Bottom padding
              ],
            ),
          );
        },
      ),
    );
  }

  // --- HELPER WIDGETS ---

  Widget _buildGridContainer({required double height, required Widget child}) {
    return Container(
      height: height,
      width: double.infinity, // Ensure it takes full width available
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withValues(alpha:0.05),
            blurRadius: 10,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      clipBehavior: Clip.antiAlias,
      child: child,
    );
  }

  Widget _buildGaugeCard(String title, double percentage) {
    return Container(
      width: 140, // Matches KPI Card
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withValues(alpha: 0.05),
            blurRadius: 10,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          SizedBox(
            height: 70, // Matches Donut Chart Height
            child: Center(
              child: NrbGaugeChart(
                value: percentage,
                size: 100, // Scaled down to fit
                strokeWidth: 12.0, // Thinner stroke for smaller size
                showLabels: false, // Turn off outer labels so it fits the small card
                segments: [
                  NrbGaugeSegment(startValue: 0, endValue: 50, color: const Color(0xFFF0716A)),
                  NrbGaugeSegment(startValue: 50, endValue: 75, color: const Color(0xFFFFCA3A)),
                  NrbGaugeSegment(startValue: 75, endValue: 100, color: const Color(0xFF67B28C)),
                ],
                centerContent: Text(
                  "${percentage.toStringAsFixed(0)}%",
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 14,
                    color: Colors.grey.shade800,
                  ),
                ),
              ),
            ),
          ),
          const SizedBox(height: 16),
          Text(
            title,
            textAlign: TextAlign.center,
            style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Colors.black54),
          ),
        ],
      ),
    );
  }

  Widget _buildKpiCard(String title, double value, Color color) {
    return Container(
      width: 140, // Slightly narrower to fit 3 nicely
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withValues(alpha:0.05),
            blurRadius: 10,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          NrbDonutChart(
            value: value,
            size: 70,
            progressColor: color,
            trackColor: Colors.grey.shade200,
            strokeWidth: 8,
            centerContent: Text(
              "${value.toStringAsFixed(1)}%",
              style: TextStyle(
                fontWeight: FontWeight.bold,
                fontSize: 14,
                color: Colors.grey.shade800,
              ),
            ),
          ),
          const SizedBox(height: 16),
          Text(
            title,
            textAlign: TextAlign.center,
            style: const TextStyle(fontSize: 12, fontWeight: FontWeight.w600, color: Colors.black54),
          ),
        ],
      ),
    );
  }

  Widget _buildChartCard({required String title, required Widget child}) {
    return Container(
      width: double.infinity, // Charts span their column width
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withValues(alpha:0.05),
            blurRadius: 10,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        mainAxisSize: MainAxisSize.min,
        children: [
          Text(
            title,
            style: const TextStyle(fontSize: 14, fontWeight: FontWeight.bold, color: Colors.black87),
          ),
          const SizedBox(height: 24),
          child,
        ],
      ),
    );
  }

  // --- DOWNLOAD HANDLER ---
  Future<void> _handleDownloadSuccess(Uint8List bytes, String fileName) async {
    if (!kIsWeb) {
      bool hasPermission = false;

      if (Platform.isAndroid) {
        final deviceInfo = DeviceInfoPlugin();
        final androidInfo = await deviceInfo.androidInfo;

        if (androidInfo.version.sdkInt >= 33) {
          hasPermission = true;
        } else {
          var status = await Permission.storage.status;
          if (!status.isGranted) {
            status = await Permission.storage.request();
          }
          hasPermission = status.isGranted;
        }
      } else {
        hasPermission = true;
      }

      if (hasPermission) {
        try {
          final directory = Directory('/storage/emulated/0/Download');
          if (!await directory.exists()) {
            await directory.create(recursive: true);
          }

          final file = File('${directory.path}/$fileName');
          await file.writeAsBytes(bytes);

          if (mounted) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                content: Text('Report saved to: ${file.path}'),
                backgroundColor: Colors.green,
                behavior: SnackBarBehavior.floating,
              ),
            );
          }
        } catch (e) {
          if (mounted) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(content: Text('Save failed: $e'), backgroundColor: Colors.red),
            );
          }
        }
      }
    }
  }
}

class _SectionTitle extends StatelessWidget {
  final String title;
  const _SectionTitle(this.title);

  @override
  Widget build(BuildContext context) {
    return Text(
      title,
      style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black87),
    );
  }
}
8
likes
120
points
772
downloads

Documentation

API reference

Publisher

verified publisherinnovatenestlabs.com

Weekly Downloads

A highly responsive Flutter table and report builder for complex nested headers, editable data grids, and premium Excel/PDF exports.

License

unknown (license)

Dependencies

cross_file, flutter, share_plus, web

More

Packages that depend on nrb