Files
imajviewer/lib/widgets/image_canvas.dart
T
Alhan 5e31a9e6bc
Build Windows / build-windows (push) Has been cancelled
fix: info cubugu kaldirildi, kod temizlendi
2026-07-22 05:45:33 +03:00

164 lines
5.0 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
class ImageCanvas extends StatefulWidget {
final String filePath;
const ImageCanvas({super.key, required this.filePath});
@override
State<ImageCanvas> createState() => _ImageCanvasState();
}
class _ImageCanvasState extends State<ImageCanvas> {
late TransformationController _controller;
double _currentScale = 1.0;
bool _isFilled = true; // varsayılan: ekranı doldur
@override
void initState() {
super.initState();
_controller = TransformationController();
_controller.addListener(_onTransformChanged);
}
@override
void didUpdateWidget(ImageCanvas oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.filePath != widget.filePath) {
_resetView();
}
}
void _resetView() {
_controller.value = Matrix4.identity();
_currentScale = 1.0;
_isFilled = true;
}
void _onTransformChanged() {
_currentScale = _controller.value.getMaxScaleOnAxis();
}
void _toggleFillFit() {
setState(() {
_isFilled = !_isFilled;
_controller.value = Matrix4.identity();
_currentScale = 1.0;
});
}
void _handleDoubleTap() {
_toggleFillFit();
}
void _handleScroll(PointerScrollEvent event) {
final renderBox = context.findRenderObject() as RenderBox?;
if (renderBox == null) return;
final localPosition = renderBox.globalToLocal(event.position);
final delta = event.scrollDelta.dy;
final scaleFactor = delta < 0 ? 1.1 : 1 / 1.1;
final newScale = (_currentScale * scaleFactor).clamp(0.1, 10.0);
if (newScale <= 0.1 || newScale >= 10.0) return;
final actualScale = newScale / _currentScale;
// Fare altındaki child-space noktayı hesapla
final matrix = _controller.value;
final tx = matrix.getTranslation().x;
final ty = matrix.getTranslation().y;
final childX = (localPosition.dx - tx) / _currentScale;
final childY = (localPosition.dy - ty) / _currentScale;
// controller * zoomAroundPoint sırasıyla uygula
final newMatrix = Matrix4.copy(_controller.value)
// ignore: deprecated_member_use
..translate(childX, childY)
// ignore: deprecated_member_use
..scale(actualScale)
// ignore: deprecated_member_use
..translate(-childX, -childY);
_controller.value = newMatrix;
}
@override
void dispose() {
_controller.removeListener(_onTransformChanged);
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (context, constraints) {
return Listener(
onPointerSignal: (event) {
if (event is PointerScrollEvent) {
_handleScroll(event);
}
},
child: GestureDetector(
onDoubleTap: _handleDoubleTap,
child: InteractiveViewer(
transformationController: _controller,
boundaryMargin: const EdgeInsets.all(double.infinity),
minScale: 0.1,
maxScale: 10.0,
panEnabled: true,
scaleEnabled: false,
child: _isFilled
? SizedBox.expand(
child: Image.file(
File(widget.filePath),
fit: BoxFit.cover,
filterQuality: FilterQuality.high,
errorBuilder: (context, error, stackTrace) {
return const Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.broken_image, size: 48, color: Colors.white38),
SizedBox(height: 8),
Text('Resim yüklenemedi',
style: TextStyle(color: Colors.white38)),
],
),
);
},
),
)
: Center(
child: Image.file(
File(widget.filePath),
fit: BoxFit.contain,
filterQuality: FilterQuality.high,
errorBuilder: (context, error, stackTrace) {
return const Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.broken_image, size: 48, color: Colors.white38),
SizedBox(height: 8),
Text('Resim yüklenemedi',
style: TextStyle(color: Colors.white38)),
],
),
);
},
),
),
),
),
);
},
);
}
}