V1 - Personal Account and Budgeting Done

Personal Account and Budgeting
This commit is contained in:
2026-08-16 20:13:21 +05:30
commit 17c0526ed6
247 changed files with 19002 additions and 0 deletions

View File

@@ -0,0 +1,138 @@
import 'package:flutter/material.dart';
class AttachmentGalleryScreen extends StatefulWidget {
final List<ImageProvider> images;
final int initialIndex;
const AttachmentGalleryScreen({
super.key,
required this.images,
required this.initialIndex,
});
@override
State<AttachmentGalleryScreen> createState() => _AttachmentGalleryScreenState();
}
class _AttachmentGalleryScreenState extends State<AttachmentGalleryScreen> {
late PageController _pageController;
int _currentIndex = 0;
@override
void initState() {
super.initState();
_currentIndex = widget.initialIndex;
_pageController = PageController(initialPage: widget.initialIndex);
}
@override
void dispose() {
_pageController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.black,
iconTheme: const IconThemeData(color: Colors.white),
title: Text(
'${_currentIndex + 1} / ${widget.images.length}',
style: const TextStyle(color: Colors.white),
),
),
body: PageView.builder(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_currentIndex = index;
});
},
itemCount: widget.images.length,
itemBuilder: (context, index) {
return _ZoomableImage(image: widget.images[index]);
},
),
);
}
}
class _ZoomableImage extends StatefulWidget {
final ImageProvider image;
const _ZoomableImage({required this.image});
@override
State<_ZoomableImage> createState() => _ZoomableImageState();
}
class _ZoomableImageState extends State<_ZoomableImage> with SingleTickerProviderStateMixin {
final TransformationController _transformationController = TransformationController();
late AnimationController _animationController;
Animation<Matrix4>? _animation;
TapDownDetails? _doubleTapDetails;
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 200),
)..addListener(() {
if (_animation != null) {
_transformationController.value = _animation!.value;
}
});
}
@override
void dispose() {
_animationController.dispose();
_transformationController.dispose();
super.dispose();
}
void _handleDoubleTap() {
if (_doubleTapDetails == null) return;
final position = _doubleTapDetails!.localPosition;
final matrix = _transformationController.value;
final scale = matrix.getMaxScaleOnAxis();
Matrix4 endMatrix;
if (scale > 1.0) {
// Zoom out
endMatrix = Matrix4.identity();
} else {
// Zoom in
endMatrix = Matrix4.identity()
..translate(-position.dx * 1.5, -position.dy * 1.5)
..scale(2.5);
}
_animation = Matrix4Tween(
begin: _transformationController.value,
end: endMatrix,
).animate(CurveTween(curve: Curves.easeInOut).animate(_animationController));
_animationController.forward(from: 0);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onDoubleTapDown: (d) => _doubleTapDetails = d,
onDoubleTap: _handleDoubleTap,
child: InteractiveViewer(
transformationController: _transformationController,
minScale: 1.0,
maxScale: 4.0,
child: Image(
image: widget.image,
fit: BoxFit.contain,
),
),
);
}
}