feat: update Kotlin plugin version and modify API endpoints for improved functionality

This commit is contained in:
Vaibhav Surve 2025-11-25 13:19:00 +05:30
parent c69e4280ef
commit 3fa578f1b4
5 changed files with 363 additions and 134 deletions

View File

@ -19,7 +19,7 @@ pluginManagement {
plugins { plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0" id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.6.0" apply false id "com.android.application" version "8.6.0" apply false
id "org.jetbrains.kotlin.android" version "1.8.22" apply false id "org.jetbrains.kotlin.android" version "2.2.21" apply false
id("com.google.gms.google-services") version "4.4.2" apply false id("com.google.gms.google-services") version "4.4.2" apply false
} }

View File

@ -1,9 +1,9 @@
class ApiEndpoints { class ApiEndpoints {
static const String baseUrl = "https://stageapi.marcoaiot.com/api"; // static const String baseUrl = "https://stageapi.marcoaiot.com/api";
// static const String baseUrl = "https://api.marcoaiot.com/api"; // static const String baseUrl = "https://api.marcoaiot.com/api";
// static const String baseUrl = "https://devapi.marcoaiot.com/api"; // static const String baseUrl = "https://devapi.marcoaiot.com/api";
// static const String baseUrl = "https://mapi.marcoaiot.com/api"; // static const String baseUrl = "https://mapi.marcoaiot.com/api";
// static const String baseUrl = "https://api.onfieldwork.com/api"; static const String baseUrl = "https://api.onfieldwork.com/api";
static const String getMasterCurrencies = "/Master/currencies/list"; static const String getMasterCurrencies = "/Master/currencies/list";

View File

@ -1,14 +1,27 @@
import 'dart:io'; import 'dart:io';
import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:photo_view/photo_view.dart';
import 'package:share_plus/share_plus.dart';
import 'package:gallery_saver_plus/gallery_saver.dart';
import 'package:http/http.dart' as http;
import 'package:path_provider/path_provider.dart';
import 'package:get/get.dart';
import 'package:on_field_work/helpers/widgets/my_confirmation_dialog.dart';
class ImageViewerDialog extends StatefulWidget { class ImageViewerDialog extends StatefulWidget {
final List<dynamic> imageSources; final List<dynamic> imageSources;
final int initialIndex; final int initialIndex;
final List<String>? captions;
final String? title;
const ImageViewerDialog({ const ImageViewerDialog({
Key? key, Key? key,
required this.imageSources, required this.imageSources,
required this.initialIndex, required this.initialIndex,
this.captions,
this.title,
}) : super(key: key); }) : super(key: key);
@override @override
@ -28,87 +41,291 @@ class _ImageViewerDialogState extends State<ImageViewerDialog> {
_controller = PageController(initialPage: widget.initialIndex); _controller = PageController(initialPage: widget.initialIndex);
} }
Future<void> shareImage(dynamic image) async {
try {
if (isFile(image)) {
await Share.shareXFiles([XFile(image.path)],
text: 'Check out this image!');
} else if (image is String) {
await Share.share(image, subject: 'Check out this image!');
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to share image: $e')),
);
}
}
Future<void> downloadImage(dynamic image) async {
try {
if (isFile(image)) {
await GallerySaver.saveImage(image.path);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Image saved to gallery')),
);
} else if (image is String) {
final response = await http.get(Uri.parse(image));
final bytes = response.bodyBytes;
final tempDir = await getTemporaryDirectory();
final filePath =
'${tempDir.path}/${DateTime.now().millisecondsSinceEpoch}.png';
final file = await File(filePath).writeAsBytes(bytes);
await GallerySaver.saveImage(file.path);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Image saved to gallery')),
);
}
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to save image: $e')),
);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final double dialogHeight = MediaQuery.of(context).size.height * 0.55; return Scaffold(
backgroundColor: Colors.black,
body: Stack(
children: [
Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
colors: [Color(0xFF101018), Color(0xFF050509)],
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
),
return Dialog( // Add vertical padding to avoid overlap with header/footer
backgroundColor: Colors.transparent, Padding(
insetPadding: const EdgeInsets.symmetric(horizontal: 24, vertical: 100), padding:
const EdgeInsets.only(top: 72, bottom: 110), // Adjust as needed
child: PageView.builder(
controller: _controller,
itemCount: widget.imageSources.length,
onPageChanged: (index) => setState(() => currentIndex = index),
itemBuilder: (context, index) {
final item = widget.imageSources[index];
final ImageProvider provider = isFile(item)
? FileImage(item)
: CachedNetworkImageProvider(item);
return Center(
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(20),
child: PhotoView(
imageProvider: provider,
backgroundDecoration:
const BoxDecoration(color: Colors.transparent),
loadingBuilder: (context, event) =>
const Center(child: CircularProgressIndicator()),
errorBuilder: (context, error, stackTrace) =>
const Center(
child: Icon(Icons.broken_image,
size: 64, color: Colors.white54),
),
minScale: PhotoViewComputedScale.contained,
maxScale: PhotoViewComputedScale.covered * 3,
),
),
);
},
),
),
// Top blurred app bar with back button and title
SafeArea(
child: Padding(
padding: const EdgeInsets.all(12),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 10, sigmaY: 10),
child: Container( child: Container(
height: dialogHeight, height: 50,
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: Colors.black.withOpacity(0.4),
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
color: Colors.black.withValues(alpha: 0.1), color: Colors.black.withOpacity(0.4),
blurRadius: 12, blurRadius: 8,
offset: const Offset(0, 4), offset: Offset(0, 3),
),
],
),
child: Row(
children: [
Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(24),
onTap: () => Navigator.of(context).pop(),
child: const Padding(
padding: EdgeInsets.all(8),
child: Icon(Icons.arrow_back_ios_new_rounded,
color: Colors.white, size: 24),
),
),
),
const SizedBox(width: 8),
Expanded(
child: Text(
widget.title ?? 'Preview',
style: const TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w600,
letterSpacing: 0.5,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
),
),
),
),
// Bottom control panel with blur and rounded corners
Align(
alignment: Alignment.bottomCenter,
child: SafeArea(
top: false,
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
child: ClipRRect(
borderRadius: BorderRadius.circular(22),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 12, sigmaY: 12),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 20, vertical: 14),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.4),
boxShadow: [
BoxShadow(
color: Colors.black54,
blurRadius: 10,
offset: Offset(0, 4),
), ),
], ],
), ),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min,
children: [ children: [
// Top Close Button Row(
Align( children: [
alignment: Alignment.topRight, Text(
child: IconButton(
icon: const Icon(Icons.close, size: 26),
onPressed: () => Navigator.of(context).pop(),
splashRadius: 22,
tooltip: 'Close',
),
),
// Image Viewer
Expanded(
child: PageView.builder(
controller: _controller,
itemCount: widget.imageSources.length,
onPageChanged: (index) {
setState(() => currentIndex = index);
},
itemBuilder: (context, index) {
final item = widget.imageSources[index];
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12),
child: isFile(item)
? Image.file(item, fit: BoxFit.contain)
: Image.network(
item,
fit: BoxFit.contain,
loadingBuilder: (context, child, loadingProgress) {
if (loadingProgress == null) return child;
return Center(
child: CircularProgressIndicator(
value: loadingProgress.expectedTotalBytes != null
? loadingProgress.cumulativeBytesLoaded /
(loadingProgress.expectedTotalBytes ?? 1)
: null,
),
);
},
errorBuilder: (context, error, stackTrace) =>
const Center(
child: Icon(Icons.broken_image, size: 48, color: Colors.grey),
),
),
);
},
),
),
// Index Indicator
Padding(
padding: const EdgeInsets.only(top: 8, bottom: 12),
child: Text(
'${currentIndex + 1}/${widget.imageSources.length}', '${currentIndex + 1}/${widget.imageSources.length}',
style: const TextStyle( style: const TextStyle(
color: Colors.black87, color: Colors.white,
fontSize: 14, fontWeight: FontWeight.bold,
fontWeight: FontWeight.w500, fontSize: 15,
),
),
const SizedBox(width: 16),
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
widget.imageSources.length, (index) {
final bool active = index == currentIndex;
return AnimatedContainer(
duration:
const Duration(milliseconds: 300),
margin: const EdgeInsets.symmetric(
horizontal: 4),
height: 8,
width: active ? 22 : 8,
decoration: BoxDecoration(
color: active
? Colors.white
: Colors.white54,
borderRadius: BorderRadius.circular(30),
boxShadow: active
? [
BoxShadow(
color: Colors.white70,
blurRadius: 6,
spreadRadius: 1,
)
]
: [],
),
);
}),
),
),
const SizedBox(width: 16),
Tooltip(
message: 'Download Image',
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(30),
onTap: () async {
final confirmed = await Get.dialog<bool>(
ConfirmDialog(
title: "Download Image",
message:
"Do you want to download this image to your device?",
confirmText: "Download",
cancelText: "Cancel",
onConfirm: () async {
// Call your existing download method here for the current image
await downloadImage(widget
.imageSources[currentIndex]);
},
confirmColor: Colors.blueAccent,
confirmIcon: Icons.download_rounded,
icon: Icons.download_rounded,
errorMessage:
"Failed to download image. Please try again.",
loadingText: "Downloading…",
),
barrierDismissible: false,
);
// Optionally handle if the dialog was cancelled (confirmed == false or null)
if (confirmed != true) {
// Download cancelled by user
}
},
child: const Padding(
padding: EdgeInsets.all(6),
child: Icon(Icons.download_rounded,
color: Colors.white, size: 28),
),
),
),
),
],
),
if (widget.captions != null &&
widget.captions!.isNotEmpty &&
currentIndex < widget.captions!.length)
Padding(
padding: const EdgeInsets.only(top: 8),
child: AnimatedOpacity(
opacity: 1.0,
duration: const Duration(milliseconds: 400),
child: Text(
widget.captions![currentIndex],
style: const TextStyle(
color: Colors.white70,
fontSize: 13,
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
), ),
), ),
), ),
@ -116,6 +333,12 @@ class _ImageViewerDialogState extends State<ImageViewerDialog> {
), ),
), ),
), ),
),
),
),
),
],
),
); );
} }
} }

