|
@@ -0,0 +1,158 @@
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+
|
|
|
+///按钮式单选框组
|
|
|
+class VSelectBoxButtonGroup<T, TValue> extends StatefulWidget {
|
|
|
+ final List<T> source;
|
|
|
+ final TValue? value;
|
|
|
+ final String Function(T data) labelGetter;
|
|
|
+ final TValue Function(T data) valueGetter;
|
|
|
+ final ValueChanged<TValue?>? onChanged;
|
|
|
+ final double? itemWidth;
|
|
|
+ const VSelectBoxButtonGroup({
|
|
|
+ super.key,
|
|
|
+ required this.source,
|
|
|
+ this.value,
|
|
|
+ required this.labelGetter,
|
|
|
+ required this.valueGetter,
|
|
|
+ this.onChanged,
|
|
|
+ this.itemWidth,
|
|
|
+ });
|
|
|
+
|
|
|
+ @override
|
|
|
+ State<StatefulWidget> createState() => _VSelectBoxGroupState<T, TValue>();
|
|
|
+}
|
|
|
+
|
|
|
+class _VSelectBoxGroupState<T, TValue>
|
|
|
+ extends State<VSelectBoxButtonGroup<T, TValue>> {
|
|
|
+ late TValue? _selectedValue;
|
|
|
+ @override
|
|
|
+ void initState() {
|
|
|
+ _selectedValue = widget.value;
|
|
|
+ super.initState();
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ final children = <Widget>[];
|
|
|
+ final length = widget.source.length;
|
|
|
+ for (var i = 0; i < length; i++) {
|
|
|
+ final e = widget.source[i];
|
|
|
+ final value = widget.valueGetter(e);
|
|
|
+ final isSelect = _selectedValue == value;
|
|
|
+ children.add(VSelectBoxButton(
|
|
|
+ // key: UniqueKey(),
|
|
|
+ label: widget.labelGetter(e),
|
|
|
+ isSelected: isSelect,
|
|
|
+ onChanged: (status) {
|
|
|
+ setState(() {
|
|
|
+ if (status) {
|
|
|
+ _onChanged(value);
|
|
|
+ } else {
|
|
|
+ _onChanged(null);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ return Wrap(
|
|
|
+ spacing: 12,
|
|
|
+ runSpacing: 8,
|
|
|
+ children: children,
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ void _onChanged(TValue? value) {
|
|
|
+ _selectedValue = value;
|
|
|
+ widget.onChanged?.call(_selectedValue);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class VSelectBoxButton extends StatefulWidget {
|
|
|
+ final String label;
|
|
|
+ final bool? isSelected;
|
|
|
+ final ValueChanged<bool>? onChanged;
|
|
|
+
|
|
|
+ const VSelectBoxButton(
|
|
|
+ {super.key,
|
|
|
+ required this.label,
|
|
|
+ this.isSelected,
|
|
|
+ required this.onChanged});
|
|
|
+
|
|
|
+ @override
|
|
|
+ State<StatefulWidget> createState() => _VSelectBoxState();
|
|
|
+}
|
|
|
+
|
|
|
+class _VSelectBoxState extends State<VSelectBoxButton> {
|
|
|
+ bool _isSelected = false;
|
|
|
+ @override
|
|
|
+ void initState() {
|
|
|
+ if (widget.isSelected != null) {
|
|
|
+ _isSelected = widget.isSelected!;
|
|
|
+ }
|
|
|
+ super.initState();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ @override
|
|
|
+ void didUpdateWidget(covariant VSelectBoxButton oldWidget) {
|
|
|
+ _isSelected=widget.isSelected==true;
|
|
|
+ super.didUpdateWidget(oldWidget);
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ const height = 56.0;
|
|
|
+ const borderRadius = height / 2;
|
|
|
+ final primaryColor = Theme.of(context).primaryColor;
|
|
|
+ return Material(
|
|
|
+ child: Ink(
|
|
|
+ child: InkWell(
|
|
|
+ borderRadius: BorderRadius.circular(borderRadius),
|
|
|
+ onTap: () {
|
|
|
+ setState(
|
|
|
+ () {
|
|
|
+ _isSelected = !_isSelected;
|
|
|
+ widget.onChanged?.call(_isSelected);
|
|
|
+ },
|
|
|
+ );
|
|
|
+ },
|
|
|
+ child: Container(
|
|
|
+ padding: const EdgeInsets.only(left: 0, right: borderRadius),
|
|
|
+ alignment: Alignment.center,
|
|
|
+ height: height,
|
|
|
+ decoration: BoxDecoration(
|
|
|
+ color: _isSelected ? primaryColor : Colors.white,
|
|
|
+ borderRadius: BorderRadius.circular(borderRadius),
|
|
|
+ border: _isSelected ? null : Border.all(color: primaryColor),
|
|
|
+ ),
|
|
|
+ child: Row(
|
|
|
+ mainAxisSize: MainAxisSize.min,
|
|
|
+ crossAxisAlignment: CrossAxisAlignment.center,
|
|
|
+ children: [
|
|
|
+ if (_isSelected) ...[
|
|
|
+ const Icon(
|
|
|
+ Icons.check_rounded,
|
|
|
+ color: Colors.green,
|
|
|
+ size: 24,
|
|
|
+ ),
|
|
|
+ const SizedBox(
|
|
|
+ width: 8,
|
|
|
+ ),
|
|
|
+ ],
|
|
|
+ if (!_isSelected)
|
|
|
+ const SizedBox(
|
|
|
+ width: 32,
|
|
|
+ ),
|
|
|
+ Text(
|
|
|
+ widget.label,
|
|
|
+ style: TextStyle(
|
|
|
+ color: _isSelected ? Colors.white : primaryColor,
|
|
|
+ fontSize: 20),
|
|
|
+ )
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|