143 lines
4.2 KiB
Dart
143 lines
4.2 KiB
Dart
import 'dart:async';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:intl/intl.dart';
|
|
|
|
|
|
class HiddenRollWidget extends StatefulWidget {
|
|
/// 隐患列表数据
|
|
final List<Map<String, dynamic>> hiddenList;
|
|
/// 每行高度
|
|
final double rowHeight;
|
|
/// 同一时间可见的行数
|
|
final int visibleCount;
|
|
/// 滚动间隔
|
|
final Duration interval;
|
|
/// 点击回调,传递 HIDDEN_ID
|
|
final ValueChanged<String>? onItemTap;
|
|
|
|
const HiddenRollWidget({
|
|
Key? key,
|
|
required this.hiddenList,
|
|
this.rowHeight = 35,
|
|
this.visibleCount = 5,
|
|
this.interval = const Duration(seconds: 3),
|
|
this.onItemTap,
|
|
}) : super(key: key);
|
|
|
|
@override
|
|
_HiddenRollWidgetState createState() => _HiddenRollWidgetState();
|
|
}
|
|
|
|
class _HiddenRollWidgetState extends State<HiddenRollWidget> {
|
|
late final ScrollController _ctrl;
|
|
late final Timer _timer;
|
|
int _currentIndex = 0;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_ctrl = ScrollController();
|
|
_timer = Timer.periodic(widget.interval, (_) => _scrollToNext());
|
|
}
|
|
|
|
void _scrollToNext() {
|
|
if (!_ctrl.hasClients || widget.hiddenList.isEmpty) return;
|
|
_currentIndex++;
|
|
if (_currentIndex >= widget.hiddenList.length) {
|
|
_currentIndex = 0;
|
|
_ctrl.jumpTo(0);
|
|
} else {
|
|
_ctrl.animateTo(
|
|
widget.rowHeight * _currentIndex,
|
|
duration: const Duration(milliseconds: 400),
|
|
curve: Curves.easeInOut,
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_timer.cancel();
|
|
_ctrl.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
// 容器高度 = 行高 * 可见行数
|
|
return SizedBox(
|
|
height: widget.rowHeight * widget.visibleCount,
|
|
child: ListView.builder(
|
|
controller: _ctrl,
|
|
physics: const NeverScrollableScrollPhysics(),
|
|
itemExtent: widget.rowHeight,
|
|
itemCount: widget.hiddenList.length,
|
|
itemBuilder: (_, idx) {
|
|
final item = widget.hiddenList[idx];
|
|
// 原始时间字符串
|
|
String rawTime = item['CREATTIME'] ?? '';
|
|
DateTime? dt;
|
|
if (rawTime.isNotEmpty) {
|
|
try {
|
|
dt = DateTime.parse(rawTime.replaceAll('-', '/'));
|
|
} catch (_) {}
|
|
}
|
|
// 去除年份,仅保留 MM-dd HH:mm
|
|
String displayTime;
|
|
if (dt != null) {
|
|
displayTime = DateFormat('MM-dd HH:mm').format(dt);
|
|
} else {
|
|
final parts = rawTime.split(' ');
|
|
if (parts.length >= 2) {
|
|
final datePart = parts[0];
|
|
final timePart = parts[1];
|
|
final mmdd = datePart.length >= 5 ? datePart.substring(5) : datePart;
|
|
final hm = timePart.length >= 5 ? timePart.substring(0, 5) : timePart;
|
|
displayTime = '$mmdd $hm';
|
|
} else {
|
|
displayTime = rawTime;
|
|
}
|
|
}
|
|
// 隐患描述裁剪
|
|
String descr = item['HIDDENDESCR'] ?? '';
|
|
final displayDescr = descr.length > 10 ? '${descr.substring(0, 10)}...' : descr;
|
|
return InkWell(
|
|
onTap: () => widget.onItemTap?.call(item['HIDDEN_ID'] as String),
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 15),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Expanded(
|
|
flex: 3,
|
|
child: Text(
|
|
displayDescr,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
Expanded(
|
|
flex: 2,
|
|
child: Text(
|
|
item['CREATORNAME'] ?? '',
|
|
textAlign: TextAlign.center,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
Expanded(
|
|
flex: 2,
|
|
child: Text(
|
|
displayTime,
|
|
textAlign: TextAlign.right,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
),
|
|
);
|
|
}
|
|
}
|