nested_choice_list 1.1.1
nested_choice_list: ^1.1.1 copied to clipboard
A flutter package for handling nested list item selection without limitation for the depth of the nested list.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:nested_choice_list/nested_choice_list.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'NestedChoiceList Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const HomePage(),
);
}
}
// Home page
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool showSelectedItems = true;
bool showNavigationPath = false;
bool enableMultiSelect = false;
bool enableSearch = false;
NestedChoiceListType choiceListType = NestedChoiceListType.navigation;
//
NestedChoiceEntity? selectedItem;
List<NestedChoiceEntity>? selectedItems;
final items = const [
NestedChoiceEntity(
value: 'value1',
label: 'label1 level1',
children: [
NestedChoiceEntity(value: 'value2', label: 'label1 level 2'),
NestedChoiceEntity(value: 'value3', label: 'label1 level 2'),
NestedChoiceEntity(
value: 'value4',
label: 'label1.2 level 2',
children: [
NestedChoiceEntity(value: 'value2', label: 'label1.2 level 3'),
NestedChoiceEntity(value: 'value3', label: 'label1.2 level 3'),
NestedChoiceEntity(
value: 'value4',
label: 'label1.3 level 3',
children: [
NestedChoiceEntity(value: 'value2', label: 'label1.3 level 4'),
NestedChoiceEntity(value: 'value3', label: 'label1.3 level 4'),
NestedChoiceEntity(value: 'value4', label: 'label1.3 level 4'),
],
),
],
),
],
),
NestedChoiceEntity(
value: 'value2',
label: 'label2 level1',
children: [
NestedChoiceEntity(value: 'value2', label: 'label2 level 2'),
NestedChoiceEntity(value: 'value3', label: 'label2 level 2'),
NestedChoiceEntity(
value: 'value4',
label: 'label2.1 level 2',
children: [
NestedChoiceEntity(value: 'value2', label: 'label2.1 level 3'),
NestedChoiceEntity(value: 'value3', label: 'label2.1 level 3'),
NestedChoiceEntity(value: 'value4', label: 'label2.1 level 3'),
],
),
],
),
NestedChoiceEntity(
value: 'value3',
label: 'label3 level1',
children: [
NestedChoiceEntity(value: 'value2', label: 'label3 level 2'),
NestedChoiceEntity(value: 'value3', label: 'label3 level 2'),
NestedChoiceEntity(value: 'value4', label: 'label3 level 2'),
],
),
NestedChoiceEntity(value: 'value4', label: 'label4 level1'),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('NestedChoiceList'),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CheckboxListTile.adaptive(
title: const Text('ShowNavigationPath'),
value: showNavigationPath,
onChanged: (newValue) {
setState(() {
showNavigationPath = newValue ?? false;
});
},
),
const Divider(),
CheckboxListTile.adaptive(
title: const Text('EnableMultiSelect'),
value: enableMultiSelect,
onChanged: (newValue) {
setState(() {
enableMultiSelect = newValue ?? false;
});
},
),
const Divider(),
CheckboxListTile.adaptive(
title: const Text('ShowSelectedItems'),
value: showSelectedItems,
onChanged: (newValue) {
setState(() {
showSelectedItems = newValue ?? false;
});
},
),
const Divider(),
CheckboxListTile.adaptive(
title: const Text('EnableSearch'),
value: enableSearch,
onChanged: (newValue) {
setState(() {
enableSearch = newValue ?? false;
});
},
),
const Divider(),
DropdownButton(
value: choiceListType,
items: NestedChoiceListType.values
.map(
(type) => DropdownMenuItem<NestedChoiceListType>(
value: type,
child: Text(
type.toString(),
),
),
)
.toList(),
onChanged: (newType) {
setState(() {
choiceListType = newType ?? NestedChoiceListType.navigation;
});
},
),
const Divider(),
ElevatedButton(
onPressed: () async {
final result = await Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => DemoOfNestedChoiceList(
items: items,
showSelectedItems: showSelectedItems,
showNavigationPath: showNavigationPath,
enableMultiSelect: enableMultiSelect,
enableSearch: enableSearch,
choiceListType: choiceListType,
),
),
);
if (result is NestedChoiceEntity) {
setState(() {
selectedItem = result;
selectedItems = null;
});
} else if (result is List<NestedChoiceEntity>) {
setState(() {
selectedItem = null;
selectedItems = result;
});
}
},
child: const Text(
'Show Demo',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
const Divider(),
const Text(
'⇩ Result ⇩',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
const SizedBox(
height: 20,
),
if (selectedItem != null)
Text(
'onTapItem: ${selectedItem!.label}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.orange,
),
),
if (selectedItems != null)
Text(
'onSelectionChange: $selectedItems',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.orange,
),
)
],
),
),
),
);
}
}
class DemoOfNestedChoiceList extends StatelessWidget {
const DemoOfNestedChoiceList({
required this.items,
super.key,
required this.showSelectedItems,
required this.showNavigationPath,
required this.enableMultiSelect,
required this.enableSearch,
required this.choiceListType,
});
final List<NestedChoiceEntity> items;
final bool showSelectedItems;
final bool showNavigationPath;
final bool enableMultiSelect;
final bool enableSearch;
final NestedChoiceListType choiceListType;
@override
Widget build(BuildContext context) {
List<NestedChoiceEntity> selectedItems = [];
return Scaffold(
appBar: AppBar(
title: const Text('Demo'),
),
body: NestedChoiceList(
items: items,
type: choiceListType,
showSelectedItems: showSelectedItems,
showNavigationPath: showNavigationPath,
enableMultiSelect: enableMultiSelect,
enableSearch: enableSearch,
style: const NestedListStyle(),
// this callback triggers when we are in
// single select mode (enableMultiSelect = false)
onTapItem: (item) {
debugPrint('onTapItem -> $item');
Navigator.of(context).pop(item);
},
// this callback triggers when we are in
// multi select mode (enableMultiSelect = true)
onSelectionChange: (items) {
debugPrint('onSelectionChange -> $items');
selectedItems = items;
},
/// Callback function triggered when the navigation changes.
/// Useful for handling UI updates based on the current page index.
onNavigationChange: (pageIndex) {
debugPrint('onNavigationChange -> pageIndex: $pageIndex');
},
),
bottomNavigationBar: enableMultiSelect
? SafeArea(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop(selectedItems);
},
child: const Text('Confirm selected items'),
),
)
: null,
);
}
}