import 'dart:async'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; class HiddenRollWidget extends StatefulWidget { /// 隐患列表数据 final List> hiddenList; /// 每行高度 final double rowHeight; /// 同一时间可见的行数 final int visibleCount; /// 滚动间隔 final Duration interval; /// 点击回调,传递 HIDDEN_ID final ValueChanged? 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 { 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, ), ), ], ), ), ); }, ), ); } }