update
This commit is contained in:
parent
2619288234
commit
84055eda57
206
lib/tools/remote_unlock_overlay.dart
Normal file
206
lib/tools/remote_unlock_overlay.dart
Normal file
@ -0,0 +1,206 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:star_lock/app_settings/app_colors.dart';
|
||||
import 'package:star_lock/network/api_repository.dart';
|
||||
|
||||
class RemoteUnlockOverlay {
|
||||
static Future<void> show({required int lockId, required String lockAlias, required int operateDate, int timeoutSeconds = 60}) async {
|
||||
await Get.to(() => RemoteUnlockOverlayPage(lockId: lockId, lockAlias: lockAlias, operateDate: operateDate, timeoutSeconds: timeoutSeconds), opaque: false);
|
||||
}
|
||||
}
|
||||
|
||||
class RemoteUnlockOverlayPage extends StatefulWidget {
|
||||
const RemoteUnlockOverlayPage({required this.lockId, required this.lockAlias, required this.operateDate, this.timeoutSeconds = 60, Key? key}) : super(key: key);
|
||||
final int lockId;
|
||||
final String lockAlias;
|
||||
final int operateDate;
|
||||
final int timeoutSeconds;
|
||||
|
||||
@override
|
||||
State<RemoteUnlockOverlayPage> createState() => _RemoteUnlockOverlayPageState();
|
||||
}
|
||||
|
||||
class _RemoteUnlockOverlayPageState extends State<RemoteUnlockOverlayPage> {
|
||||
late int seconds;
|
||||
Timer? timer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
seconds = widget.timeoutSeconds;
|
||||
timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
if (seconds <= 0) {
|
||||
t.cancel();
|
||||
timer?.cancel();
|
||||
setState(() {
|
||||
seconds = 0;
|
||||
});
|
||||
Get.back();
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
seconds = seconds - 1;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
timer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double cardWidth = 0.9.sw;
|
||||
final double ringSize = 260.r;
|
||||
return PopScope(
|
||||
canPop: false,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: BackdropFilter(
|
||||
filter: ui.ImageFilter.blur(sigmaX: 3, sigmaY: 3),
|
||||
child: Container(color: Colors.black.withOpacity(0.18)),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Container(
|
||||
width: cardWidth,
|
||||
padding: EdgeInsets.symmetric(vertical: 24.h, horizontal: 20.w),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(16.r),
|
||||
boxShadow: [BoxShadow(color: Colors.black.withOpacity(0.08), blurRadius: 20.r, offset: const Offset(0, 6))],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.lock, color: AppColors.mainColor, size: 24.r),
|
||||
SizedBox(width: 8.w),
|
||||
Expanded(child: Text(widget.lockAlias, style: TextStyle(fontSize: 22.sp, color: AppColors.blackColor))),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 12.h),
|
||||
Text('远程开锁请求'.tr, style: TextStyle(fontSize: 28.sp, color: AppColors.blackColor, fontWeight: FontWeight.w700)),
|
||||
SizedBox(height: 20.h),
|
||||
SizedBox(
|
||||
width: ringSize,
|
||||
height: ringSize,
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
_DottedCountdownRing(seconds: seconds, size: ringSize),
|
||||
Text('${seconds} s', style: TextStyle(fontSize: 48.sp, color: AppColors.mainColor, fontWeight: FontWeight.w700)),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 16.h),
|
||||
Text('请确认是否允许该门锁进行远程开锁'.tr, style: TextStyle(fontSize: 18.sp, color: AppColors.btnDisableColor)),
|
||||
SizedBox(height: 8.h),
|
||||
Text('请求将在倒计时结束后自动取消'.tr, style: TextStyle(fontSize: 16.sp, color: AppColors.btnDisableColor)),
|
||||
SizedBox(height: 20.h),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: _reject,
|
||||
child: Container(
|
||||
width: 0.35.sw,
|
||||
height: 48.h,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(color: Colors.redAccent, borderRadius: BorderRadius.circular(8.r)),
|
||||
child: Text('拒绝'.tr, style: TextStyle(color: Colors.white, fontSize: 22.sp)),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 20.w),
|
||||
GestureDetector(
|
||||
onTap: _accept,
|
||||
child: Container(
|
||||
width: 0.35.sw,
|
||||
height: 48.h,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(color: AppColors.mainColor, borderRadius: BorderRadius.circular(8.r)),
|
||||
child: Text('同意'.tr, style: TextStyle(color: Colors.white, fontSize: 22.sp)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: 8.h),
|
||||
Container(
|
||||
padding: EdgeInsets.symmetric(vertical: 10.h, horizontal: 12.w),
|
||||
decoration: BoxDecoration(color: const Color(0xFFF7F9FC), borderRadius: BorderRadius.circular(8.r)),
|
||||
child: Row(children: [Icon(Icons.info_outline, color: AppColors.mainColor, size: 18.r), SizedBox(width: 6.w), Expanded(child: Text('远程开锁需确保现场安全与人员确认'.tr, style: TextStyle(color: AppColors.btnDisableColor, fontSize: 14.sp)))]),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _accept() async {
|
||||
timer?.cancel();
|
||||
await ApiRepository.to.remoteOpenLock(lockId: widget.lockId.toString(), timeOut: 60);
|
||||
Get.back();
|
||||
}
|
||||
|
||||
void _reject() {
|
||||
timer?.cancel();
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
|
||||
class _DottedCountdownRing extends StatelessWidget {
|
||||
const _DottedCountdownRing({required this.seconds, required this.size});
|
||||
final int seconds;
|
||||
final double size;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final double progress = seconds.clamp(0, 60) / 60.0;
|
||||
return CustomPaint(size: Size(size, size), painter: _DottedCirclePainter(progress: progress));
|
||||
}
|
||||
}
|
||||
|
||||
class _DottedCirclePainter extends CustomPainter {
|
||||
_DottedCirclePainter({required this.progress});
|
||||
final double progress;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final Paint paint = Paint()
|
||||
..color = AppColors.mainColor
|
||||
..strokeWidth = 4.w
|
||||
..style = PaintingStyle.stroke;
|
||||
final double radius = size.width / 2 - 2.w;
|
||||
final Offset center = Offset(size.width / 2, size.height / 2);
|
||||
final Path path = Path();
|
||||
final double angle = -pi / 2 + 2 * pi * progress.clamp(0.0, 1.0);
|
||||
for (int i = 0; i <= 60; i++) {
|
||||
final double startAngle = (2 * pi / 60) * i - pi / 2;
|
||||
final double endAngle = startAngle + (2 * pi / 60) * 0.7;
|
||||
if (startAngle <= angle) {
|
||||
final Path segmentPath = Path();
|
||||
segmentPath.arcTo(Rect.fromCircle(center: center, radius: radius), startAngle, endAngle - startAngle, true);
|
||||
path.addPath(segmentPath, Offset.zero);
|
||||
}
|
||||
}
|
||||
canvas.drawPath(path, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user