refactor ui
This commit is contained in:
141
web/repertory/lib/widgets/app_dropdown.dart
Normal file
141
web/repertory/lib/widgets/app_dropdown.dart
Normal file
@@ -0,0 +1,141 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/helpers.dart';
|
||||
|
||||
class AppDropdownFormField<T> extends StatelessWidget {
|
||||
const AppDropdownFormField({
|
||||
super.key,
|
||||
required this.values,
|
||||
required this.labelOf,
|
||||
this.value,
|
||||
this.onChanged,
|
||||
this.validator,
|
||||
this.labelText,
|
||||
this.prefixIcon,
|
||||
this.enabled = true,
|
||||
this.constrainToIntrinsic = false,
|
||||
this.widthMultiplier = 1.0,
|
||||
this.maxWidth,
|
||||
this.isExpanded = false,
|
||||
this.dropdownColor,
|
||||
this.textStyle,
|
||||
this.contentPadding,
|
||||
this.fillColor,
|
||||
});
|
||||
|
||||
final List<T> values;
|
||||
final String Function(T value) labelOf;
|
||||
final T? value;
|
||||
final ValueChanged<T?>? onChanged;
|
||||
final FormFieldValidator<T>? validator;
|
||||
final String? labelText;
|
||||
final IconData? prefixIcon;
|
||||
final bool enabled;
|
||||
final bool constrainToIntrinsic;
|
||||
final double widthMultiplier;
|
||||
final double? maxWidth;
|
||||
final bool isExpanded;
|
||||
final Color? dropdownColor;
|
||||
final TextStyle? textStyle;
|
||||
final EdgeInsetsGeometry? contentPadding;
|
||||
|
||||
final Color? fillColor;
|
||||
|
||||
double _measureTextWidth(
|
||||
BuildContext context,
|
||||
String text,
|
||||
TextStyle? style,
|
||||
) {
|
||||
final tp = TextPainter(
|
||||
text: TextSpan(text: text, style: style),
|
||||
maxLines: 1,
|
||||
textDirection: Directionality.of(context),
|
||||
)..layout();
|
||||
return tp.width;
|
||||
}
|
||||
|
||||
double? _computedMaxWidth(BuildContext context) {
|
||||
if (!constrainToIntrinsic) return maxWidth;
|
||||
|
||||
final theme = Theme.of(context);
|
||||
final scheme = theme.colorScheme;
|
||||
final effectiveStyle =
|
||||
textStyle ??
|
||||
theme.textTheme.bodyMedium?.copyWith(
|
||||
color: scheme.onSurface.withValues(alpha: 0.96),
|
||||
);
|
||||
|
||||
final longest = values.isEmpty
|
||||
? ''
|
||||
: values
|
||||
.map((v) => labelOf(v))
|
||||
.reduce((a, b) => a.length >= b.length ? a : b);
|
||||
|
||||
final labelW = _measureTextWidth(context, longest, effectiveStyle);
|
||||
|
||||
final prefixW = prefixIcon == null ? 0.0 : 48.0;
|
||||
const arrowW = 32.0;
|
||||
final pad = contentPadding ?? const EdgeInsets.all(constants.paddingSmall);
|
||||
final padW = (pad is EdgeInsets)
|
||||
? (pad.left + pad.right)
|
||||
: constants.paddingSmall * 2;
|
||||
|
||||
final base = labelW + prefixW + arrowW + padW;
|
||||
|
||||
final cap =
|
||||
maxWidth ?? (MediaQuery.of(context).size.width - constants.padding * 2);
|
||||
return (base * widthMultiplier).clamp(0.0, cap);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final scheme = theme.colorScheme;
|
||||
|
||||
final effectiveFill =
|
||||
fillColor ?? scheme.primary.withValues(alpha: constants.primaryAlpha);
|
||||
|
||||
final effectiveTextStyle =
|
||||
textStyle ??
|
||||
theme.textTheme.bodyMedium?.copyWith(
|
||||
color: scheme.onSurface.withValues(alpha: 0.96),
|
||||
);
|
||||
|
||||
final items = values.map((v) {
|
||||
return DropdownMenuItem<T>(
|
||||
value: v,
|
||||
child: Text(
|
||||
labelOf(v),
|
||||
style: effectiveTextStyle,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
final field = DropdownButtonFormField<T>(
|
||||
decoration: createCommonDecoration(scheme, labelText ?? ""),
|
||||
dropdownColor: dropdownColor ?? effectiveFill,
|
||||
iconEnabledColor: scheme.onSurface.withValues(alpha: 0.90),
|
||||
initialValue: value,
|
||||
isExpanded: isExpanded,
|
||||
items: items,
|
||||
onChanged: enabled ? onChanged : null,
|
||||
style: effectiveTextStyle,
|
||||
validator: validator,
|
||||
);
|
||||
|
||||
final maxW = _computedMaxWidth(context);
|
||||
final wrapped = maxW == null
|
||||
? field
|
||||
: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: maxW),
|
||||
child: field,
|
||||
);
|
||||
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
heightFactor: 1.0,
|
||||
child: wrapped,
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user