voo_kanban 0.0.2
voo_kanban: ^0.0.2 copied to clipboard
A highly customizable Kanban board widget for Flutter with drag-and-drop, swimlanes, WIP limits, and extensive theming
import 'package:flutter/material.dart';
import 'pages/basic_board_page.dart';
import 'pages/swimlanes_page.dart';
import 'pages/drag_drop_page.dart';
import 'pages/theming_page.dart';
void main() {
runApp(const VooKanbanExampleApp());
}
class VooKanbanExampleApp extends StatelessWidget {
const VooKanbanExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Voo Kanban Examples',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
useMaterial3: true,
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.indigo,
brightness: Brightness.dark,
),
useMaterial3: true,
),
themeMode: ThemeMode.system,
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
int _selectedIndex = 0;
final List<_NavigationItem> _pages = [
_NavigationItem(
icon: Icons.view_kanban,
label: 'Basic',
page: const BasicBoardPage(),
),
_NavigationItem(
icon: Icons.view_week,
label: 'Swimlanes',
page: const SwimlanesPage(),
),
_NavigationItem(
icon: Icons.drag_indicator,
label: 'Drag & Drop',
page: const DragDropPage(),
),
_NavigationItem(
icon: Icons.palette,
label: 'Theming',
page: const ThemingPage(),
),
];
@override
Widget build(BuildContext context) {
final isDesktop = MediaQuery.of(context).size.width >= 900;
final isTablet = MediaQuery.of(context).size.width >= 600 &&
MediaQuery.of(context).size.width < 900;
final isMobile = !isDesktop && !isTablet;
return Scaffold(
appBar: AppBar(
title: const Text('Voo Kanban Examples'),
centerTitle: true,
elevation: 0,
backgroundColor: Theme.of(context).colorScheme.primaryContainer,
foregroundColor: Theme.of(context).colorScheme.onPrimaryContainer,
),
drawer: isMobile
? Drawer(
child: ListView(
children: [
DrawerHeader(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primaryContainer,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(
Icons.view_kanban,
size: 48,
color:
Theme.of(context).colorScheme.onPrimaryContainer,
),
const SizedBox(height: 8),
Text(
'Voo Kanban',
style: Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(
color: Theme.of(context)
.colorScheme
.onPrimaryContainer,
),
),
],
),
),
..._pages.asMap().entries.map((entry) {
final index = entry.key;
final item = entry.value;
return ListTile(
leading: Icon(item.icon),
title: Text(item.label),
selected: _selectedIndex == index,
onTap: () {
setState(() => _selectedIndex = index);
Navigator.of(context).pop();
},
);
}),
],
),
)
: null,
body: Row(
children: [
if (isDesktop || isTablet)
NavigationRail(
selectedIndex: _selectedIndex,
onDestinationSelected: (index) {
setState(() => _selectedIndex = index);
},
labelType: isDesktop
? NavigationRailLabelType.all
: NavigationRailLabelType.selected,
destinations: _pages
.map((item) => NavigationRailDestination(
icon: Icon(item.icon),
label: Text(item.label),
))
.toList(),
),
Expanded(child: _pages[_selectedIndex].page),
],
),
);
}
}
class _NavigationItem {
final IconData icon;
final String label;
final Widget page;
const _NavigationItem({
required this.icon,
required this.label,
required this.page,
});
}