voo_section_list 0.1.0
voo_section_list: ^0.1.0 copied to clipboard
A comprehensive Flutter package for creating settings-style section lists with multiple visual styles, custom builders, and full theming support.
import 'package:flutter/material.dart';
import 'package:voo_section_list/voo_section_list.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'VooSectionList Example',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.dark,
),
useMaterial3: true,
),
home: const StyleSelectorPage(),
);
}
}
class StyleSelectorPage extends StatelessWidget {
const StyleSelectorPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('VooSectionList Styles'),
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
_StyleCard(
title: 'Card Style',
description: 'Elevated cards with rounded corners',
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const SettingsPage(style: VooSectionStyle.card),
),
),
),
const SizedBox(height: 12),
_StyleCard(
title: 'iOS Inset Grouped',
description: 'iOS-style with inset and rounded containers',
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const SettingsPage(style: VooSectionStyle.insetGrouped),
),
),
),
const SizedBox(height: 12),
_StyleCard(
title: 'Flat / Material',
description: 'Android/Material flat style with headers',
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const SettingsPage(style: VooSectionStyle.flat),
),
),
),
const SizedBox(height: 12),
_StyleCard(
title: 'Plain',
description: 'Minimal plain list with dividers',
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const SettingsPage(style: VooSectionStyle.plain),
),
),
),
],
),
);
}
}
class _StyleCard extends StatelessWidget {
final String title;
final String description;
final VoidCallback onTap;
const _StyleCard({
required this.title,
required this.description,
required this.onTap,
});
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
title: Text(title),
subtitle: Text(description),
trailing: const Icon(Icons.chevron_right),
onTap: onTap,
),
);
}
}
class SettingsPage extends StatefulWidget {
final VooSectionStyle style;
const SettingsPage({super.key, required this.style});
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
bool _notificationsEnabled = true;
bool _emailUpdates = false;
bool _darkMode = false;
bool _autoplay = true;
bool _downloadWifiOnly = true;
String _language = 'English';
String _theme = 'System';
VooSectionConfig get _config {
switch (widget.style) {
case VooSectionStyle.card:
return VooSectionConfig.card;
case VooSectionStyle.insetGrouped:
return VooSectionConfig.ios;
case VooSectionStyle.flat:
return VooSectionConfig.material;
case VooSectionStyle.plain:
return VooSectionConfig.plain;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('${widget.style.displayName} Style'),
),
body: VooSectionList<void>(
config: _config,
padding: const EdgeInsets.symmetric(vertical: 8),
sections: [
VooSection(
id: 'account',
title: 'Account',
items: [
VooSectionItem(
id: 'profile',
title: 'Edit Profile',
leading: const Icon(Icons.person_outline),
type: VooSectionItemType.navigation,
onTap: () => _showSnackBar('Edit Profile tapped'),
),
VooSectionItem(
id: 'password',
title: 'Change Password',
leading: const Icon(Icons.lock_outline),
type: VooSectionItemType.navigation,
onTap: () => _showSnackBar('Change Password tapped'),
),
],
),
VooSection(
id: 'notifications',
title: 'Notifications',
items: [
VooSectionItem(
id: 'push',
title: 'Push Notifications',
leading: const Icon(Icons.notifications_outlined),
type: VooSectionItemType.toggle,
toggleValue: _notificationsEnabled,
onToggleChanged: (value) {
setState(() => _notificationsEnabled = value);
},
),
VooSectionItem(
id: 'email',
title: 'Email Updates',
leading: const Icon(Icons.email_outlined),
type: VooSectionItemType.toggle,
toggleValue: _emailUpdates,
onToggleChanged: (value) {
setState(() => _emailUpdates = value);
},
),
],
),
VooSection(
id: 'preferences',
title: 'Preferences',
footerText: 'Choose your preferred display settings',
items: [
VooSectionItem(
id: 'language',
title: 'Language',
leading: const Icon(Icons.language),
type: VooSectionItemType.value,
displayValue: _language,
onTap: () => _showLanguagePicker(),
),
VooSectionItem(
id: 'theme',
title: 'Theme',
leading: const Icon(Icons.palette_outlined),
type: VooSectionItemType.value,
displayValue: _theme,
onTap: () => _showThemePicker(),
),
VooSectionItem(
id: 'darkMode',
title: 'Dark Mode',
leading: const Icon(Icons.dark_mode_outlined),
type: VooSectionItemType.toggle,
toggleValue: _darkMode,
onToggleChanged: (value) {
setState(() => _darkMode = value);
},
),
],
),
VooSection(
id: 'playback',
title: 'Playback',
items: [
VooSectionItem(
id: 'autoplay',
title: 'Autoplay',
subtitle: 'Automatically play next video',
leading: const Icon(Icons.play_circle_outline),
type: VooSectionItemType.toggle,
toggleValue: _autoplay,
onToggleChanged: (value) {
setState(() => _autoplay = value);
},
),
VooSectionItem(
id: 'wifiOnly',
title: 'Download via WiFi only',
leading: const Icon(Icons.wifi),
type: VooSectionItemType.toggle,
toggleValue: _downloadWifiOnly,
onToggleChanged: (value) {
setState(() => _downloadWifiOnly = value);
},
),
],
),
VooSection(
id: 'about',
title: 'About',
items: [
VooSectionItem(
id: 'version',
title: 'Version',
leading: const Icon(Icons.info_outline),
type: VooSectionItemType.info,
trailing: Text(
'1.0.0',
style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant),
),
),
VooSectionItem(
id: 'terms',
title: 'Terms of Service',
leading: const Icon(Icons.description_outlined),
type: VooSectionItemType.navigation,
onTap: () => _showSnackBar('Terms of Service tapped'),
),
VooSectionItem(
id: 'privacy',
title: 'Privacy Policy',
leading: const Icon(Icons.privacy_tip_outlined),
type: VooSectionItemType.navigation,
onTap: () => _showSnackBar('Privacy Policy tapped'),
),
],
),
VooSection(
id: 'danger',
title: 'Danger Zone',
items: [
VooSectionItem(
id: 'logout',
title: 'Sign Out',
leading: const Icon(Icons.logout),
type: VooSectionItemType.destructive,
onTap: () => _showSignOutDialog(),
),
VooSectionItem(
id: 'delete',
title: 'Delete Account',
leading: const Icon(Icons.delete_forever),
type: VooSectionItemType.destructive,
onTap: () => _showDeleteDialog(),
),
],
),
],
),
);
}
void _showSnackBar(String message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(message)),
);
}
void _showLanguagePicker() {
showModalBottomSheet(
context: context,
builder: (context) => Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: const Text('English'),
trailing: _language == 'English' ? const Icon(Icons.check) : null,
onTap: () {
setState(() => _language = 'English');
Navigator.pop(context);
},
),
ListTile(
title: const Text('Spanish'),
trailing: _language == 'Spanish' ? const Icon(Icons.check) : null,
onTap: () {
setState(() => _language = 'Spanish');
Navigator.pop(context);
},
),
ListTile(
title: const Text('French'),
trailing: _language == 'French' ? const Icon(Icons.check) : null,
onTap: () {
setState(() => _language = 'French');
Navigator.pop(context);
},
),
const SizedBox(height: 16),
],
),
);
}
void _showThemePicker() {
showModalBottomSheet(
context: context,
builder: (context) => Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: const Text('System'),
trailing: _theme == 'System' ? const Icon(Icons.check) : null,
onTap: () {
setState(() => _theme = 'System');
Navigator.pop(context);
},
),
ListTile(
title: const Text('Light'),
trailing: _theme == 'Light' ? const Icon(Icons.check) : null,
onTap: () {
setState(() => _theme = 'Light');
Navigator.pop(context);
},
),
ListTile(
title: const Text('Dark'),
trailing: _theme == 'Dark' ? const Icon(Icons.check) : null,
onTap: () {
setState(() => _theme = 'Dark');
Navigator.pop(context);
},
),
const SizedBox(height: 16),
],
),
);
}
void _showSignOutDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Sign Out'),
content: const Text('Are you sure you want to sign out?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
_showSnackBar('Signed out');
},
child: const Text('Sign Out'),
),
],
),
);
}
void _showDeleteDialog() {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('Delete Account'),
content: const Text(
'This action cannot be undone. All your data will be permanently deleted.',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
_showSnackBar('Account deleted');
},
style: TextButton.styleFrom(foregroundColor: Colors.red),
child: const Text('Delete'),
),
],
),
);
}
}