import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'dart:async'; import 'constants.dart'; class MyClipper extends CustomClipper { final double durationNow; final double duration; final double margin; MyClipper(this.durationNow, this.duration, this.margin); @override // Rect getClip(Size size) => Rect.fromLTWH(0, 0, width - margin, height - margin); Rect getClip(Size size) { // 计算剪切的宽度和高度 double width = durationNow * (240-margin*2); double height = 80 - margin; // 返回剪切区域 return Rect.fromLTWH(0, 0, width + margin, height); } @override bool shouldReclip(CustomClipper oldClipper) => true; } class TimersPageLandscape extends StatefulWidget { const TimersPageLandscape({ super.key, }); @override State createState() => _TimersPageLandscapeState(); } class _TimersPageLandscapeState extends State with TickerProviderStateMixin{ late Animation animation; late AnimationController controller; double duration = 10; double durationNow = 0; late Timer timer; bool isRunning = false; double targetProgress = 1; double currentProgress = 0; int counts = 5; @override void initState() { super.initState(); controller = AnimationController( vsync: this, duration: Duration(seconds: duration.toInt()), ); animation = Tween( begin: durationNow/duration, end: targetProgress, ).animate(CurvedAnimation( parent: controller, curve: Curves.linear, )) ..addListener(() { setState(() { currentProgress = animation.value; }); }); } @override Widget build(BuildContext context) { final theme = Theme.of(context); Stream dateTimeStream() { // 使用Stream.periodic创建一个每秒更新一次的时间流 return Stream.periodic(const Duration(seconds: 1), (_) => DateTime.now()); } Stream _getDurationNowStream() { return Stream.periodic(const Duration(seconds: 1), (timer) { return durationNow; }); } return Scaffold( body: Container( color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.3), padding: EdgeInsets.all((MediaQuery.of(context).size.width / 20).toDouble()), child: Row( crossAxisAlignment: CrossAxisAlignment.start, // mainAxisAlignment: MainAxisAlignment.center, children: [ Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( margin: const EdgeInsets.all(14.0), // padding: const EdgeInsets.all(12.0), decoration: BoxDecoration( color: Colors.transparent, borderRadius: myBorderRadius, ), child: Column( children: [ StreamBuilder( stream: dateTimeStream(), builder: (context, snapshot) { return Text( DateFormat.Hm().format(DateTime.now()), style: theme.textTheme.displayLarge!.copyWith(color: theme.colorScheme.primary), ); }, ), Text( DateFormat('EEEE, MMMM dd').format(DateTime.now()), style: theme.textTheme.titleLarge!.copyWith(color: theme.colorScheme.secondary), ), const Divider(), Text( 'You have $counts timers', style: theme.textTheme.titleMedium!.copyWith(color: theme.colorScheme.primary), ), const Divider(), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( '剩余时间:${(duration-durationNow).toStringAsFixed(1)} 秒', style: const TextStyle(fontSize: 20), ), const Divider(), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( child: const Text('开始/暂停'), onPressed: () { isRunning = !isRunning; if (isRunning) { timer = Timer.periodic(const Duration(seconds: 1), (timer) { setState(() { if (durationNow < duration) durationNow++; }); }); } else { timer.cancel(); } }, ), ElevatedButton( child: Text('重置'), onPressed: () { if (isRunning) { timer.cancel(); isRunning = false; } durationNow = 0; controller.reset(); setState(() {}); }, ), ], ), ] ), ], ), ), ], ), Expanded( child: ListView( children: [ GestureDetector( onTap: () { controller.forward(); isRunning = !isRunning; if (isRunning) { timer = Timer.periodic(const Duration(milliseconds: 10), (timer) { setState(() { if (durationNow < duration) {durationNow += 0.01;} }); }); } else { timer.cancel(); controller.stop(); } }, onLongPress: () { controller.reset(); }, child: Padding( padding: const EdgeInsets.symmetric(horizontal:16.0, vertical:4.0), child: Stack( children: [ Container( // Card background width: 240, height: 80, decoration: BoxDecoration( color: theme.colorScheme.secondaryContainer.withAlpha(60), borderRadius: BorderRadius.circular(18.0), ), ), // StreamBuilder( // stream: _getDurationNowStream(), AnimatedBuilder( animation: animation, builder: (context, snapshot) { return ClipRect( clipper: MyClipper(animation.value, duration, 6), child: Container( // Progress bar decoration: BoxDecoration( color: theme.colorScheme.primaryContainer.withAlpha(192), borderRadius: BorderRadius.circular(16.0), ), margin: const EdgeInsets.all(6), // duration: Duration(seconds: (duration-durationNow).toInt()), alignment: Alignment.center, width: 240 - 12, height: 80 - 12, ), ); } ), Container( margin: const EdgeInsets.symmetric(vertical:6,horizontal:2), alignment: Alignment.center, width: 240, height: 60, child: ListTile( title: Text( '${(duration-durationNow).toInt()}', style: theme.textTheme.titleLarge!.copyWith(color: theme.colorScheme.primary), textAlign: TextAlign.right, ), subtitle: Text( 'Countdown: $duration', ), ), ), ], ) ), ), ], ), ), ], ), ), ); } }