feat: varsayilan fill modu, zoom merkezleme duzeltildi, alt bilgi cubugu
Build Windows / build-windows (push) Has been cancelled
Build Windows / build-windows (push) Has been cancelled
This commit is contained in:
1 parent
c5679f457d
commit
5f9382ef61
4 files changed
+95
-51
No files matched your search
Binary file not shown.
Binary file not shown.
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
import '../services/image_manager.dart';
|
||||||
|
|
||||||
class ImageCanvas extends StatefulWidget {
|
class ImageCanvas extends StatefulWidget {
|
||||||
final String filePath;
|
final String filePath;
|
||||||
@@ -14,7 +15,7 @@ class ImageCanvas extends StatefulWidget {
|
|||||||
class _ImageCanvasState extends State<ImageCanvas> {
|
class _ImageCanvasState extends State<ImageCanvas> {
|
||||||
late TransformationController _controller;
|
late TransformationController _controller;
|
||||||
double _currentScale = 1.0;
|
double _currentScale = 1.0;
|
||||||
bool _isFilled = false; // false=fit, true=cover
|
bool _isFilled = true; // varsayılan: ekranı doldur
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -34,7 +35,7 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
|||||||
void _resetView() {
|
void _resetView() {
|
||||||
_controller.value = Matrix4.identity();
|
_controller.value = Matrix4.identity();
|
||||||
_currentScale = 1.0;
|
_currentScale = 1.0;
|
||||||
_isFilled = false;
|
_isFilled = true; // yeni resimde fill moduna dön
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onTransformChanged() {
|
void _onTransformChanged() {
|
||||||
@@ -67,6 +68,7 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
|||||||
|
|
||||||
final actualScale = newScale / _currentScale;
|
final actualScale = newScale / _currentScale;
|
||||||
|
|
||||||
|
// Fare altındaki child-space noktayı hesapla
|
||||||
final matrix = _controller.value;
|
final matrix = _controller.value;
|
||||||
final tx = matrix.getTranslation().x;
|
final tx = matrix.getTranslation().x;
|
||||||
final ty = matrix.getTranslation().y;
|
final ty = matrix.getTranslation().y;
|
||||||
@@ -74,18 +76,28 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
|||||||
final childX = (localPosition.dx - tx) / _currentScale;
|
final childX = (localPosition.dx - tx) / _currentScale;
|
||||||
final childY = (localPosition.dy - ty) / _currentScale;
|
final childY = (localPosition.dy - ty) / _currentScale;
|
||||||
|
|
||||||
final newMatrix = Matrix4.identity()
|
// controller * zoomAroundPoint sırasıyla uygula
|
||||||
|
final newMatrix = Matrix4.copy(_controller.value)
|
||||||
// ignore: deprecated_member_use
|
// ignore: deprecated_member_use
|
||||||
..translate(childX, childY)
|
..translate(childX, childY)
|
||||||
// ignore: deprecated_member_use
|
// ignore: deprecated_member_use
|
||||||
..scale(actualScale)
|
..scale(actualScale)
|
||||||
// ignore: deprecated_member_use
|
// ignore: deprecated_member_use
|
||||||
..translate(-childX, -childY)
|
..translate(-childX, -childY);
|
||||||
..multiply(_controller.value);
|
|
||||||
|
|
||||||
_controller.value = newMatrix;
|
_controller.value = newMatrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _zoomPercent() {
|
||||||
|
return '${(_currentScale * 100).round()}%';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _imageInfo() {
|
||||||
|
final mgr = ImageManager.instance;
|
||||||
|
if (mgr.images.isEmpty) return '';
|
||||||
|
return '${mgr.currentIndex + 1} / ${mgr.imageCount}';
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_controller.removeListener(_onTransformChanged);
|
_controller.removeListener(_onTransformChanged);
|
||||||
@@ -105,54 +117,86 @@ class _ImageCanvasState extends State<ImageCanvas> {
|
|||||||
},
|
},
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
onDoubleTap: _handleDoubleTap,
|
onDoubleTap: _handleDoubleTap,
|
||||||
child: InteractiveViewer(
|
child: Stack(
|
||||||
transformationController: _controller,
|
children: [
|
||||||
boundaryMargin: const EdgeInsets.all(double.infinity),
|
InteractiveViewer(
|
||||||
minScale: 0.1,
|
transformationController: _controller,
|
||||||
maxScale: 10.0,
|
boundaryMargin: const EdgeInsets.all(double.infinity),
|
||||||
panEnabled: true,
|
minScale: 0.1,
|
||||||
scaleEnabled: false,
|
maxScale: 10.0,
|
||||||
child: _isFilled
|
panEnabled: true,
|
||||||
? SizedBox.expand(
|
scaleEnabled: false,
|
||||||
child: Image.file(
|
child: _isFilled
|
||||||
File(widget.filePath),
|
? SizedBox.expand(
|
||||||
fit: BoxFit.cover,
|
child: Image.file(
|
||||||
filterQuality: FilterQuality.high,
|
File(widget.filePath),
|
||||||
errorBuilder: (context, error, stackTrace) {
|
fit: BoxFit.cover,
|
||||||
return const Center(
|
filterQuality: FilterQuality.high,
|
||||||
child: Column(
|
errorBuilder: (context, error, stackTrace) {
|
||||||
mainAxisSize: MainAxisSize.min,
|
return const Center(
|
||||||
children: [
|
child: Column(
|
||||||
Icon(Icons.broken_image, size: 48, color: Colors.white38),
|
mainAxisSize: MainAxisSize.min,
|
||||||
SizedBox(height: 8),
|
children: [
|
||||||
Text('Resim yüklenemedi',
|
Icon(Icons.broken_image, size: 48, color: Colors.white38),
|
||||||
style: TextStyle(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)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
// Alt bilgi çubuğu
|
||||||
|
Positioned(
|
||||||
|
bottom: 4,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
child: Center(
|
||||||
|
child: ListenableBuilder(
|
||||||
|
listenable: _controller,
|
||||||
|
builder: (context, _) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 3),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.black54,
|
||||||
|
borderRadius: BorderRadius.circular(6),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'${_imageInfo()} | ${_zoomPercent()}',
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white70,
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: 'monospace',
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
);
|
||||||
),
|
},
|
||||||
)
|
|
||||||
: 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)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Binary file not shown.
Reference in new issue
Block a user