~/icsd.ir — bash
SYSTEM_ONLINE

قابلیت‌های Platform

اپلیکیشن‌های موبایل به قابلیت‌های دستگاه نیاز دارند: دوربین، اعلان، GPS، فایل سیستم. در این فصل با ابزارهای Flutter برای دسترسی به این قابلیت‌ها آشنا می‌شویم.

۱۱.۱ مقدمه

اپلیکیشن‌های موبایل به قابلیت‌های دستگاه نیاز دارند: دوربین، اعلان، GPS، فایل سیستم. در این فصل با ابزارهای Flutter برای دسترسی به این قابلیت‌ها آشنا می‌شویم.

۱۱.۲ Permissions

قبل از استفاده از قابلیت‌های حساس، باید اجازه کاربر را بگیریم.


flutter pub add permission_handler
    

import "package:permission_handler/permission_handler.dart";

// بررسی و درخواست
Future requestCameraPermission() async {
  final status = await Permission.camera.status;
  
  if (status.isGranted) return true;
  if (status.isPermanentlyDenied) {
    // باید از Settings فعال کند
    await openAppSettings();
    return false;
  }
  
  final result = await Permission.camera.request();
  return result.isGranted;
}

// چندین permission با هم
final statuses = await [
  Permission.camera,
  Permission.microphone,
  Permission.location,
].request();

if (statuses[Permission.camera]!.isGranted &&
    statuses[Permission.microphone]!.isGranted) {
  // OK
}
    

تنظیمات Android


<!-- android/app/src/main/AndroidManifest.xml -->
<manifest ...>
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <application ...>...</application>
</manifest>
    

تنظیمات iOS


<!-- ios/Runner/Info.plist -->
<key>NSCameraUsageDescription</key>
<string>برای گرفتن عکس از محصولات</string>

<key>NSMicrophoneUsageDescription</key>
<string>برای ضبط صدا</string>

<key>NSLocationWhenInUseUsageDescription</key>
<string>برای پیدا کردن نزدیک‌ترین فروشگاه</string>

<key>NSPhotoLibraryUsageDescription</key>
<string>برای انتخاب عکس از گالری</string>
    

۱۱.۳ دوربین


flutter pub add camera
    

import "package:camera/camera.dart";

class CameraPage extends StatefulWidget {
  @override
  State createState() => _CameraPageState();
}

class _CameraPageState extends State {
  CameraController? _controller;
  List? _cameras;
  
  @override
  void initState() {
    super.initState();
    _initCamera();
  }
  
  Future _initCamera() async {
    _cameras = await availableCameras();
    if (_cameras!.isEmpty) return;
    
    _controller = CameraController(
      _cameras!.first,  // دوربین پشت
      ResolutionPreset.high,
      enableAudio: false,
    );
    
    await _controller!.initialize();
    if (mounted) setState(() {});
  }
  
  Future _takePicture() async {
    if (_controller == null || !_controller!.value.isInitialized) return;
    
    try {
      final image = await _controller!.takePicture();
      // image.path → ذخیره یا پردازش
      print("ذخیره شد: ${image.path}");
    } catch (e) {
      print("خطا: $e");
    }
  }
  
  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }
  
  @override
  Widget build(BuildContext context) {
    if (_controller == null || !_controller!.value.isInitialized) {
      return const Center(child: CircularProgressIndicator());
    }
    
    return Stack(
      children: [
        CameraPreview(_controller!),
        Positioned(
          bottom: 32,
          left: 0,
          right: 0,
          child: Center(
            child: FloatingActionButton(
              onPressed: _takePicture,
              child: const Icon(Icons.camera),
            ),
          ),
        ),
      ],
    );
  }
}
    

نکته: برای موارد ساده (انتخاب از gallery یا گرفتن عکس)، image_picker از فصل ۹ کافی است. camera برای custom UI استفاده می‌شود.

۱۱.۴ Push Notification

Local Notification


flutter pub add flutter_local_notifications
    

import "package:flutter_local_notifications/flutter_local_notifications.dart";

class NotificationService {
  final _plugin = FlutterLocalNotificationsPlugin();
  
