nrb 2.0.3
nrb: ^2.0.3 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 'package:flutter/foundation.dart' show kIsWeb, kDebugMode;
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(
MaterialApp(
debugShowCheckedModeBanner: false,
home: InnovateNestReportScreen(),
),
);
}
class InnovateNestReportScreen extends StatelessWidget {
InnovateNestReportScreen({super.key});
final _headers = [
HeaderCell(
text: "Financials (USD)",
span: 3,
backgroundColor: const Color(0xFF0B7A3E), // Brand Accent
),
HeaderCell(
text: "Operations",
span: 2,
backgroundColor: const Color(0xFF0B7A3E),
),
];
final _subHeaders = [
SubHeaderCell(text: "Gross Rev.", backgroundColor: const Color(0xFF0B7A3E), foregroundColor: Colors.white,),
SubHeaderCell(text: "Op. Expenses", backgroundColor: const Color(0xFF0B7A3E), foregroundColor: Colors.white,),
SubHeaderCell(text: "Net Profit", backgroundColor: const Color(0xFF0B7A3E), foregroundColor: Colors.white,),
SubHeaderCell(text: "Active Clients", backgroundColor: const Color(0xFF0B7A3E), foregroundColor: Colors.white,),
SubHeaderCell(text: "SLA Uptime", backgroundColor: const Color(0xFF0B7A3E), foregroundColor: Colors.white,),
];
final _leftColumns = [
TextCell(
itemContent: "Custom Web Applications",
textAlignment: Alignment.centerLeft,
),
TextCell(
itemContent: "Mobile App Development",
textAlignment: Alignment.centerLeft,
),
TextCell(
itemContent: "Enterprise ERP Solutions",
textAlignment: Alignment.centerLeft,
),
TextCell(
itemContent: "Cloud Hosting & DevOps",
textAlignment: Alignment.centerLeft,
),
TextCell(
itemContent: "AI & Machine Learning",
textAlignment: Alignment.centerLeft,
),
TextCell(
itemContent: "UI/UX Design Services",
textAlignment: Alignment.centerLeft,
),
TextCell(
itemContent: "E-Commerce Platforms",
textAlignment: Alignment.centerLeft,
),
TextCell(
itemContent: "API Development",
textAlignment: Alignment.centerLeft,
),
TextCell(
itemContent: "Cybersecurity Audits",
textAlignment: Alignment.centerLeft,
),
TextCell(
itemContent: "Data Analytics & BI",
textAlignment: Alignment.centerLeft,
),
TextCell(itemContent: "IT Consulting", textAlignment: Alignment.centerLeft),
TextCell(
itemContent: "Maintenance & Support",
textAlignment: Alignment.centerLeft,
),
];
final _tableData = [
// Web Apps
[
TextCell(itemContent: "450,000", isAmount: true),
TextCell(itemContent: "180,000", isAmount: true),
TextCell(itemContent: "270,000", isAmount: true),
TextCell(itemContent: "42"),
TextCell(itemContent: "99.9%"),
],
// Mobile Apps
[
TextCell(itemContent: "385,500", isAmount: true),
TextCell(itemContent: "155,000", isAmount: true),
TextCell(itemContent: "230,500", isAmount: true),
TextCell(itemContent: "28"),
TextCell(itemContent: "99.8%"),
],
// ERP
[
TextCell(itemContent: "850,000", isAmount: true),
TextCell(itemContent: "320,000", isAmount: true),
TextCell(itemContent: "530,000", isAmount: true),
TextCell(itemContent: "8"),
TextCell(itemContent: "99.99%"),
],
// Cloud
[
TextCell(itemContent: "210,000", isAmount: true),
TextCell(itemContent: "95,000", isAmount: true),
TextCell(itemContent: "115,000", isAmount: true),
TextCell(itemContent: "115"),
TextCell(itemContent: "99.99%"),
],
// AI & ML
[
TextCell(itemContent: "540,200", isAmount: true),
TextCell(itemContent: "210,000", isAmount: true),
TextCell(itemContent: "330,200", isAmount: true),
TextCell(itemContent: "14"),
TextCell(itemContent: "99.5%"),
],
// UI/UX
[
TextCell(itemContent: "125,000", isAmount: true),
TextCell(itemContent: "45,000", isAmount: true),
TextCell(itemContent: "80,000", isAmount: true),
TextCell(itemContent: "55"),
TextCell(itemContent: "N/A"),
],
// E-Commerce
[
TextCell(itemContent: "310,000", isAmount: true),
TextCell(itemContent: "110,000", isAmount: true),
TextCell(itemContent: "200,000", isAmount: true),
TextCell(itemContent: "34"),
TextCell(itemContent: "99.9%"),
],
// API Dev
[
TextCell(itemContent: "195,000", isAmount: true),
TextCell(itemContent: "65,000", isAmount: true),
TextCell(itemContent: "130,000", isAmount: true),
TextCell(itemContent: "22"),
TextCell(itemContent: "99.95%"),
],
// Cybersec
[
TextCell(itemContent: "280,000", isAmount: true),
TextCell(itemContent: "90,000", isAmount: true),
TextCell(itemContent: "190,000", isAmount: true),
TextCell(itemContent: "19"),
TextCell(itemContent: "100%"),
],
// Data Analytics
[
TextCell(itemContent: "415,000", isAmount: true),
TextCell(itemContent: "140,000", isAmount: true),
TextCell(itemContent: "275,000", isAmount: true),
TextCell(itemContent: "26"),
TextCell(itemContent: "99.8%"),
],
// IT Consulting
[
TextCell(itemContent: "150,000", isAmount: true),
TextCell(itemContent: "30,000", isAmount: true),
TextCell(itemContent: "120,000", isAmount: true),
TextCell(itemContent: "45"),
TextCell(itemContent: "N/A"),
],
// Maintenance
[
TextCell(itemContent: "290,000", isAmount: true),
TextCell(itemContent: "85,000", isAmount: true),
TextCell(itemContent: "205,000", isAmount: true),
TextCell(itemContent: "88"),
TextCell(itemContent: "99.9%"),
],
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Innovate Nest Labs - Financial Report Q4'),
backgroundColor: Colors.white,
foregroundColor: Colors.black87,
elevation: 2,
),
body: ReportMaker(
// --- REPORT STRUCTURE ---
// --- 1. HEADERS ---
headers: _headers,
// --- 2. SUB-HEADERS ---
subHeaders: _subHeaders,
// --- 3. LEFT STICKY COLUMNS ---
leftColumn: _leftColumns,
// --- 4. TABLE DATA ---
tableData: _tableData,
// --- LEFT top ---
stickyHeaderLabel: "Service / Division",
stickyHeaderBackgroundColor: const Color(0xFF0B7A3E), // Brand Accent
// Brand Accent
stickyHeaderForegroundColor: Colors.white,
// --- FOR DOWNLOAD FEATURES ---
packageName: "com.innovatenestlabs.demoapp", // For free demo
apiKey: "", // Empty for free demo
enableDownload: true,
showDownloadFloatingButton: true,
reportName: "Financial Report Q4",
// --- 5. PERMISSION HANDLING & DOWNLOAD LOGIC ---
// Update your onDownloadCompleted logic:
onDownloadCompleted: (bytes, fileName) async {
if (!kIsWeb) {
bool hasPermission = false;
if (Platform.isAndroid) {
final deviceInfo = DeviceInfoPlugin();
final androidInfo = await deviceInfo.androidInfo;
if (androidInfo.version.sdkInt >= 33) {
// On Android 13+, we don't need 'storage' permission to write to Downloads
// We just need to ensure we have 'photos' if we were reading,
// but for writing, we can often just proceed.
hasPermission = true;
} else {
// For Android 12 and below
var status = await Permission.storage.status;
if (!status.isGranted) {
status = await Permission.storage.request();
}
hasPermission = status.isGranted;
}
}
if (hasPermission) {
try {
// Use path_provider for better reliability
// For now, continuing with your path:
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 (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Report saved to Downloads'), backgroundColor: Colors.green),
);
}
} catch (e) {
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Save failed: $e'), backgroundColor: Colors.red),
);
}
}
} else {
// Handle denied state
}
}
},
),
);
}
}