flutter_artist_2d_scrollables 0.9.1
flutter_artist_2d_scrollables: ^0.9.1 copied to clipboard
An identity-driven, strictly stateless 2D scrolling layout engine built on top of Google's two_dimensional_scrollables for the FlutterArtist ecosystem.
example/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_artist_2d_scrollables/flutter_artist_2d_scrollables.dart';
import 'package:two_dimensional_scrollables/two_dimensional_scrollables.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FlutterArtist 2D Grid Workspace',
theme: ThemeData(useMaterial3: true, primarySwatch: Colors.blue),
home: const Employee2DGridDemoScreen(),
);
}
}
class EmployeePosition {
final String name;
const EmployeePosition(this.name);
}
class EmployeeInfo {
final int id;
final String name;
final EmployeePosition employeePosition;
const EmployeeInfo({
required this.id,
required this.name,
required this.employeePosition,
});
}
class MockEmployeeBlock {
final List<EmployeeInfo> items = const [
EmployeeInfo(
id: 101,
name: 'James Carter',
employeePosition: EmployeePosition('Senior Engineer'),
),
EmployeeInfo(
id: 102,
name: 'Helen Foster',
employeePosition: EmployeePosition('Product Manager'),
),
EmployeeInfo(
id: 103,
name: 'Michael Finch',
employeePosition: EmployeePosition('UI/UX Designer'),
),
EmployeeInfo(
id: 104,
name: 'Linda Albright',
employeePosition: EmployeePosition('QA Architect'),
),
];
int? currentItemId;
final Set<int> selectedItemIds = {};
void setCurrentItem(EmployeeInfo item) {
currentItemId = item.id;
}
void toggleSelection(EmployeeInfo item) {
if (selectedItemIds.contains(item.id)) {
selectedItemIds.remove(item.id);
} else {
selectedItemIds.add(item.id);
}
}
}
class Employee2DGridDemoScreen extends StatefulWidget {
const Employee2DGridDemoScreen({super.key});
@override
State<Employee2DGridDemoScreen> createState() =>
_Employee2DGridDemoScreenState();
}
class _Employee2DGridDemoScreenState extends State<Employee2DGridDemoScreen> {
final MockEmployeeBlock block = MockEmployeeBlock();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('FlutterArtist × 2D Core Spreadsheet')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Card(
color: Colors.grey.shade100,
elevation: 0,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'• Current Active Target (Via Button): ${block.currentItemId ?? "None"}',
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.indigo,
),
),
Text(
'• Multi-Select Rows (Via Row Click): ${block.selectedItemIds}',
),
],
),
),
),
const SizedBox(height: 16),
Expanded(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Colors.black12),
),
child: FaTwoDimensionalTable<int, EmployeeInfo>(
items: block.items,
getItemId: (item) => item.id,
currentItemId: block.currentItemId,
selectedItemIds: Set<int>.from(block.selectedItemIds),
columns: const [
Fa2DColumn(columnName: 'id', extent: FixedSpanExtent(70.0)),
Fa2DColumn(
columnName: 'name',
extent: FixedSpanExtent(180.0),
),
Fa2DColumn(
columnName: 'designation',
extent: FixedSpanExtent(220.0),
),
Fa2DColumn(
columnName: 'action',
extent: FixedSpanExtent(140.0),
),
],
// Perfect theme-driven priority translation mapping modeled from FaDaviTable
themeData: FaTwoDimensionalTableThemeData<EmployeeInfo>(
gridLineColor: Colors.black12,
gridLineThickness: 1.0,
headerDecoration: BoxDecoration(color: Colors.blue.shade50),
rowColorBuilder:
(
item, {
required isCurrent,
required isSelected,
required isChecked,
}) {
if (isCurrent)
return Colors.indigo.withAlpha(
45,
); // Current focus takes first priority
if (isSelected)
return Colors.blue.withAlpha(
20,
); // Multi-selection takes second priority
return null;
},
),
onRowTap: (employee) {
setState(() {
block.toggleSelection(employee);
});
},
cellBuilder: (context, column, item, isCurrent, isSelected) {
if (column.columnName == 'action') {
return Center(
child: ElevatedButton(
onPressed: () =>
setState(() => block.setCurrentItem(item)),
style: ElevatedButton.styleFrom(
backgroundColor: isCurrent
? Colors.indigo
: Colors.grey.shade200,
foregroundColor: isCurrent
? Colors.white
: Colors.black87,
padding: const EdgeInsets.symmetric(horizontal: 8),
),
child: const Text(
'Set Current',
style: TextStyle(fontSize: 11),
),
),
);
}
String text = '';
switch (column.columnName) {
case 'id':
text = item.id.toString();
break;
case 'name':
text = item.name;
break;
case 'designation':
text = item.employeePosition.name;
break;
}
return Container(
alignment: Alignment.centerLeft,
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Text(text),
);
},
),
),
),
],
),
),
);
}
}