lib/ui/aliplayer_custom_selector_widget.dart (119 lines of code) (raw):
// Copyright © 2025 Alibaba Cloud. All rights reserved.
//
// Author: keria
// Date: 2025/2/13
// Brief: 自定义选择器控件,用于显示一个带有文本和图标的选项选择组件。
import 'package:flutter/material.dart';
/// A customizable selector widget for displaying a list of options with text and icons.
///
/// 自定义选择器控件,用于显示一组带有文本和图标的选项。
class AliPlayerCustomSelectorWidget<T> extends StatefulWidget {
/// 显示的文本
///
/// Displayed text
final String text;
/// 起始图标
///
/// Leading icon
final IconData startIcon;
/// 可选项列表
///
/// List of selectable options
final List<T>? options;
/// 初始值
///
/// Initial selected value
final T? initialValue;
/// 值变化时的回调
///
/// Callback when the value changes
final ValueChanged<T>? onChanged;
/// 选项显示格式化函数(允许为空,为空时使用默认的 toString() 方法)。
///
/// Formatter for displaying options (falls back to toString() if null).
final String Function(T option)? displayFormatter;
const AliPlayerCustomSelectorWidget({
super.key,
required this.text,
required this.startIcon,
required this.options,
required this.initialValue,
this.onChanged,
this.displayFormatter,
});
@override
State<AliPlayerCustomSelectorWidget<T>> createState() =>
_AliPlayerCustomSelectorWidgetState<T>();
}
class _AliPlayerCustomSelectorWidgetState<T>
extends State<AliPlayerCustomSelectorWidget<T>> {
late T? _currentValue;
@override
void initState() {
super.initState();
_currentValue = widget.initialValue;
}
/// 更新当前值并触发回调
///
/// Updates the current value and triggers the callback.
void _onValueChanged(T newValue) {
if (_currentValue != newValue) {
setState(() {
_currentValue = newValue;
});
widget.onChanged?.call(newValue);
}
}
@override
Widget build(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
widget.text,
style: const TextStyle(
fontSize: 12,
color: Colors.black,
),
),
const SizedBox(width: 4),
Icon(
widget.startIcon,
size: 16,
),
const SizedBox(width: 4),
_buildOptions(),
const SizedBox(width: 4),
],
);
}
/// 构建选项列表
///
/// Builds the option list.
Widget _buildOptions() {
// 默认显示内容
final bool isEmpty = widget.initialValue == null ||
widget.options == null ||
widget.options!.isEmpty;
return // 如果为空,显示默认内容
isEmpty
? const Expanded(
child: Text(
"No options available",
style: TextStyle(
fontSize: 12,
color: Colors.grey,
),
),
)
:
// 横滑列表
Expanded(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: widget.options!
.map((option) => _buildSingleItem(option))
.toList(),
),
),
);
}
/// 构建单个选项项
///
/// Builds a single option item.
Widget _buildSingleItem(T option) {
final isSelected = option == _currentValue;
// 如果 displayFormatter 为空,使用默认的 toString() 方法
final displayText = widget.displayFormatter != null
? widget.displayFormatter!(option)
: option.toString();
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => _onValueChanged(option),
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Text(
displayText,
style: TextStyle(
color: isSelected ? Colors.blue : Colors.grey,
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
fontSize: 12,
),
),
),
);
}
/// 监听外部状态变化
///
/// Listen for external state changes.
@override
void didUpdateWidget(covariant AliPlayerCustomSelectorWidget<T> oldWidget) {
super.didUpdateWidget(oldWidget);
// Trigger rebuild if properties changes
if (widget.initialValue != oldWidget.initialValue ||
!identical(widget.options, oldWidget.options)) {
setState(() {
_currentValue = widget.initialValue;
});
}
}
}