Files
kuaishai2/lib/features/home/widgets/program_list.dart
Developer 5d28bf631b chore(project): 初始化项目基础配置文件
- 添加 CodeGraph、Android 和通用 gitignore 配置
- 创建项目元数据文件跟踪 Flutter 项目属性
- 添加 Codex AI 指导文档 AGENTS.md 说明项目架构
- 配置代码分析选项 analysis_options.yaml
- 设置 Android 应用清单权限和 Kiosk 模式配置
- 实现中英文国际化支持 AppLocalizations
- 配置 GoRouter 应用路由导航
- 创建明亮工业控制风格的主题配置 AppTheme
2026-06-04 11:19:44 +08:00

223 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(
width: 380,
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;
}
}
}