  Future init() async {
    const android = AndroidInitializationSettings("@mipmap/ic_launcher");
    const ios = DarwinInitializationSettings();
    const settings = InitializationSettings(android: android, iOS: ios);
    
    await _plugin.initialize(
      settings,
      onDidReceiveNotificationResponse: (response) {
        // کلیک روی notification
        print("Tapped: ${response.payload}");
      },
    );
  }
  
  Future showNotification({
    required String title,
    required String body,
    String? payload,
  }) async {
    const android = AndroidNotificationDetails(
      "main_channel",
      "اعلان‌های اصلی",
      importance: Importance.max,
      priority: Priority.high,
    );
    
    const ios = DarwinNotificationDetails();
    
    await _plugin.show(
      0,
      title,
      body,
      const NotificationDetails(android: android, iOS: ios),
      payload: payload,
    );
  }
  
  Future scheduleNotification({
    required DateTime scheduledTime,
    required String title,
    required String body,
  }) async {
    await _plugin.zonedSchedule(
      0,
      title,
      body,
      tz.TZDateTime.from(scheduledTime, tz.local),
      const NotificationDetails(...),
      uiLocalNotificationDateInterpretation: 
          UILocalNotificationDateInterpretation.absoluteTime,
    );
  }
}
    

Firebase Cloud Messaging (Push)


flutter pub add firebase_core firebase_messaging
    

import "package:firebase_messaging/firebase_messaging.dart";

class FCMService {
  final _messaging = FirebaseMessaging.instance;
  
  Future init() async {
    // Permission
    await _messaging.requestPermission(
      alert: true,
      badge: true,
      sound: true,
    );
    
    // Get token
    final token = await _messaging.getToken();
    print("FCM token: $token");
    // ارسال به backend برای ذخیره
    
    // Foreground messages
    FirebaseMessaging.onMessage.listen((message) {
      print("Foreground: ${message.notification?.title}");
      // نمایش با local notification
    });
    
    // Background tap
    FirebaseMessaging.onMessageOpenedApp.listen((message) {
      print("Tapped: ${message.data}");
      // navigate to specific screen
    });
    
    // Background handler
    FirebaseMessaging.onBackgroundMessage(_backgroundHandler);
  }
  
  static Future _backgroundHandler(RemoteMessage message) async {
    print("Background: ${message.messageId}");
  }
}
    

۱۱.۵ Location و GPS


flutter pub add geolocator
    

import "package:geolocator/geolocator.dart";

class LocationService {
  Future getCurrentLocation() async {
    bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      return null;
    }
    
    LocationPermission permission = await Geolocator.checkPermission();
    if (permission == LocationPermission.denied) {
      permission = await Geolocator.requestPermission();
      if (permission == LocationPermission.denied) return null;
    }
    
    if (permission == LocationPermission.deniedForever) {
      return null;
    }
    
    return await Geolocator.getCurrentPosition(
      desiredAccuracy: LocationAccuracy.high,
    );
  }
  
  Stream watchLocation() {
    return Geolocator.getPositionStream(
      locationSettings: const LocationSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 10,  // متر
      ),
    );
  }
  
  // فاصله بین دو نقطه (متر)
  double distance(double lat1, double lng1, double lat2, double lng2) {
    return Geolocator.distanceBetween(lat1, lng1, lat2, lng2);
  }
}
    

۱۱.۶ Map


flutter pub add google_maps_flutter
# یا برای متن‌باز:
flutter pub add flutter_map latlong2
    

// با Google Maps
import "package:google_maps_flutter/google_maps_flutter.dart";

class MapPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GoogleMap(
      initialCameraPosition: const CameraPosition(
        target: LatLng(33.9850, 51.4100),  // کاشان
        zoom: 14,
      ),
      markers: {
        const Marker(
          markerId: MarkerId("1"),
          position: LatLng(33.9850, 51.4100),
          infoWindow: InfoWindow(title: "کاشان"),
        ),
      },
      onMapCreated: (controller) {
        // کنترل برنامه‌ریزی شده
      },
    );
  }
}

// با OpenStreetMap (رایگان)
import "package:flutter_map/flutter_map.dart";
import "package:latlong2/latlong.dart";