View File

@ -68,7 +68,7 @@ class ConfirmDialog extends StatelessWidget {
maxWidth: 480, maxWidth: 480,
), ),
child: Padding( child: Padding(
padding: const EdgeInsets.fromLTRB(24, 20, 24, 16), padding: const EdgeInsets.fromLTRB(12, 10, 12, 8),
child: _ContentView( child: _ContentView(
title: title, title: title,
message: message, message: message,
@ -161,7 +161,7 @@ class _ContentView extends StatelessWidget {
Align( Align(
alignment: Alignment.center, alignment: Alignment.center,
child: SizedBox( child: SizedBox(
width: double.infinity, // allow full available width width: double.infinity,
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@ -174,7 +174,9 @@ class _ContentView extends StatelessWidget {
textColor: colorScheme.onSurface, textColor: colorScheme.onSurface,
isFilled: false, isFilled: false,
isLoading: false, isLoading: false,
onPressed: loading.value ? null : () => Navigator.pop(context, false), onPressed: loading.value
? null
: () => Navigator.pop(context, false),
), ),
), ),
), ),
@ -210,7 +212,6 @@ class _ContentView extends StatelessWidget {
), ),
), ),
), ),
], ],
); );
} }

View File

@ -12,7 +12,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# In Android, build-name is used as versionName while build-number used as versionCode. # In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at # Document Reference for iOS Versioning at:
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
@ -32,7 +32,6 @@ dependencies:
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8 cupertino_icons: ^1.0.8
@ -56,7 +55,7 @@ dependencies:
appflowy_board: ^0.1.2 appflowy_board: ^0.1.2
syncfusion_flutter_calendar: ^29.1.40 syncfusion_flutter_calendar: ^29.1.40
syncfusion_flutter_maps: ^29.1.40 syncfusion_flutter_maps: ^29.1.40
http: ^1.2.2 http: ^1.6.0
geolocator: ^14.0.2 geolocator: ^14.0.2
permission_handler: ^12.0.1 permission_handler: ^12.0.1
image: ^4.0.17 image: ^4.0.17
@ -84,7 +83,10 @@ dependencies:
timeago: ^3.7.1 timeago: ^3.7.1
cached_network_image: ^3.4.1 cached_network_image: ^3.4.1
gallery_saver_plus: ^3.2.9
share_plus: ^12.0.1
timeline_tile: ^2.0.0 timeline_tile: ^2.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
@ -147,3 +149,6 @@ flutter:
# #
# For details regarding fonts from package dependencies, # For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package # see https://flutter.dev/to/font-from-package
dependency_overrides:
http: ^1.6.0