Ocideck/lib/widgets/editors/two_images_editor.dart
Brenno de Winter 1fc4d25dcf
Some checks are pending
CI / Format · Analyze · Test (push) Waiting to run
Add slide quality checks for accessibility with export warnings.
Help authors spot missing alt text, low contrast, and dense slides in the editor, with an optional confirmation step before export when issues remain.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-16 08:57:18 +02:00

145 lines
4.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../models/slide.dart';
import '../../state/deck_provider.dart';
import '_editor_field.dart';
class TwoImagesEditor extends ConsumerStatefulWidget {
final Slide slide;
final ValueChanged<Slide> onUpdate;
final List<String> searchPaths;
final String? captionBasePath;
const TwoImagesEditor({
super.key,
required this.slide,
required this.onUpdate,
this.searchPaths = const [],
this.captionBasePath,
});
@override
ConsumerState<TwoImagesEditor> createState() => _TwoImagesEditorState();
}
class _TwoImagesEditorState extends ConsumerState<TwoImagesEditor> {
late final TextEditingController _title;
@override
void initState() {
super.initState();
_title = TextEditingController(text: widget.slide.title);
_title.addListener(_emitTitle);
}
void _emitTitle() {
widget.onUpdate(widget.slide.copyWith(title: _title.text));
}
@override
void dispose() {
_title.dispose();
super.dispose();
}
Future<void> _pasteImage(bool isSecond) async {
final imgService = ref.read(imageServiceProvider);
final path = await imgService.pasteImage();
if (path != null) {
widget.onUpdate(
isSecond
? widget.slide.copyWith(imagePath2: path, imageCaption2: '')
: widget.slide.copyWith(imagePath: path, imageCaption: ''),
);
}
}
Future<void> _pickImage(bool isSecond) async {
final imgService = ref.read(imageServiceProvider);
final path = await imgService.pickImage();
if (path != null) {
widget.onUpdate(
isSecond
? widget.slide.copyWith(imagePath2: path, imageCaption2: '')
: widget.slide.copyWith(imagePath: path, imageCaption: ''),
);
}
}
void _clearImage(bool isSecond) {
widget.onUpdate(
isSecond
? widget.slide.copyWith(imagePath2: '', imageCaption2: '')
: widget.slide.copyWith(imagePath: '', imageCaption: ''),
);
}
@override
Widget build(BuildContext context) {
return ListView(
padding: const EdgeInsets.all(16),
children: [
EditorField(
label: 'Ondertitel (optioneel)',
controller: _title,
hint: 'Tekst onder de afbeeldingen',
),
const SizedBox(height: 20),
const SectionLabel('Linker afbeelding'),
ImagePickerBar(
imagePath: widget.slide.imagePath,
imageCaption: widget.slide.imageCaption,
searchPaths: widget.searchPaths,
captionBasePath: widget.captionBasePath,
onPicked: (path, caption) => widget.onUpdate(
widget.slide.copyWith(imagePath: path, imageCaption: caption),
),
onBrowse: () => _pickImage(false),
onPaste: () => _pasteImage(false),
onClear: widget.slide.imagePath.isNotEmpty
? () => _clearImage(false)
: null,
onCaptionChanged: (caption) =>
widget.onUpdate(widget.slide.copyWith(imageCaption: caption)),
),
const SizedBox(height: 20),
const SectionLabel('Rechter afbeelding'),
ImagePickerBar(
imagePath: widget.slide.imagePath2,
imageCaption: widget.slide.imageCaption2,
captionField: 'imageCaption2',
searchPaths: widget.searchPaths,
captionBasePath: widget.captionBasePath,
onPicked: (path, caption) => widget.onUpdate(
widget.slide.copyWith(imagePath2: path, imageCaption2: caption),
),
onBrowse: () => _pickImage(true),
onPaste: () => _pasteImage(true),
onClear: widget.slide.imagePath2.isNotEmpty
? () => _clearImage(true)
: null,
onCaptionChanged: (caption) =>
widget.onUpdate(widget.slide.copyWith(imageCaption2: caption)),
),
const SizedBox(height: 20),
const SectionLabel('Verdeling (links / rechts)'),
ImageZoomControl(
value: widget.slide.imageSize > 0 ? widget.slide.imageSize : 50,
onChanged: (v) =>
widget.onUpdate(widget.slide.copyWith(imageSize: v)),
step: 5,
minValue: 20,
maxValue: 80,
),
Padding(
padding: const EdgeInsets.only(left: 8),
child: Text(
'Links ${widget.slide.imageSize > 0 ? widget.slide.imageSize : 50}% — '
'Rechts ${100 - (widget.slide.imageSize > 0 ? widget.slide.imageSize : 50)}%',
style: const TextStyle(fontSize: 11, color: Color(0xFF64748B)),
),
),
],
);
}
}