commit 278ee8989d2889c4f2f8f1831def02631e552ecb Author: amirdevd12 Date: Tue Aug 25 17:23:48 2026 +0330 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..33a7f94 --- /dev/null +++ b/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.build/ +.buildlog/ +.history +.svn/ +.swiftpm/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.tool-data/ +.flutter-plugins-dependencies +.pub-cache/ +.pub/ +/build/ +/coverage/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..83b8b83 --- /dev/null +++ b/.metadata @@ -0,0 +1,33 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "67323de285b00232883f53b84095eb72be97d35c" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 67323de285b00232883f53b84095eb72be97d35c + base_revision: 67323de285b00232883f53b84095eb72be97d35c + - platform: android + create_revision: 67323de285b00232883f53b84095eb72be97d35c + base_revision: 67323de285b00232883f53b84095eb72be97d35c + - platform: web + create_revision: 67323de285b00232883f53b84095eb72be97d35c + base_revision: 67323de285b00232883f53b84095eb72be97d35c + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/README.md b/README.md new file mode 100644 index 0000000..13ad3e1 --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# نمایش دقت موقعیت مکانی با Flutter + +این برنامه روی Android و Web مختصات زیر را دریافت و نمایش می‌دهد: + +- عرض جغرافیایی (Latitude) +- طول جغرافیایی (Longitude) +- دقت تخمینی موقعیت بر حسب متر (Accuracy) +- زمان ثبت موقعیت + +همین اطلاعات با `debugPrint` در کنسول نیز چاپ می‌شوند. + +## اجرا + +```bash +flutter pub get +flutter run +``` + +برای ساخت خروجی وب: + +```bash +flutter build web --release +``` + +خروجی در `build/web` ساخته می‌شود. مرورگرها به دلایل امنیتی Location را فقط روی +`HTTPS` یا `localhost` ارائه می‌کنند؛ بنابراین نسخه‌ای که روی دامنه قرار می‌گیرد باید +حتماً HTTPS داشته باشد. + +## انتشار روی Cloudflare + +تنظیمات Workers Static Assets در `wrangler.jsonc` قرار دارد. بعد از هر build، فایل +ignore مخصوص Cloudflare را به خروجی کپی و سپس deploy کنید: + +```powershell +flutter build web --release --no-wasm-dry-run +Copy-Item cloudflare.assetsignore build/web/.assetsignore +npx wrangler deploy --config wrangler.jsonc +``` + +CanvasKit از CDN رسمی و نسخه‌بندی‌شده Flutter دریافت می‌شود تا فایل‌های renderer +بزرگ‌تر از محدودیت آپلود Cloudflare داخل deployment قرار نگیرند. + +برای ساخت APK: + +```bash +flutter build apk --release +``` + +مجوزهای `ACCESS_FINE_LOCATION` و `ACCESS_COARSE_LOCATION` در AndroidManifest ثبت +شده‌اند و برنامه هنگام اجرا نیز مجوز لازم را از کاربر درخواست می‌کند. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..0d29021 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,28 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/android/.gitignore b/android/.gitignore new file mode 100644 index 0000000..be3943c --- /dev/null +++ b/android/.gitignore @@ -0,0 +1,14 @@ +gradle-wrapper.jar +/.gradle +/captures/ +/gradlew +/gradlew.bat +/local.properties +GeneratedPluginRegistrant.java +.cxx/ + +# Remember to never publicly share your keystore. +# See https://flutter.dev/to/reference-keystore +key.properties +**/*.keystore +**/*.jks diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts new file mode 100644 index 0000000..efe4dda --- /dev/null +++ b/android/app/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.example.location_accuracy_app" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.location_accuracy_app" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a869523 --- /dev/null +++ b/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/kotlin/com/example/location_accuracy_app/MainActivity.kt b/android/app/src/main/kotlin/com/example/location_accuracy_app/MainActivity.kt new file mode 100644 index 0000000..cee94ea --- /dev/null +++ b/android/app/src/main/kotlin/com/example/location_accuracy_app/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.location_accuracy_app + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity : FlutterActivity() diff --git a/android/app/src/main/res/drawable-v21/launch_background.xml b/android/app/src/main/res/drawable-v21/launch_background.xml new file mode 100644 index 0000000..f74085f --- /dev/null +++ b/android/app/src/main/res/drawable-v21/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/drawable/launch_background.xml b/android/app/src/main/res/drawable/launch_background.xml new file mode 100644 index 0000000..304732f --- /dev/null +++ b/android/app/src/main/res/drawable/launch_background.xml @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..db77bb4 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..17987b7 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..09d4391 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..d5f1c8d Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..4d6372e Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/values-night/styles.xml b/android/app/src/main/res/values-night/styles.xml new file mode 100644 index 0000000..06952be --- /dev/null +++ b/android/app/src/main/res/values-night/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..cb1ef88 --- /dev/null +++ b/android/app/src/main/res/values/styles.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/android/app/src/profile/AndroidManifest.xml b/android/app/src/profile/AndroidManifest.xml new file mode 100644 index 0000000..399f698 --- /dev/null +++ b/android/app/src/profile/AndroidManifest.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 0000000..dbee657 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,24 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = + rootProject.layout.buildDirectory + .dir("../../build") + .get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/android/gradle.properties b/android/gradle.properties new file mode 100644 index 0000000..fbee1d8 --- /dev/null +++ b/android/gradle.properties @@ -0,0 +1,2 @@ +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e4ef43f --- /dev/null +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 0000000..ca7fe06 --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1,26 @@ +pluginManagement { + val flutterSdkPath = + run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + id("org.jetbrains.kotlin.android") version "2.2.20" apply false +} + +include(":app") diff --git a/cloudflare.assetsignore b/cloudflare.assetsignore new file mode 100644 index 0000000..196e4a9 --- /dev/null +++ b/cloudflare.assetsignore @@ -0,0 +1,2 @@ +# CanvasKit is loaded from Flutter's versioned Google CDN by flutter_bootstrap.js. +canvaskit/ diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..d816053 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,307 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:geolocator/geolocator.dart'; + +void main() => runApp(const LocationApp()); + +class LocationApp extends StatelessWidget { + const LocationApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + debugShowCheckedModeBanner: false, + title: 'دقت موقعیت مکانی', + theme: ThemeData( + colorScheme: ColorScheme.fromSeed( + seedColor: const Color(0xFF006C67), + brightness: Brightness.light, + ), + useMaterial3: true, + ), + home: const Directionality( + textDirection: TextDirection.rtl, + child: LocationPage(), + ), + ); + } +} + +class LocationPage extends StatefulWidget { + const LocationPage({super.key}); + + @override + State createState() => _LocationPageState(); +} + +class _LocationPageState extends State { + final Stopwatch _stopwatch = Stopwatch(); + Timer? _timer; + Position? _position; + Duration _elapsed = Duration.zero; + String _status = 'دریافت خودکار موقعیت مکانی در حال شروع است…'; + bool _loading = false; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) => _getLocation()); + } + + @override + void dispose() { + _timer?.cancel(); + _stopwatch.stop(); + super.dispose(); + } + + void _startTimer() { + _timer?.cancel(); + _elapsed = Duration.zero; + _stopwatch + ..reset() + ..start(); + _timer = Timer.periodic(const Duration(milliseconds: 100), (_) { + if (mounted) setState(() => _elapsed = _stopwatch.elapsed); + }); + } + + void _stopTimer() { + _stopwatch.stop(); + _timer?.cancel(); + _timer = null; + _elapsed = _stopwatch.elapsed; + } + + Future _getLocation() async { + if (_loading) return; + + _startTimer(); + setState(() { + _loading = true; + _status = 'در حال بررسی مجوز و دریافت موقعیت مکانی…'; + }); + + try { + final serviceEnabled = await Geolocator.isLocationServiceEnabled(); + if (!serviceEnabled) { + _showError( + kIsWeb + ? 'دسترسی موقعیت مکانی مرورگر یا دستگاه خاموش است.' + : 'سرویس Location دستگاه خاموش است؛ آن را روشن کنید و صفحه را دوباره باز کنید.', + ); + return; + } + + var permission = await Geolocator.checkPermission(); + if (permission == LocationPermission.denied) { + permission = await Geolocator.requestPermission(); + } + + if (permission == LocationPermission.denied) { + _showError( + 'مجوز موقعیت مکانی داده نشد. مجوز را از تنظیمات مرورگر فعال و صفحه را دوباره باز کنید.', + ); + return; + } + + if (permission == LocationPermission.deniedForever) { + _showError( + kIsWeb + ? 'دسترسی Location برای این سایت مسدود شده است؛ آن را از تنظیمات سایت مرورگر فعال کنید.' + : 'مجوز Location مسدود شده است؛ آن را از تنظیمات برنامه فعال کنید.', + ); + return; + } + + final position = await Geolocator.getCurrentPosition( + desiredAccuracy: LocationAccuracy.best, + timeLimit: const Duration(seconds: 30), + ); + + _stopTimer(); + debugPrint('---------- LOCATION RESULT ----------'); + debugPrint('Latitude : ${position.latitude}'); + debugPrint('Longitude: ${position.longitude}'); + debugPrint('Accuracy : ${position.accuracy} meters'); + debugPrint('Elapsed : ${_elapsed.inMilliseconds} ms'); + debugPrint('Timestamp: ${position.timestamp.toIso8601String()}'); + debugPrint('-------------------------------------'); + + if (!mounted) return; + setState(() { + _position = position; + _status = 'موقعیت مکانی با موفقیت دریافت شد.'; + }); + } on TimeoutException { + _showError( + 'دریافت موقعیت بیش از ۳۰ ثانیه طول کشید؛ در فضای باز صفحه را دوباره بارگذاری کنید.', + ); + } catch (error, stackTrace) { + debugPrint('Location error: $error'); + debugPrintStack(stackTrace: stackTrace); + _showError( + kIsWeb + ? 'دریافت موقعیت ممکن نشد. دسترسی Location مرورگر را بررسی کنید. خطا: $error' + : 'دریافت موقعیت ممکن نشد: $error', + ); + } finally { + _stopTimer(); + if (mounted) setState(() => _loading = false); + } + } + + void _showError(String message) { + if (!mounted) return; + setState(() => _status = message); + } + + String get _formattedElapsed { + final seconds = _elapsed.inMilliseconds / 1000; + return '${seconds.toStringAsFixed(1)} ثانیه'; + } + + @override + Widget build(BuildContext context) { + final position = _position; + + return Scaffold( + appBar: AppBar(title: const Text('موقعیت مکانی من'), centerTitle: true), + body: SafeArea( + child: Center( + child: SingleChildScrollView( + padding: const EdgeInsets.all(20), + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 560), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Icon( + position == null + ? Icons.location_searching_rounded + : Icons.my_location_rounded, + size: 76, + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox(height: 18), + Text( + _status, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 18), + Card( + margin: EdgeInsets.zero, + color: Theme.of(context).colorScheme.primaryContainer, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 14, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (_loading) ...[ + const SizedBox.square( + dimension: 18, + child: CircularProgressIndicator(strokeWidth: 2), + ), + const SizedBox(width: 10), + ] else + const Icon(Icons.timer_outlined), + const SizedBox(width: 8), + Text( + '${_loading ? 'زمان سپری‌شده' : 'زمان نهایی'}: $_formattedElapsed', + style: Theme.of(context).textTheme.titleMedium, + ), + ], + ), + ), + ), + const SizedBox(height: 20), + if (position != null) ...[ + _LocationValue( + icon: Icons.north_rounded, + label: 'عرض جغرافیایی (Latitude)', + value: position.latitude.toStringAsFixed(7), + ), + const SizedBox(height: 12), + _LocationValue( + icon: Icons.east_rounded, + label: 'طول جغرافیایی (Longitude)', + value: position.longitude.toStringAsFixed(7), + ), + const SizedBox(height: 12), + _LocationValue( + icon: Icons.gps_fixed_rounded, + label: 'دقت تخمینی موقعیت (Accuracy)', + value: '${position.accuracy.toStringAsFixed(2)} متر', + ), + const SizedBox(height: 12), + _LocationValue( + icon: Icons.schedule_rounded, + label: 'زمان ثبت', + value: position.timestamp.toLocal().toString(), + ), + const SizedBox(height: 12), + Text( + 'عدد Accuracy شعاع خطای تخمینی است؛ هرچه کمتر باشد، موقعیت دقیق‌تر است.', + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ], + ), + ), + ), + ), + ), + ); + } +} + +class _LocationValue extends StatelessWidget { + const _LocationValue({ + required this.icon, + required this.label, + required this.value, + }); + + final IconData icon; + final String label; + final String value; + + @override + Widget build(BuildContext context) { + return Card( + margin: EdgeInsets.zero, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), + child: Row( + children: [ + Icon(icon, color: Theme.of(context).colorScheme.primary), + const SizedBox(width: 12), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(label, style: Theme.of(context).textTheme.bodySmall), + const SizedBox(height: 3), + Directionality( + textDirection: TextDirection.ltr, + child: SelectableText( + value, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..d8d9e6c --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,306 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + async: + dependency: transitive + description: + name: async + sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37 + url: "https://pub.dev" + source: hosted + version: "2.13.1" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf + url: "https://pub.dev" + source: hosted + version: "3.0.7" + cupertino_icons: + dependency: "direct main" + description: + name: cupertino_icons + sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd" + url: "https://pub.dev" + source: hosted + version: "1.0.9" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + geolocator: + dependency: "direct main" + description: + name: geolocator + sha256: "5c23f3613f50586c0bbb2b8f970240ae66b3bd992088cf60dd5ee2e6f7dde3a8" + url: "https://pub.dev" + source: hosted + version: "9.0.2" + geolocator_android: + dependency: transitive + description: + name: geolocator_android + sha256: "80cd39a94100957431a6dd37cebd686f6d446bbc9a884d90c9a07b34fd28b923" + url: "https://pub.dev" + source: hosted + version: "4.5.5" + geolocator_apple: + dependency: transitive + description: + name: geolocator_apple + sha256: dbdd8789d5aaf14cf69f74d4925ad1336b4433a6efdf2fce91e8955dc921bf22 + url: "https://pub.dev" + source: hosted + version: "2.3.13" + geolocator_platform_interface: + dependency: transitive + description: + name: geolocator_platform_interface + sha256: "30cb64f0b9adcc0fb36f628b4ebf4f731a2961a0ebd849f4b56200205056fe67" + url: "https://pub.dev" + source: hosted + version: "4.2.6" + geolocator_web: + dependency: transitive + description: + name: geolocator_web + sha256: "102e7da05b48ca6bf0a5bda0010f886b171d1a08059f01bfe02addd0175ebece" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + geolocator_windows: + dependency: transitive + description: + name: geolocator_windows + sha256: "4f4218f122a6978d0ad655fa3541eea74c67417440b09f0657238810d5af6bdc" + url: "https://pub.dev" + source: hosted + version: "0.1.3" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + lints: + dependency: transitive + description: + name: lints + sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + url: "https://pub.dev" + source: hosted + version: "1.17.0" + path: + dependency: transitive + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + source_span: + dependency: transitive + description: + name: source_span + sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab" + url: "https://pub.dev" + source: hosted + version: "1.10.2" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" + url: "https://pub.dev" + source: hosted + version: "1.12.1" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" + url: "https://pub.dev" + source: hosted + version: "1.4.1" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" + url: "https://pub.dev" + source: hosted + version: "1.2.2" + test_api: + dependency: transitive + description: + name: test_api + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 + url: "https://pub.dev" + source: hosted + version: "0.7.7" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + uuid: + dependency: transitive + description: + name: uuid + sha256: "9b129329f58692f6e6578329498a8fe9fbe98f090beb764ffbb8ee2eadd01dcd" + url: "https://pub.dev" + source: hosted + version: "4.6.0" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b + url: "https://pub.dev" + source: hosted + version: "2.2.0" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "5f37239c4851efcef929cea7824e76df7f2f0970aef85d66bbc430afa40e72f0" + url: "https://pub.dev" + source: hosted + version: "15.3.0" +sdks: + dart: ">=3.10.8 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..31d91f7 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,90 @@ +name: location_accuracy_app +description: "نمایش مختصات و دقت موقعیت مکانی در Android و Web" +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# 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 +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# 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 +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ^3.10.8 + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.8 + geolocator: ^9.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^6.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/to/asset-from-package + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/to/font-from-package diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..978732d --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:location_accuracy_app/main.dart'; + +void main() { + testWidgets('دریافت موقعیت بدون دکمه و به‌صورت خودکار شروع می‌شود', ( + tester, + ) async { + await tester.pumpWidget(const LocationApp()); + + expect(find.text('موقعیت مکانی من'), findsOneWidget); + expect(find.byType(FilledButton), findsNothing); + expect(find.textContaining('زمان'), findsOneWidget); + }); +} diff --git a/web/favicon.png b/web/favicon.png new file mode 100644 index 0000000..8aaa46a Binary files /dev/null and b/web/favicon.png differ diff --git a/web/flutter_bootstrap.js b/web/flutter_bootstrap.js new file mode 100644 index 0000000..c4e3802 --- /dev/null +++ b/web/flutter_bootstrap.js @@ -0,0 +1,11 @@ +{{flutter_js}} +{{flutter_build_config}} + +_flutter.loader.load({ + config: { + canvasKitBaseUrl: + "https://www.gstatic.com/flutter-canvaskit/" + + _flutter.buildConfig.engineRevision + + "/", + }, +}); diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png new file mode 100644 index 0000000..b749bfe Binary files /dev/null and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png new file mode 100644 index 0000000..88cfd48 Binary files /dev/null and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png new file mode 100644 index 0000000..eb9b4d7 Binary files /dev/null and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png new file mode 100644 index 0000000..d69c566 Binary files /dev/null and b/web/icons/Icon-maskable-512.png differ diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..1c9c049 --- /dev/null +++ b/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + موقعیت مکانی من + + + + + + diff --git a/web/manifest.json b/web/manifest.json new file mode 100644 index 0000000..c82210d --- /dev/null +++ b/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "موقعیت مکانی من", + "short_name": "موقعیت من", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/wrangler.jsonc b/wrangler.jsonc new file mode 100644 index 0000000..ff261fe --- /dev/null +++ b/wrangler.jsonc @@ -0,0 +1,8 @@ +{ + "name": "location-accuracy-mobile-test", + "compatibility_date": "2026-08-24", + "assets": { + "directory": "./build/web", + "not_found_handling": "single-page-application" + } +}