Files
kuaishai2/lib/features/home/widgets/program_list.dart
Developer 736c36a98e refactor(home): 优化主页布局和运行控制面板
- 将主页左右布局改为弹性布局,左侧程序列表占2/5宽度,右侧控制区域占3/5宽度
- 移除程序列表组件的固定宽度设置,使其能够自适应布局
- 在运行控制面板中添加主轴最小尺寸限制以优化空间使用
- 移除暂停/继续按钮中的占位按钮,简化按钮逻辑
- 修改开始/继续按钮为暂停/继续按钮,支持运行中状态切换
- 更新按钮图标和文字根据当前运行状态动态显示
- 移除运行状态指示器,精简界面元素
2026-06-04 17:22:38 +08:00

222 lines
7.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import '../../../core/localization/app_localizations.dart';
import '../../../core/theme/app_theme.dart';
import '../../programs/models/program.dart';
import '../../programs/providers/programs_provider.dart';
/// 程序列表组件 - 暗色工业风格
/// 显示程序卡片列表,支持选择操作
class ProgramList extends ConsumerWidget {
const ProgramList({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
final l10n = AppLocalizations.of(context);
final programsState = ref.watch(programsProvider);
final programsNotifier = ref.read(programsProvider.notifier);
return Container(
decoration: BoxDecoration(
color: AppTheme.cardBg,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: AppTheme.borderSubtle, width: 1),
),
child: Column(
children: [
// 标题
Padding(
padding: const EdgeInsets.all(14),
child: Row(
children: [
Icon(Icons.list_alt, color: AppTheme.textHeading, size: 18),
const SizedBox(width: 10),
Text(
l10n?.availablePrograms ?? '可用程序',
style: const TextStyle(
color: AppTheme.textHeading,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
],
),
),
// 程序列表
Expanded(
child: programsState.isLoading
? const Center(child: CircularProgressIndicator())
: programsState.programs.isEmpty
? Center(
child: Text(
l10n?.noData ?? '暂无数据',
style: const TextStyle(
color: AppTheme.textSecondary,
fontSize: 14,
),
),
)
: ListView.builder(
padding: const EdgeInsets.symmetric(horizontal: 14),
itemCount: programsState.programs.length,
itemBuilder: (context, index) {
final program = programsState.programs[index];
final isSelected =
programsState.selectedProgramId == program.id;
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: _ProgramCard(
program: program,
isSelected: isSelected,
onTap: () {
programsNotifier.selectProgram(program.id);
},
),
);
},
),
),
],
),
);
}
}
/// 单个程序卡片 - 暗色工业风格
class _ProgramCard extends StatelessWidget {
final Program program;
final bool isSelected;
final VoidCallback? onTap;
const _ProgramCard({
required this.program,
this.isSelected = false,
this.onTap,
});
@override
Widget build(BuildContext context) {
final dateFormat = DateFormat('yyyy-MM-dd HH:mm');
final createdAt = _parseDate(program.createdAt);
return Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(8),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isSelected ? AppTheme.cardSelectedBg : AppTheme.cardBg,
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: isSelected ? AppTheme.accentPrimary : AppTheme.borderSubtle,
width: isSelected ? 2 : 1,
),
),
child: Row(
children: [
// 选择指示器
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: isSelected
? AppTheme.accentPrimary
: Colors.transparent,
border: Border.all(
color: isSelected
? AppTheme.accentPrimary
: AppTheme.statusStopped,
width: 2,
),
),
child: isSelected
? const Icon(Icons.check, color: Colors.white, size: 12)
: null,
),
const SizedBox(width: 12),
// 程序信息
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Text(
program.code,
style: const TextStyle(
color: AppTheme.textSecondary,
fontSize: 12,
),
),
const SizedBox(width: 8),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: BoxDecoration(
color: program.status == 1
? AppTheme.statusRunning.withValues(alpha: 0.15)
: AppTheme.statusStopped.withValues(alpha: 0.15),
borderRadius: BorderRadius.circular(4),
),
child: Text(
program.status == 1 ? '启用' : '停用',
style: TextStyle(
color: program.status == 1
? AppTheme.statusRunning
: AppTheme.statusStopped,
fontSize: 10,
),
),
),
],
),
const SizedBox(height: 4),
Text(
program.name,
style: const TextStyle(
color: AppTheme.textHeading,
fontSize: 14,
fontWeight: FontWeight.w500,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 4),
Text(
createdAt != null
? dateFormat.format(createdAt)
: program.createdAt,
style: const TextStyle(
color: AppTheme.textTertiary,
fontSize: 11,
fontFamily: 'monospace',
),
),
],
),
),
],
),
),
),
);
}
DateTime? _parseDate(String dateStr) {
try {
return DateTime.parse(dateStr);
} catch (e) {
return null;
}
}
}