Created
May 9, 2023 05:28
-
-
Save MelbourneDeveloper/cd233afb0bf718d4b8bc71cc3999b776 to your computer and use it in GitHub Desktop.
ChipSet Sample
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
final colors = [ | |
const Color.fromARGB(255, 255, 0, 0), | |
const Color.fromARGB(255, 0, 255, 0), | |
const Color.fromARGB(255, 0, 0, 255), | |
]; | |
void main() { | |
runApp( | |
MaterialApp( | |
debugShowCheckedModeBanner: false, | |
theme: ThemeData(useMaterial3: true), | |
home: const Scaffold( | |
body: ChipSets(), | |
), | |
), | |
); | |
} | |
class ChipSets extends StatefulWidget { | |
const ChipSets({ | |
super.key, | |
}); | |
@override | |
State<ChipSets> createState() => _ChipSetsState(); | |
} | |
class _ChipSetsState extends State<ChipSets> { | |
var selectedItems = <Color>{}; | |
@override | |
Widget build(BuildContext context) { | |
return Align( | |
alignment: Alignment.topLeft, | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.start, | |
children: [ | |
Text( | |
'RawChip', | |
style: Theme.of(context).textTheme.bodyMedium, | |
), | |
const SizedBox(height: 12), | |
chipSet(), | |
const SizedBox(height: 12), | |
Text( | |
'FilterChip', | |
style: Theme.of(context).textTheme.bodyMedium, | |
), | |
const SizedBox(height: 12), | |
chipSet(builder: ( | |
context, | |
color, | |
selected, | |
onSelected, | |
) { | |
return FilterChip( | |
selectedColor: color, | |
backgroundColor: color.withOpacity(.5), | |
selected: selected, | |
label: Text(colorToText(color)), | |
onSelected: onSelected, | |
); | |
}), | |
const SizedBox(height: 12), | |
Text( | |
'InputChip', | |
style: Theme.of(context).textTheme.bodyMedium, | |
), | |
const SizedBox(height: 12), | |
chipSet(builder: ( | |
context, | |
color, | |
selected, | |
onSelected, | |
) { | |
return InputChip( | |
selectedColor: color, | |
backgroundColor: color.withOpacity(.5), | |
selected: selected, | |
label: Text(colorToText(color)), | |
onSelected: onSelected, | |
); | |
}), | |
const SizedBox(height: 12), | |
Text( | |
'ActionChip', | |
style: Theme.of(context).textTheme.bodyMedium, | |
), | |
chipSet(builder: ( | |
context, | |
color, | |
selected, | |
onSelected, | |
) { | |
return ActionChip( | |
backgroundColor: color.withOpacity(.5), | |
label: Text(colorToText(color)), | |
//onPressed: () => onSelected(selected), | |
); | |
}), | |
], | |
), | |
); | |
} | |
String colorToText(Color color) { | |
if (color.blue == 255) return 'Blue'; | |
if (color.red == 255) { | |
return 'Red'; | |
} else { | |
return 'Green'; | |
} | |
} | |
Widget chipSet<T extends Widget>( | |
{T Function( | |
BuildContext context, | |
Color color, | |
bool selected, | |
void Function(bool value) onSelected, | |
)? | |
builder}) => | |
ChipSet( | |
constraints: const BoxConstraints( | |
maxWidth: 200, | |
minWidth: 200, | |
), | |
//direction: Axis.vertical, | |
spacing: 8, | |
runSpacing: 8, | |
chipBuilder: builder, | |
items: colors, | |
isSelected: (item) => selectedItems.contains(item), | |
onSelected: (item, value) { | |
setState( | |
() { | |
if (value) { | |
if (!selectedItems.contains(item)) selectedItems.add(item); | |
} else { | |
if (selectedItems.contains(item)) selectedItems.remove(item); | |
} | |
}, | |
); | |
}); | |
} | |
///Builds a custom chip widget for a given item of type T | |
///- BuildContext context: The build context in which the widget is being built. | |
///- T item: An item of type T from the list of items provided to the ChipSet. | |
///- bool selected: A boolean flag indicating whether the current item is selected or not. | |
///- void Function(bool value) onSelected: A callback function that is triggered when a chip is selected or deselected. It accepts a boolean value to indicate the selection state. | |
typedef ChipBuilder<T> = Widget Function( | |
BuildContext context, | |
T item, | |
bool selected, | |
void Function(bool value) onSelected, | |
); | |
///A Material Design widget that displays a set of chips | |
///based on a list of items of type [T]. | |
/// | |
///The type T is the type of the value that each chip item represents. | |
///All the entries in a given set must represent values | |
///with consistent types. Each item in items must be specialized | |
///with that same type argument. The chips can be created | |
///using a custom [ChipBuilder] function or the default | |
///implementation using [RawChip]. | |
/// | |
///[ChipSet] supports customizing the appearance and layout of each chip. | |
///The chips render in a [Wrap] and render horiontally by default. | |
/// | |
///The onSelected callback should update a state list | |
///that defines the selected value. It should also call [State.setState] | |
///to rebuild the set with the new values. | |
class ChipSet<T> extends StatefulWidget { | |
/// Creates a [ChipSet]. | |
/// | |
/// The [items] must have distinct values. | |
/// | |
/// The [onSelected] callback must update a state selected list | |
/// | |
/// The [isSelected] callback must return a boolean value | |
/// based on the state selected list | |
const ChipSet({ | |
required this.items, | |
required this.onSelected, | |
required this.isSelected, | |
this.chipBuilder, | |
this.spacing, | |
this.runSpacing, | |
this.constraints, | |
this.direction, | |
super.key, | |
}); | |
/// An optional custom ChipBuilder function to build the chips. | |
/// If not provided, the default implementation using RawChip will be used. | |
/// and the chips will display a [toString] of the item. | |
final ChipBuilder<T>? chipBuilder; | |
///A function that returns true if the item is selected, otherwise false. | |
final bool Function(T item) isSelected; | |
///A list of items of type [T] that the chips will be built from. | |
final List<T> items; | |
//An optional set of constraints to apply to the size of the chips | |
//in the ChipSet. | |
final BoxConstraints? constraints; | |
///How much space to place between chips in a run in the main axis. | |
final double? spacing; | |
///How much space to place between the runs themselves in the cross axis. | |
final double? runSpacing; | |
///The direction to use as the main axis. | |
final Axis? direction; | |
///A callback function that is triggered when a chip is selected or deselected. | |
final void Function(T item, bool value) onSelected; | |
@override | |
State<ChipSet<T>> createState() => _ChipSetState<T>(); | |
} | |
class _ChipSetState<T> extends State<ChipSet<T>> { | |
@override | |
Widget build(BuildContext context) => Wrap( | |
direction: widget.direction ?? Axis.horizontal, | |
spacing: widget.spacing ?? 0, | |
runSpacing: widget.runSpacing ?? 0, | |
children: | |
widget.items.map((item) => _buildItem(context, item)).toList(), | |
); | |
Widget _rawChip( | |
BuildContext context, | |
T item, | |
bool selected, | |
void Function(bool value) onSelected, | |
) => | |
RawChip( | |
selected: selected, | |
label: Text(item.toString()), | |
onSelected: (value) => setState(() { | |
onSelected(value); | |
}), | |
); | |
Widget _buildItem( | |
BuildContext context, | |
item, | |
) { | |
final builder = widget.chipBuilder ?? _rawChip; | |
var child = builder( | |
context, | |
item, | |
widget.isSelected(item), | |
(value) => widget.onSelected(item, value), | |
); | |
if (widget.constraints != null) { | |
return ConstrainedBox( | |
constraints: widget.constraints!, | |
child: child, | |
); | |
} | |
return child; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment