added guardian letters with chat and multiple answer functionalities
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:signature/signature.dart';
|
||||
|
||||
import '../../../../widget/info_dialog.dart';
|
||||
|
||||
/// Upper bound the server accepts for a signature image.
|
||||
const int maxSignatureBytes = 256 * 1024;
|
||||
|
||||
/// Lets the guardian draw a signature. Resolves to the PNG (transparent
|
||||
/// background, cropped to the strokes) or null when dismissed.
|
||||
Future<Uint8List?> showSignatureSheet(
|
||||
BuildContext context, {
|
||||
required String signerHint,
|
||||
}) => showModalBottomSheet<Uint8List>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
enableDrag: false,
|
||||
showDragHandle: false,
|
||||
builder: (_) => _SignatureSheet(signerHint: signerHint),
|
||||
);
|
||||
|
||||
class _SignatureSheet extends StatefulWidget {
|
||||
final String signerHint;
|
||||
|
||||
const _SignatureSheet({required this.signerHint});
|
||||
|
||||
@override
|
||||
State<_SignatureSheet> createState() => _SignatureSheetState();
|
||||
}
|
||||
|
||||
class _SignatureSheetState extends State<_SignatureSheet> {
|
||||
// Paper-like pad in both themes: what is drawn is what gets exported.
|
||||
final SignatureController _controller = SignatureController(
|
||||
penStrokeWidth: 2.5,
|
||||
penColor: Colors.black,
|
||||
strokeCap: StrokeCap.round,
|
||||
strokeJoin: StrokeJoin.round,
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller.addListener(_onChanged);
|
||||
}
|
||||
|
||||
bool _isEmpty = true;
|
||||
|
||||
// The controller notifies per drawn point; only the buttons depend on it.
|
||||
void _onChanged() {
|
||||
if (_controller.isEmpty == _isEmpty) return;
|
||||
setState(() => _isEmpty = _controller.isEmpty);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.removeListener(_onChanged);
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _accept() async {
|
||||
var png = await _controller.toPngBytes();
|
||||
if (png != null && png.lengthInBytes > maxSignatureBytes) {
|
||||
png = await _controller.toPngBytes(width: 600);
|
||||
}
|
||||
if (!mounted) return;
|
||||
if (png == null || png.lengthInBytes > maxSignatureBytes) {
|
||||
InfoDialog.show(
|
||||
context,
|
||||
'Die Unterschrift konnte nicht übernommen werden. Bitte versuche es '
|
||||
'erneut.',
|
||||
title: 'Unterschrift',
|
||||
);
|
||||
return;
|
||||
}
|
||||
Navigator.pop(context, png);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 8),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Text('Unterschrift', style: Theme.of(context).textTheme.titleLarge),
|
||||
const SizedBox(height: 4),
|
||||
Text(widget.signerHint),
|
||||
const SizedBox(height: 12),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Signature(
|
||||
controller: _controller,
|
||||
height: 220,
|
||||
backgroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
TextButton.icon(
|
||||
onPressed: _isEmpty ? null : _controller.clear,
|
||||
icon: const Icon(Icons.undo),
|
||||
label: const Text('Löschen'),
|
||||
),
|
||||
const Spacer(),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text('Abbrechen'),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
FilledButton(
|
||||
onPressed: _isEmpty ? null : _accept,
|
||||
child: const Text('Übernehmen'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user