FlutterMap(
  options: const MapOptions(
    initialCenter: LatLng(33.9850, 51.4100),
    initialZoom: 14,
  ),
  children: [
    TileLayer(
      urlTemplate: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
    ),
    const MarkerLayer(
      markers: [
        Marker(
          point: LatLng(33.9850, 51.4100),
          child: Icon(Icons.location_on, color: Colors.red),
        ),
      ],
    ),
  ],
)
    

۱۱.۷ احراز هویت بیومتریک


flutter pub add local_auth
    

import "package:local_auth/local_auth.dart";

class BiometricService {
  final _auth = LocalAuthentication();
  
  Future isAvailable() async {
    return await _auth.canCheckBiometrics &&
           await _auth.isDeviceSupported();
  }
  
  Future authenticate() async {
    try {
      return await _auth.authenticate(
        localizedReason: "برای ورود به اپ خود را احراز هویت کنید",
        options: const AuthenticationOptions(
          biometricOnly: true,
          stickyAuth: true,
        ),
      );
    } catch (e) {
      return false;
    }
  }
}
    

۱۱.۸ Device Info و Connectivity


flutter pub add device_info_plus connectivity_plus
    

import "package:device_info_plus/device_info_plus.dart";

final deviceInfo = DeviceInfoPlugin();

if (Platform.isAndroid) {
  final android = await deviceInfo.androidInfo;
  print("Model: ${android.model}");
  print("Android: ${android.version.release}");
}

if (Platform.isIOS) {
  final ios = await deviceInfo.iosInfo;
  print("Model: ${ios.model}");
  print("iOS: ${ios.systemVersion}");
}

// === Connectivity ===
import "package:connectivity_plus/connectivity_plus.dart";

final connectivity = Connectivity();

final result = await connectivity.checkConnectivity();
if (result.contains(ConnectivityResult.none)) {
  print("بدون اینترنت");
}

// Listen
connectivity.onConnectivityChanged.listen((results) {
  if (results.contains(ConnectivityResult.wifi)) {
    print("WiFi متصل");
  } else if (results.contains(ConnectivityResult.mobile)) {
    print("Mobile data");
  } else {
    print("اتصال قطع شد");
  }
});
    

۱۱.۹ Platform Channel — کد Native

گاهی نیاز به نوشتن کد Java/Kotlin (Android) یا Swift (iOS) داریم.


// Dart side
import "package:flutter/services.dart";

class BatteryService {
  static const _channel = MethodChannel("samples.flutter.dev/battery");
  
  Future getBatteryLevel() async {
    try {
      final level = await _channel.invokeMethod("getBatteryLevel");
      return level ?? 0;
    } on PlatformException catch (e) {
      print("خطا: $e");
      return 0;
    }
  }
}
    

// Android side: MainActivity.kt
class MainActivity: FlutterActivity() {
    private val CHANNEL = "samples.flutter.dev/battery"
    
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                if (call.method == "getBatteryLevel") {
                    val batteryLevel = getBatteryLevel()
                    result.success(batteryLevel)
                } else {
                    result.notImplemented()
                }
            }
    }
    
    private fun getBatteryLevel(): Int {
        val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
        return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    }
}
    

۱۱.۱۰ بهترین تجربیات

  1. Permission‌های حداقلی. فقط آنچه واقعاً نیاز دارید.
  2. توضیح دلیل permission. در iOS اجباری.
  3. Graceful fallback. اگر permission رد شد.
  4. FCM token را به backend بفرستید.
  5. Notification channels در Android 13+.
  6. Connectivity check قبل از network call.
  7. Plugin های پایدار. با star زیاد در pub.dev.
  8. Privacy Policy. برای Play Store/App Store.

۱۱.۱۱ خلاصه فصل

  • permission_handler برای دسترسی‌ها
  • camera برای دوربین custom
  • flutter_local_notifications و FCM
  • geolocator برای GPS
  • google_maps یا flutter_map
  • local_auth برای biometric
  • device_info و connectivity
  • Platform Channel برای native code
در فصل بعد: Test — Unit، Widget، Integration.

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.