
需求:列表中的第一条向上滑出移除,第二条顶到第一的位置的置顶动画。
第一位置的数据移除了需要在列表的末尾加回来。
dart
import 'dart:async';
import 'package:atui/jade/configs/PathConfig.dart';
import 'package:atui/jade/customWidget/custom_separator_vertical.dart';
import 'package:atui/jade/utils/JadeColors.dart';
import 'package:atui/jade/utils/Utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
class InstantBanner extends StatefulWidget {
const InstantBanner({super.key});
@override
State<StatefulWidget> createState() {
return _InstantBannerState();
}
}
class _InstantBannerState extends State<InstantBanner> {
final List<String> _data = [
"【iPhone17Pro Max怎买最便宜,全...",
"1分钱购买新会陈皮",
"【1条你不得不看的装修避坑指南】",
"【2条你不得不看的装修避坑指南】",
"【3条你不得不看的装修避坑指南】",
"【4条你不得不看的装修避坑指南】",
"【5条你不得不看的装修避坑指南】",
"【6条你不得不看的装修避坑指南】",
];
final GlobalKey<AnimatedListState> _listKey = GlobalKey<AnimatedListState>();
Timer? _timer;
@override
void initState() {
super.initState();
_startAnimationTimer();
}
@override
void dispose() {
_timer?.cancel(); // 销毁时取消定时器
super.dispose();
}
// 启动轮播动画
void _startAnimationTimer() {
// 先取消之前的定时器,避免重复
_timer?.cancel();
// 立即执行一次动画,让用户有即时反馈
_moveFirstToLast();
// 然后设置周期性定时器,5秒执行一次
_timer = Timer.periodic(const Duration(seconds: 3), (timer) {
_moveFirstToLast();
});
}
// 停止轮播动画
void _stopAnimationTimer() {
_timer?.cancel();
_timer = null;
}
void _moveFirstToLast() {
if (_data.isEmpty || !mounted) return;
setState(() {
final firstItem = _data.removeAt(0);
_listKey.currentState?.removeItem(
0,
(context, animation) => SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, -1),
end: const Offset(0, 0), // 向上滑出
).animate(animation),
child: _itemView(firstItem, 0),
),
duration: const Duration(milliseconds: 300),
);
// 延迟插入到末尾,实现"顶上去"的视觉效果
Future.delayed(const Duration(milliseconds: 300), () {
if (!mounted) return;
_data.add(firstItem);
_listKey.currentState?.insertItem(
_data.length - 1,
duration: const Duration(milliseconds: 300),
);
});
});
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: 440.w,
width: Utils().screenWidth(context),
child: _popularityRankingView(),
);
}
// 人气榜模块
Widget _popularityRankingView() {
return Container(
margin: EdgeInsets.symmetric(horizontal: 20.w),
padding: EdgeInsets.only(left: 30.w, right: 20.w, top: 30.w, bottom: 20.w),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [JadeColors.orange_17, Colors.white],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
borderRadius: BorderRadius.circular(10),
),
child: Column(
children: [
Padding(
padding: EdgeInsets.only(bottom: 20.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'周边人气榜',
style: TextStyle(fontSize: 34.sp, fontWeight: FontWeight.bold),
),
Image.asset(PathConfig.iconArrowRightBlack, width: 12.w)
],
),
),
Expanded(child: _rankingListView()),
GestureDetector(
onTap: () {},
child: Container(
padding: EdgeInsets.symmetric(horizontal: 40.w, vertical: 10.w),
decoration: BoxDecoration(
color: JadeColors.gold_9,
borderRadius: BorderRadius.circular(20),
),
child: Text(
'查看更多',
style: TextStyle(color: JadeColors.gold_10, fontSize: 26.sp, fontWeight: FontWeight.bold),
),
),
)
],
),
);
}
Widget _rankingListView() {
return AnimatedList(
key: _listKey,
initialItemCount: _data.length,
itemBuilder: (context, index, animation) {
return SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, 1), // 从下方滑入
end: Offset.zero,
).animate(animation),
child: _itemView(_data[index], index),
);
},
);
}
Widget _itemView(String title, int index) {
// 修复高度问题,使用ScreenUtil单位
return SizedBox(
height: 60.w, // 调整为合适的高度
child: Stack(
children: [
Container(
margin: EdgeInsets.only(left: 44.w),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Container(
padding: EdgeInsets.all(3.w), // 使用ScreenUtil单位
decoration: BoxDecoration(
color: JadeColors.red_14,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(2),
topRight: Radius.circular(2),
bottomRight: Radius.circular(2),
),
),
child: Text(
'新',
style: TextStyle(
color: Colors.white,
fontSize: 18.sp,
fontWeight: FontWeight.bold,
),
),
),
SizedBox(width: 8.w), // 增加间距
Expanded(
child: Text(
title,
style: TextStyle(fontSize: 26.sp, fontWeight: FontWeight.bold),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
Positioned(
left: 0,
width: 40.w,
top: 0,
bottom: 0,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ClipOval(
child: Container(
width: 12.w,
height: 12.w,
color: JadeColors.orange_14,
),
),
index == _data.length - 1
? const SizedBox()
: Expanded(
child: SizedBox(
width: 1.w, // 调整竖线宽度
child: CustomSeparatorVertical(
color: JadeColors.orange_14,
dashWidth: 1,
),
),
),
],
),
),
],
),
);
}
}