import 'package:flutter/material.dart';
import '../../../app/theme.dart';
class SignatureScreen extends StatefulWidget {
const SignatureScreen({super.key, required this.customer});
final String customer;
@override
State<SignatureScreen> createState() => _SignatureScreenState();
}
class _SignatureScreenState extends State<SignatureScreen> {
final List<Offset?> points = [];
List<List<double>> get serialized => points
.map(
(point) =>
point == null ? <double>[-1, -1] : <double>[point.dx, point.dy],
)
.toList();
@override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('Kundenunterschrift'),
backgroundColor: Colors.white,
surfaceTintColor: Colors.transparent,
),
body: SafeArea(
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 760),
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Freigabe durch den Kunden',
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: 8),
Text(
'${widget.customer} bestätigt die aufgeführten Leistungen und Arbeitszeiten.',
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: 24),
Expanded(
child: Card(
child: ClipRRect(
borderRadius: BorderRadius.circular(22),
child: LayoutBuilder(
builder: (context, constraints) {
final size = Size(
constraints.maxWidth,
constraints.maxHeight,
);
return GestureDetector(
behavior: HitTestBehavior.opaque,
onPanStart: (details) => setState(
() => points.add(
_normalize(details.localPosition, size),
),
),
onPanUpdate: (details) => setState(
() => points.add(
_normalize(details.localPosition, size),
),
),
onPanEnd: (_) => setState(() => points.add(null)),
child: CustomPaint(
painter: _SignaturePainter(List.of(points)),
child: Stack(
children: [
if (points.isEmpty)
const Center(
child: Text(
'Hier mit dem Finger oder der Maus unterschreiben',
style: TextStyle(color: Colors.black45),
),
),
const Positioned(
left: 28,
right: 28,
bottom: 54,
child: Divider(color: Colors.black38),
),
Positioned(
left: 28,
bottom: 24,
child: Text(
widget.customer,
style: const TextStyle(
color: Colors.black54,
),
),
),
],
),
),
);
},
),
),
),
),
const SizedBox(height: 18),
Row(
children: [
OutlinedButton.icon(
onPressed: points.isEmpty
? null
: () => setState(points.clear),
icon: const Icon(Icons.refresh_rounded),
label: const Text('Neu zeichnen'),
),
const SizedBox(width: 12),
Expanded(
child: FilledButton.icon(
onPressed: points.whereType<Offset>().length < 3
? null
: () => Navigator.pop(context, serialized),
icon: const Icon(Icons.verified_outlined),
label: const Text('Unterschrift übernehmen'),
),
),
],
),
],
),
),
),
),
),
);
Offset _normalize(Offset point, Size size) =>
Offset(point.dx / size.width, point.dy / size.height);
}
class SignaturePreview extends StatelessWidget {
const SignaturePreview({super.key, required this.signature});
final List<List<double>> signature;
@override
Widget build(BuildContext context) => SizedBox(
height: 110,
child: CustomPaint(
painter: _SignaturePainter(
signature
.map((point) => point[0] < 0 ? null : Offset(point[0], point[1]))
.toList(),
),
),
);
}
class _SignaturePainter extends CustomPainter {
const _SignaturePainter(this.points);
final List<Offset?> points;
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = AppColors.ink
..strokeWidth = 2.4
..strokeCap = StrokeCap.round;
for (var index = 0; index < points.length - 1; index++) {
final first = points[index];
final second = points[index + 1];
if (first != null && second != null) {
canvas.drawLine(
Offset(first.dx * size.width, first.dy * size.height),
Offset(second.dx * size.width, second.dy * size.height),
paint,
);
}
}
}
@override
bool shouldRepaint(covariant _SignaturePainter oldDelegate) =>
oldDelegate.points != points;
}
