اذهب إلى المحتوى

Flutter Dev

الأعضاء
  • المساهمات

    787
  • تاريخ الانضمام

  • تاريخ آخر زيارة

  • عدد الأيام التي تصدر بها

    2

أجوبة بواسطة Flutter Dev

  1. السلام عليكم ورحمة الله وبركاته 

    تحيه طيبه للجميع 

    يواجهني خطاء في بداية تشغيلي للتطبيق كالتالي:

    E/flutter (26270): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: NoSuchMethodError: Class '_InternalLinkedHashMap<String, dynamic>' has no instance method 'cast' with matching arguments.
    E/flutter (26270): Receiver: _LinkedHashMap len:1
    E/flutter (26270): Tried calling: cast<Map<String, dynamic>>()
    E/flutter (26270): Found: cast<RK, RV>() => Map<RK, RV>
    E/flutter (26270): #0      Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
    E/flutter (26270): #1      JsonImageListWidget.FetchTopic (package:*******//main.dart:253:48)
    E/flutter (26270): <asynchronous suspension>
    E/flutter (26270): #2      JsonImageListWidget.initState.<anonymous closure> (package:*******/main.dart)
    E/flutter (26270): <asynchronous suspension>
    E/flutter (26270): 

    من الكود التالي:

      Future<List<Flowerdata>> FetchTopic() async {
        apiURL =
            'https://****************.php?Country=' +
               Cuntry.toString();
        var response = await http.post(Uri.parse(apiURL));
    
        var flowers = List<Flowerdata>();
    
        if (response.statusCode == 200) {
          final items = json.decode(response.body).cast<Map<String, dynamic>>();
          List<Flowerdata> listOfFruits = items.map<Flowerdata>((json) {
            return Flowerdata.fromJson(json);
          }).toList();
    
          return listOfFruits;
        } else {
        
          throw Exception('Failed to load data from Server.');
        }
      }

    في احد يعرف سبب حدوث هذا المشكله او صارت لديه من قبل نفسها؟

  2. بتاريخ 1 ساعة قال بلال زيادة:

    لماذا لا تقوم بحفظ قيمة اللغة و من قم تقوم باستخدام Provider بجلبها سأقوم بوضع كود توضيحي لذلك 

    
    return ChangeNotifierProvider<InitModel>(
          create: (context) => InitModel(),
          child: Consumer<InitModel>(
            builder: (context, value, child) {
              return MultiProvider(
                providers: [
                  ChangeNotifierProvider(create: (_) => AuthProvider()),
                  ChangeNotifierProvider(create: (_) => Cart()),
                  ChangeNotifierProvider(create: (context) => OrdersProvider(),
                  child: OrdersScreen(),),
                  /*ChangeNotifierProxyProvider<AuthProvider, Orders>(
                    create: null,
                    update: (_, auth, previousOrders) => Orders(),
                  ),*/
                ],
                child: StreamProvider<ConnectivityStatus>(
                  initialData: null,
                  create: (context) =>
                  ConnectivityService().connectionStatusController.stream,
                  child: MaterialApp(
    
                    theme: value.darkMode == false ? lightMode : darkMode,
                    debugShowCheckedModeBanner: false,
                    localizationsDelegates: [
                      RefreshLocalizations.delegate,
                      S.delegate,
                      GlobalWidgetsLocalizations.delegate,
                      GlobalMaterialLocalizations.delegate,
                      GlobalCupertinoLocalizations.delegate,
                      DefaultCupertinoLocalizations.delegate,
                    ],
                    supportedLocales: S.delegate.supportedLocales,
                    //locale: Locale('fa'),
                    locale: Locale(value.locale),
                    localeResolutionCallback:
                        (Locale locale, Iterable<Locale> supportedLocales) {
                      return locale;
                    },
                    initialRoute: '/splash',
                    routes: <String, WidgetBuilder>{
                      "/home": (context) => HomePage(),
                      "/init": (context) => InitApp(),
                      "/splash": (context) => Splash(),
                      "/login": (context) => LoginScreen(),
                      "/register": (context) => RegisterScreen(),
                      "/wishlist": (context) => WishlistScreen(),
                      "/checkouts": (context) => HomePage(),
                      "/onboardingscreen": (context) => OnboardingScreen(),
                      "/products": (context) => Products(),
                      "/languages": (context) => Languages(),
                      "/cart": (context) => CartScreen(),
                      "/orders": (context) => OrdersScreen(),
                      "/profile": (context) => Profile(),
                      '/userPage': (context) => UserPage(),
                      '/verifyAddress': (context) => VerifyAddress(),
                    },
                  ),
                ),
              );
            },
          ),
        );

     

    كل الشكر لك اخي الكريم ولكني خبرتي شبه قليله في هذا الامر قمت بتغير الحزمة وبعض الاكود وتم حل المشكله ولله الحمد

    بتاريخ منذ ساعة مضت قال Wael Aljamal:

    لنحاول هذه الخطوات:

    
    dependencies:
      easy_localization_loader:
        git:
          url: git://github.com/aissat/easy_localization_loader.git
          ref: overman-dev

    ثم الأمر:

    
    flutter pub upgrade

    وأضف الحقول الناقصة من التالي:

    
    EasyLocalization(
        supportedLocales: [Locale('en'), Locale('ar')],
        path: 'assets/translations',
        fallbackLocale: Locale('en'),
        saveLocale: false,
        useOnlyLangCode: true,
        child: Splashs(),
      )
    
    
    //supportedLocales: [Locale('en', 'US'), Locale('ar', 'SA')],
    //fallbackLocale: Locale('en', 'US'),
          

     

    كل الشكر لك اخي الكريم قمت باختصار هذا الاكود وستعمال هذا الطريقة مع شوية لفه ونجح الامر ولله الحمد كل الشكر لك 

    • أعجبني 2
  3. بتاريخ 1 دقيقة مضت قال Wael Aljamal:

    آخر أمرين لتنظيف الذاكرة المؤقتة للمكتبات لتجنب تضارب في عملهم.

    هذا الكود الذي احصل عليه بشكل عام على ما يبدو لا توجد مشكله فقط في اخطر سطر

    I/flutter (31611): [🌎 Easy Localization] [DEBUG] Start
    I/flutter (31611): [🌎 Easy Localization] [DEBUG] Init state
    I/flutter (31611): [🌎 Easy Localization] [DEBUG] Build
    I/flutter (31611): [🌎 Easy Localization] [DEBUG] Init Localization Delegate
    I/flutter (31611): [🌎 Easy Localization] [DEBUG] Init provider
    I/flutter (31611): [🌎 Easy Localization] [WARNING] Localization key [] not found
    بتاريخ 5 دقائق مضت قال بلال زيادة:

    لا. يمكن وضعه بدون استخدام context , ايضا يجب استخدام النسخة stable من فلاتر عن طريق الأمر التالي 

    
    flutter channel stable 
    flutter upgrade

     

    انا على channel stable ولكن بنسبه الى فلاتر ف استخدام اول اصدار من فلاتر 2 لتجنب المشاكل التي في الاخير

    • أعجبني 1
  4. بتاريخ 3 دقائق مضت قال بلال زيادة:

    هل يمكنك تجربة استخدام التالي 

    
    locale: Locale('هنا كود اللغة'),

     

    اهلا بك اخي لا يمكن وضعه بدون contextفي البداية 

    لدي سوال اخي الكريم

    الا توجد طريقة لوضع للغة الهاتف بشكل افتراضي ؟

    للكود هذا :

           localizationsDelegates: context.localizationDelegates,
            supportedLocales: context.supportedLocales,
            locale: context.locale,

     

    بتاريخ 1 دقيقة مضت قال Wael Aljamal:

    هل يظهر أي نص توضيحي أعلى هذه التعليمة؟ بعد وضع مؤشر الفأرة عليها أو إخطار بجانبها؟

    هل تستخدم الإصدار 3 من المكتبة؟

    مستبعد حدوث مشكلة أرجو تجريب التالي للتأكد من حذف أي كاش قديم قد يسبب تضارب و مشاكل:

    
    flutter channel stable
    flutter upgrade
    flutter pub cache repair
    flutter clean

     

    استخدم احدث اصدار نعم الاخير 

    لكن على ما يبدو ان اللغة يتم تخزينها ولكن لا يتم استراجعها للني اقوم بتخزينها في صفحة ومن بعدها انتقل الى الصفحه التاليه التي تحدث فيها المشكله

     

    هنا تكمن المشكله :

           localizationsDelegates: context.localizationDelegates,
            supportedLocales: context.supportedLocales,
            locale: context.locale,

     

  5. السلام عليكم ورحمة الله وبركاته 

    تحيه طيبه للجميع

    سابقا قبل الانتقال الى احدث اصدار من حزمة الترجمة  easy_localization كنت اقوم بتخزين القيمة التي ارغب بستخدامها او اللغة من خلال السطر التالي:

    context.locale = Locale('en', 'US');

    ولكن الان يظهر لدي ان هذا السطر قد تم تهميشه وقمت بمحاولة التخزين بشكل التالي حسب ما فهمت :

    context.setLocale(Locale('en', 'US'));

    ولكني احصل على المشكله التاليه :

    Null check operator used on a null value

    لا اعلم هل طريقة التخزين خطاء للغة ام ماذا هل يمكن الافاده لو تكرمتو 

     

    void main() async {
      WidgetsFlutterBinding.ensureInitialized();
      await EasyLocalization.ensureInitialized();
    
      runApp(Applang());
    }
    
    class Applang extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
    
            localizationsDelegates: context.localizationDelegates,
            supportedLocales: context.supportedLocales,
            locale: context.locale,
    
        home:EasyLocalization(
          saveLocale: true,
          supportedLocales: [Locale('en', 'US'), Locale('ar', 'SA')],
          path: 'assets/translations',
          fallbackLocale: Locale('en', 'US'),
          child: Splashs(),
        )
        );
      }
    }

     

    • أعجبني 1
  6. @Wael Aljamal

    كل الشكر لك اخي تم حل المشكلتين بفضل الله وفضلك

    وهذا الكود كامل للفائده:

    Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
      print("Handling a background message: ${message.messageId}");
    }
    class ShowNotifications {
    
      static FirebaseMessaging messaging = FirebaseMessaging.instance;
      static void showNotification(String title, String body) async {
        await _demoNotification(title, body);
      }
      static int i = 0;
      static Future<void> _demoNotification(String title, String body) async {
        Random random = new Random();
        int randomNumber = random.nextInt(10);
        var androidPlatformChannelSpecifics = AndroidNotificationDetails(
            'channel_ID', 'channel name', 'channel description',
            importance: Importance.max,
            playSound: true,
         
            showProgress: true,
            priority: Priority.high,
            ticker: 'test ticker');
    
        var iOSChannelSpecifics = IOSNotificationDetails();
        var platformChannelSpecifics = NotificationDetails(
            android: androidPlatformChannelSpecifics, iOS: iOSChannelSpecifics);
        await flutterLocalNotificationsPlugin
            .show(randomNumber, title, body, platformChannelSpecifics, payload: 'test');
      }
      static FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
      new FlutterLocalNotificationsPlugin();
    
      static void initialization(){
        var initializationSettingsAndroid =
        new AndroidInitializationSettings('@mipmap/ic_launcher');
        var initializationSettingsIOS = new IOSInitializationSettings();
        var initializationSettings = new InitializationSettings(
            android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
    
        flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
        flutterLocalNotificationsPlugin.initialize(initializationSettings,
            onSelectNotification: onSelectNotification);
      }
    
      static Future onSelectNotification(String payload) async {
        showDialog(
          //   context: context,
          builder: (_) {
            return new AlertDialog(
              title: Text("PayLoad"),
              content: Text("Payload : $payload"),
            );
          },
        );
      }
    
    
    
    
      static notification() async {
    
        await Firebase.initializeApp();messaging = FirebaseMessaging.instance;
    
        FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
    
        NotificationSettings settings = await messaging.requestPermission(
          alert: true,
          badge: true,
          provisional: false,
          sound: true,
        );
    
        if (settings.authorizationStatus == AuthorizationStatus.authorized) {
          print('User granted permission');
     
    
    
    
    
          FirebaseMessaging.onMessage.listen((RemoteMessage message) {
            print('Message title: ${message.notification?.title}, body: ${message.notification?.body}, data: ${message.data}');
    
            // Parse the message received
            PushNotification notification = PushNotification(
              title: message.notification?.title,
              body: message.notification?.body,
              dataTitle: message.data['title'],
              dataBody: message.data['body'],
    
            );
    
            //  showNotification(data['title'], data['notification']['body']);
            if(i%2==0) {
              showNotification( notification.title, notification.body);
            };
            i++;
    
    
    
          });
        } else {
          print('User declined or has not accepted permission');
        }
      }
    
    
    
    
    
    
    }

     

    • أعجبني 1
  7. بتاريخ 2 دقائق مضت قال Wael Aljamal:

    هل يصل إشعارين متطابقين في نفس اللحظة؟

    نعم اخي بعد استعمال الكود التالي: الكود هذا حل المشكله الاولى ولكن الان يصل اشعارين متطابقين في نفس اللحظة

     static Future<void> _demoNotification(String title, String body) async {
        Random random = new Random();
        int randomNumber = random.nextInt(100);
        var androidPlatformChannelSpecifics = AndroidNotificationDetails(
    
          //'channel_ID', 'channel name', 'channel description',
            randomNumber.toString(), 'channel name', 'channel description',
            importance: Importance.max,
            playSound: true,
            // sound: 'sound',
            // sound: true,
            //  setAsGroupSummary: true,
            showProgress: true,
            priority: Priority.high,
            ticker: 'test ticker');
    
        var iOSChannelSpecifics = IOSNotificationDetails();
        var platformChannelSpecifics = NotificationDetails(
            android: androidPlatformChannelSpecifics, iOS: iOSChannelSpecifics);
        await flutterLocalNotificationsPlugin
            .show(randomNumber, title, body, platformChannelSpecifics, payload: 'test');
      }

     

  8. بتاريخ 1 دقيقة مضت قال Wael Aljamal:

    لنفس الاشعار يكون له channel_id نفس القيمة نمررها في الحقلين نفس النمط أيضا.

    لا اقصد انني ارغب بالحصول على شعارين انا الان احصل على 2 ولنفس الاشعار يعني يحصل ازدواجيه 

  9. بتاريخ 6 دقائق مضت قال Wael Aljamal:

    ربما لأن parse لا يستطيع تحويل date of today المتغير لديك لقيمة رقمية.

    حاول استخدام المثال الذي أدرجته أنا في التعليق الأول

    هذه بشكل كامل كقيمة عددية.

    اعذرني ولكن قد اكون انا استخدمها بشكل خطاء 

    هل يتم استخدمها بهذا الشكل؟

    static Future<void> _demoNotification(String title, String body) async {
        var ggggg =DateTime.now().millisecondsSinceEpoch;
        var androidPlatformChannelSpecifics = AndroidNotificationDetails(
    
          //'channel_ID', 'channel name', 'channel description',
            ggggg.toString(), 'channel name', 'channel description',
            importance: Importance.max,
            playSound: true,
            // sound: 'sound',
            // sound: true,
            //  setAsGroupSummary: true,
            showProgress: true,
            priority: Priority.high,
            ticker: 'test ticker');
    
        var iOSChannelSpecifics = IOSNotificationDetails();
        var platformChannelSpecifics = NotificationDetails(
            android: androidPlatformChannelSpecifics, iOS: iOSChannelSpecifics);
        await flutterLocalNotificationsPlugin
            .show( ggggg, title, body, platformChannelSpecifics, payload: 'test');
      }

     

    بتاريخ 17 دقائق مضت قال Wael Aljamal:

    ربما لأن parse لا يستطيع تحويل date of today المتغير لديك لقيمة رقمية.

    حاول استخدام المثال الذي أدرجته أنا في التعليق الأول

    هذه بشكل كامل كقيمة عددية.

    لقد نجح الامر بشكل التالي ولكن حدثت مشكله ان كل اشعار ياتي الي مرتين يعني دبل او دبلكيت يحدث للاشعار 

      static Future<void> _demoNotification(String title, String body) async {
        Random random = new Random();
        int randomNumber = random.nextInt(100);
        var androidPlatformChannelSpecifics = AndroidNotificationDetails(
    
          //'channel_ID', 'channel name', 'channel description',
            randomNumber.toString(), 'channel name', 'channel description',
            importance: Importance.max,
            playSound: true,
            // sound: 'sound',
            // sound: true,
            //  setAsGroupSummary: true,
            showProgress: true,
            priority: Priority.high,
            ticker: 'test ticker');
    
        var iOSChannelSpecifics = IOSNotificationDetails();
        var platformChannelSpecifics = NotificationDetails(
            android: androidPlatformChannelSpecifics, iOS: iOSChannelSpecifics);
        await flutterLocalNotificationsPlugin
            .show(randomNumber, title, body, platformChannelSpecifics, payload: 'test');
      }

     

  10. بتاريخ 7 دقائق مضت قال Wael Aljamal:

    عفوا علينا تعديل هذه أيضا:

    
    await flutterLocalNotificationsPlugin
            .show(0, title, body, platformChannelSpecifics, payload: 'test');

    استبدل الرقم 0

     

     قمت بوضعه بشكل التالي ولكن اصبح يظهر رسالة خطاء 

        await flutterLocalNotificationsPlugin
            .show(int.parse(DataOfToday), title, body, platformChannelSpecifics, payload: 'test');
    [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: FormatException: Invalid radix-10 number (at character 1)
    E/flutter (31845): 28-5-2021
    E/flutter (31845): ^
    E/flutter (31845): 
    E/flutter (31845): #0      int._throwFormatException (dart:core-patch/integers_patch.dart:131:5)
    E/flutter (31845): #1      int._parseRadix (dart:core-patch/integers_patch.dart:142:16)
    E/flutter (31845): #2      int._parse (dart:core-patch/integers_patch.dart:100:12)
    E/flutter (31845): #3      int.parse (dart:core-patch/integers_patch.dart:63:12)

     

  11. بتاريخ 28 دقائق مضت قال Wael Aljamal:

    حاول إعطاء رقم فريد لكل إشعار، مثل المعرف الخاص به لا تجعله نص ثابت لأنه سوف يتم استبدال الإشعار الحالي بالإشعار الجديد الذي له نفس المعرًف.

    حاول وضع قيمة متغيرة مثل نص متغير أو قيمة الوقت الفعلي أو قيمة عشوائية وهذا سيعمل على الحفاظ على كل الإشعارات.

    
    AndroidNotificationDetails(
            'channel_ID', 'channel name', 'channel description',
            importance: Importance.max,
            playSound: true,
            // sound: 'sound',
            // sound: true,
            showProgress: true,
            priority: Priority.high,
            ticker: 'test ticker');

    تحديدا channel_ID.

    ضع مكانة رقم عشوائي مثلا:

    
    import 'dart:math';
    
    main() {
      var rng = new Random();
      rng.nextInt(100);
    }

    أو عداد في البرنامج أو قيمة الوقت.

    
    new DateTime.now().microsecondsSinceEpoch

    مكان channel_ID

    اهلا بيك اخي الكريم

    قمت بتجربه بشكل التالي ووضع الوقت وتاريخ وثواني حتى يتم الفرق ولكن مع الاسف المشكله ما زالت مستمره 

      static Future<void> _demoNotification(String title, String body) async {
        //String DataOfToday;
        var date = new DateTime.now().toString();
        var dateParse = DateTime.parse(date);
        var formattedDate = "${dateParse.day}-${dateParse.month}-${dateParse.year}";
    
         // DataOfToday = formattedDate.toString();
    
        var androidPlatformChannelSpecifics = AndroidNotificationDetails(
            //'channel_ID', 'channel name', 'channel description',
            formattedDate, 'channel name', 'channel description',
            importance: Importance.max,
            playSound: true,
            showProgress: true,
            priority: Priority.high,
            ticker: 'test ticker');
    
        var iOSChannelSpecifics = IOSNotificationDetails();
        var platformChannelSpecifics = NotificationDetails(
            android: androidPlatformChannelSpecifics, iOS: iOSChannelSpecifics);
        await flutterLocalNotificationsPlugin
            .show(0, title, body, platformChannelSpecifics, payload: 'test');
      }

     

    • أعجبني 1
  12. السلام عليكم ورحمة الله وبركاته

    تحيه طيبه للجميع

    لدي كلاس استقبال من خلاله الاشعارات من FCM  الكلاس يعمل جيدا ولكن توجد مشكله واحده وهيا ان كل اشعار ياتيني يحذف الذي سبقه بمعنى لم يتم عرض الا اشعار واحد وهو اخر اشعار في لوحة اشعارات الاندرويد او ما يسمى notification bar

    هل يوجد حل لحل هذا المشكله ؟

     

    الكلاس المستعمل كامل

    Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
      print("Handling a background message: ${message.messageId}");
    }
    class ShowNotifications {
    
      static FirebaseMessaging messaging = FirebaseMessaging.instance;
      static void showNotification(String title, String body) async {
        await _demoNotification(title, body);
      }
      static Future<void> _demoNotification(String title, String body) async {
        var androidPlatformChannelSpecifics = AndroidNotificationDetails(
            'channel_ID', 'channel name', 'channel description',
            importance: Importance.max,
            playSound: true,
            // sound: 'sound',
            // sound: true,
            showProgress: true,
            priority: Priority.high,
            ticker: 'test ticker');
    
        var iOSChannelSpecifics = IOSNotificationDetails();
        var platformChannelSpecifics = NotificationDetails(
            android: androidPlatformChannelSpecifics, iOS: iOSChannelSpecifics);
        await flutterLocalNotificationsPlugin
            .show(0, title, body, platformChannelSpecifics, payload: 'test');
      }
      static FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
      new FlutterLocalNotificationsPlugin();
    
      static void initialization(){
        var initializationSettingsAndroid =
        new AndroidInitializationSettings('@mipmap/ic_launcher');
        var initializationSettingsIOS = new IOSInitializationSettings();
        var initializationSettings = new InitializationSettings(
            android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
    
        flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
        flutterLocalNotificationsPlugin.initialize(initializationSettings,
            onSelectNotification: onSelectNotification);
      }
    
      static Future onSelectNotification(String payload) async {
        showDialog(
          //   context: context,
          builder: (_) {
            return new AlertDialog(
              title: Text("PayLoad"),
              content: Text("Payload : $payload"),
            );
          },
        );
      }
    
    
      static notification() async {
    
        await Firebase.initializeApp();messaging = FirebaseMessaging.instance;
    
        FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
    
        NotificationSettings settings = await messaging.requestPermission(
          alert: true,
          badge: true,
          provisional: false,
          sound: true,
        );
    
        if (settings.authorizationStatus == AuthorizationStatus.authorized) {
          print('User granted permission');
    
          FirebaseMessaging.onMessage.listen((RemoteMessage message) {
    
            // Parse the message received
            PushNotification notification = PushNotification(
              title: message.notification?.title,
              body: message.notification?.body,
              dataTitle: message.data['title'],
              dataBody: message.data['body'],
    
            );
    
    
            showNotification( notification.title, notification.body);
    
    
          });
        } else {
          print('User declined or has not accepted permission');
        }
      }
    
    
    }

     

  13. بتاريخ 2 ساعات قال Wael Aljamal:

    بالنسبة لتشغيل الإشعارات في وضع تشغيل التطبيق أي بحالة Foreground Notifications يوجد طريقة مختلفة لتشغيلها.

    لاحظ في الشيفرة لديك، قمت بعمل دالة للتعامل مع Background Notifications وهي:

    
        FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);

    أمالتشغيل الإشعارت بالواجهة تاكد من اتباع التوثيق: notifications/#foreground-notifications

    حيث يعمل على الدالة:

    
    setForegroundNotificationPresentationOptions

    مثال المكتبة: firebase_messaging/example من التوثيق.

    مثال منشور على gitHub يمكنك تجربته و التأكد من ذلك: firebase_messaging/example.

    في حال وجدت حلاً للمشكلة أرجو مشاركته ليستفاد الآخرون وشكرا لك

    تعديل، أرجو مشاركة نوع نظام التشغيل الذي تعمل عليه لاختلاف المشاكل بين أندرويد و IOS مثالا اختلاف طريقة الوصول للإشعارت في أندرويد:

    
    ["notification"]["title"]

    في IOS:

    
    ["aps"]["alert"]["title"].

    _____

    حل آخر كان بسبب عدم تحديد أيقونة التطبيق في:

    
    android drawable file contains the launcher_icon 

    وجود أيقونة  استعراض الإشعار لتعمل في الواجهة الأمامية

     

    اهلا بيك اخي الكريم

    شاكر لك مساعدتك 

    الكود اصبح كالتالي في النهاية وهو يعمل بشكل ممتاز / قمت بتجربه حتى الان على نظام الاندرويد بنسبه الى نظام IOS ساقوم بتجربته لاحقا باذن الله ولو كان فيه تعديل ساقوم بتحديثه هنا وستكمال الموضوع:

     

    Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
      print("Handling a background message: ${message.messageId}");
    }
    class ShowNotifications {
    
      static FirebaseMessaging messaging = FirebaseMessaging.instance;
      static void showNotification(String title, String body) async {
        await _demoNotification(title, body);
      }
      static Future<void> _demoNotification(String title, String body) async {
        var androidPlatformChannelSpecifics = AndroidNotificationDetails(
            'channel_ID', 'channel name', 'channel description',
            importance: Importance.max,
            playSound: true,
            // sound: 'sound',
            // sound: true,
            showProgress: true,
            priority: Priority.high,
            ticker: 'test ticker');
    
        var iOSChannelSpecifics = IOSNotificationDetails();
        var platformChannelSpecifics = NotificationDetails(
            android: androidPlatformChannelSpecifics, iOS: iOSChannelSpecifics);
        await flutterLocalNotificationsPlugin
            .show(0, title, body, platformChannelSpecifics, payload: 'test');
      }
      static FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
      new FlutterLocalNotificationsPlugin();
    
      static void initialization(){
        var initializationSettingsAndroid =
        new AndroidInitializationSettings('@mipmap/ic_launcher');
        var initializationSettingsIOS = new IOSInitializationSettings();
        var initializationSettings = new InitializationSettings(
            android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
    
        flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
        flutterLocalNotificationsPlugin.initialize(initializationSettings,
            onSelectNotification: onSelectNotification);
      }
    
      static Future onSelectNotification(String payload) async {
        showDialog(
          //   context: context,
          builder: (_) {
            return new AlertDialog(
              title: Text("PayLoad"),
              content: Text("Payload : $payload"),
            );
          },
        );
      }
      
    
      static notification() async {
    
        await Firebase.initializeApp();messaging = FirebaseMessaging.instance;
    
        FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
    
        NotificationSettings settings = await messaging.requestPermission(
          alert: true,
          badge: true,
          provisional: false,
          sound: true,
        );
    
        if (settings.authorizationStatus == AuthorizationStatus.authorized) {
          print('User granted permission');
        
          FirebaseMessaging.onMessage.listen((RemoteMessage message) {
            
            // Parse the message received
            PushNotification notification = PushNotification(
              title: message.notification?.title,
              body: message.notification?.body,
              dataTitle: message.data['title'],
              dataBody: message.data['body'],
    
            );
    
            
            showNotification( notification.title, notification.body);
    
    
          });
        } else {
          print('User declined or has not accepted permission');
        }
      }
    
    
    }

     

    • أعجبني 2
  14. بتاريخ 7 دقائق مضت قال Wael Aljamal:

    هل قمت بإلغاء تثبيت التطبيق وفتحه و عملت FCM بدون طلب إذن المستخدم؟

    لقد تم استبدال بعض الدوال من المكتبة و أصبحت deprecated يمكنك التأكد من

    الرابط: firebase.messaging.Messaging#requestpermission

    الصلاحيات: messaging/permissions

    قمت بذلك ولكن مثل ما ذكرت انت يبدو انه تم تجاهل بنسبه الى الاندرويد اما الايفون يحتاج اذن بعدني سوف اقوم بتجربه اكثر من خلال المنشور الذي قمت بمشاركته لدي

     

    شكرا لك اخي الكريم

  15. السلام عليكم ورحمة الله وبركاته 

    تحيه طيبه للجميع

    سابقا كنت استعمل اصدرا قديم من fcm  في فلاتر قبل الانتقال الى فلاتر 2 وكان المستخدم يحتاج الى منح الاشعارات الاذن حتى يستطيع استقبالها ولكن الان من تطبيق التوثيق في احدث اصدار للمكتبه هاتف الاندرويد يمنح صلاحيات الاذونات للاشعارات من تلقاء نفسه 

    هل يوجد خطاء في تطبيقي للكود ام الحزمة اصبحت بهذا الشكل بعد التحديث وهيا لا تحتاج الى اذن من المستخدم؟

    firebase_messaging: ^10.0.0

     

  16. بتاريخ 8 ساعات قال Nuhla Almasri:

    هل يمكنك أن ترسل لي شاشة الكونسل بعد أمر

    dart pub outdated --mode=null-safety

    اهلا بك اختي الكريمه 

    قمت بتغير ملف الشير _sharedPreferences بالكامل وتم حل المشكله حسب الحل الذي اشرتي له سابقا يجب ان يتم التحقق فعلا في البدايه

    كل الشكر لك

    • أعجبني 1
  17. السلام عليكم ورحمة الله وبركاته 

    تحيه طيبه للجميع 

    قمت بستعمال احدث اصدار من اشعارات فايربيز في فلاتر الكود شغال اذا كنت خارج التطبيق لا توجد مشكله ولكن لو كنت داخل التطبيق لا يتم عرض الاشعارات للمستخدم

    لا اعلم ما هو الجزء المتبقي في الامر 

    الكود كامل:

    
    
    Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
      print("Handling a background message: ${message.messageId}");
    }
    
    void main() async {
      runApp(MyApp());
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return
          MaterialApp(
            title: 'Notify',
            theme: ThemeData(
              primarySwatch: Colors.deepPurple,
            ),
            debugShowCheckedModeBanner: false,
            home: HomePage(),
    
        );
      }
    }
    
    class HomePage extends StatefulWidget {
      @override
      _HomePageState createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
       FirebaseMessaging _messaging;
       int _totalNotifications;
      PushNotification _notificationInfo;
    
      void registerNotification() async {
        await Firebase.initializeApp();_messaging = FirebaseMessaging.instance;
    
        FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
    
        NotificationSettings settings = await _messaging.requestPermission(
          alert: true,
          badge: true,
          provisional: false,
          sound: true,
        );
    
        if (settings.authorizationStatus == AuthorizationStatus.authorized) {
          print('User granted permission');
    
          FirebaseMessaging.onMessage.listen((RemoteMessage message) {
            print(
                'Message title: ${message.notification?.title}, body: ${message.notification?.body}, data: ${message.data}');
    
            // Parse the message received
            PushNotification notification = PushNotification(
              title: message.notification?.title,
              body: message.notification?.body,
              dataTitle: message.data['title'],
              dataBody: message.data['body'],
            );
    
            setState(() {
              _notificationInfo = notification;
              _totalNotifications++;
            });
    
            if (_notificationInfo != null) {
    
              showNotification;
    
            }
          });
        } else {
          print('User declined or has not accepted permission');
        }
      }
       static void showNotification(String title, String body) async {
         await _demoNotification(title, body);
       }
       static Future<void> _demoNotification(String title, String body) async {
         var androidPlatformChannelSpecifics = AndroidNotificationDetails(
             'channel_ID', 'channel name', 'channel description',
             importance: Importance.max,
             playSound: true,
             // sound: 'sound',
             // sound: true,
             showProgress: true,
             priority: Priority.high,
             ticker: 'test ticker');
    
         var iOSChannelSpecifics = IOSNotificationDetails();
         var platformChannelSpecifics = NotificationDetails(
             android: androidPlatformChannelSpecifics, iOS: iOSChannelSpecifics);
         await flutterLocalNotificationsPlugin
             .show(0, title, body, platformChannelSpecifics, payload: 'test');
       }
       static FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
       new FlutterLocalNotificationsPlugin();
    
       static void initialization(){
         var initializationSettingsAndroid =
         new AndroidInitializationSettings('@mipmap/ic_launcher');
         var initializationSettingsIOS = new IOSInitializationSettings();
         var initializationSettings = new InitializationSettings(
             android: initializationSettingsAndroid, iOS: initializationSettingsIOS);
    
         flutterLocalNotificationsPlugin = new FlutterLocalNotificationsPlugin();
         flutterLocalNotificationsPlugin.initialize(initializationSettings,
             onSelectNotification: onSelectNotification);
       }
    
       static Future onSelectNotification(String payload) async {
         showDialog(
           //   context: context,
           builder: (_) {
             return new AlertDialog(
               title: Text("PayLoad"),
               content: Text("Payload : $payload"),
             );
           },
         );
       }
       // For handling notification when the app is in terminated state
      checkForInitialMessage() async {
        await Firebase.initializeApp();
        RemoteMessage initialMessage =
            await FirebaseMessaging.instance.getInitialMessage();
    
        if (initialMessage != null) {
          PushNotification notification = PushNotification(
            title: initialMessage.notification?.title,
            body: initialMessage.notification?.body,
            dataTitle: initialMessage.data['title'],
            dataBody: initialMessage.data['body'],
          );
    
          setState(() {
            _notificationInfo = notification;
            _totalNotifications++;
          });
        }
      }
    
    
    
      @override
      void initState() {
        _totalNotifications = 0;
        registerNotification();
        checkForInitialMessage();
    
        FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
          PushNotification notification = PushNotification(
            title: message.notification?.title,
            body: message.notification?.body,
    
            dataTitle: message.data['title'],
            dataBody: message.data['body'],
          );
    
          setState(() {
            _notificationInfo = notification;
            _totalNotifications++;
          });
        });
    
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Notify'),
            brightness: Brightness.dark,
          ),
          body: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
    
            ],
          ),
        );
      }
    }

     

  18. بتاريخ 5 ساعات قال Nuhla Almasri:

    تمام الموضوع ما زال متعلق بال NULL ارجو القيام بفحص قيمة lang قبل ارسالها الى الدالة sharedPreferences.setInt فتمرير قيمة NULL الى اي دالة قد يؤدي الى نفس المشكلة في حال لم تقم بتعريف قيمة مبدأية لها init value هنا:

    
      Future<bool> commit() async {
    
    
        _sharedPreferences ??= await SharedPreferences.getInstance();
    // عليك فحص اي قيمة يتم ارسالها الى أي دالة بالنسخة الجديدة حتى يتم اعتبار ان البرنامج لن  يحتوي اي قيمة
        // null
        await _sharedPreferences.setInt(LANG, lang);
     
    
      }

     

    اهلا بك اختي

    افهم من كلامك انا حاليا مجبور حتى لو كنت املك خمسة اسطر مثلا لحفظ القيم اني مجبور اتحقق واحد واحد منهم بطريقة التاليه :

    if(LANG  != null){
          await _sharedPreferences.setInt(LANG, lang);
          return true;
        } else if .....
    
    } else if .....
    
    
    } else if .....

    الا يمكن اختصار هذا الطريقة ؟ قمت بتجربة كالتالي ولكن لم تنجح  :

    if(_sharedPreferences  != null){
          await _sharedPreferences.setInt(LANG, lang);
          return true;
        }

     

    • أعجبني 1
  19. بتاريخ 12 ساعات قال Nuhla Almasri:

    نعم الشيرد بريفيرنسز ان لم يكن هناك اي شيئ مخزن بالذاكرة سيكون قيمتها null و انت تستخدم flutter 2  اي نسخة null safty  مما يعني انك يجب ان تضمن عدم وجود اي قيمة داخل برنامجك يحتوي قيمة null لتفادي هذه الأخطاء يوجد أداه لتقوم بإصلاح أصدار برنامجك قبل النسحة الجديدة , و ذلك بعمل تحديث لجميع المكتبات لضمان انها ستحتوي على هذه الخاصية 

    يمكنك ان تقوم بعمل  

    
    dart pub outdated --mode=null-safety
    
    dart pub upgrade --null-safety
    
    flutter pub get
    
    flutter analyze
    
    dart migrate

    و عندما تقوم بعمل ال migration  ينبغي ان يقوم بتحديث جميع المكتبات يحيث أن القيمة الراجعه لاي شئ لن يحتوي على  null ممكن Zero او مصفوفة فارغة 

    اهلا بك اختي الكريمة

    قمت بتطبيق ما تم ذكره من قبلك ولكن مع الاسف المشكله مستمره لا اعلم حقيقه لماذا تحدث ولكن انا استطيع تخزين قيم من نوع سترينج بدون حدوث مثل هذا المشكله 

     

    ايضا لما قمت تطبيق ل migration  يقوم بعمل تحليل للبيانات من خلاله تظهر مجموع الاخطاء المتواجده في التطبيق حتى التي لا تشكل مشكلة في عمل التطبيق

    • أعجبني 1
  20. السلام عليكم ورحمة الله وبركاته

    تحيه طيبه للجميع

    كنت استعمل سابقا قبل الانتقال الى flutter 2  اصدار قديم من SharedPreferences الان لدي احدث اصدار منه

    كنت استطيع تخزين قيم من نوع int  في كلاس منفصل استعمله من جميع جوانب التطبيق ولكن بعد الانتقال وتحديث كل ما حاولت القيام بحفظ القيم تظهر لدي المشكله التاليه:

    [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: Invalid argument(s) (value): Must not be null
    E/flutter (24208): #0      ArgumentError.checkNotNull (dart:core/errors.dart:185:27)
    E/flutter (24208): #1      SharedPreferences._setValue (package:shared_preferences/shared_preferences.dart:147:19)
    E/flutter (24208): #2      SharedPreferences.setString (package:shared_preferences/shared_preferences.dart:133:7)
    E/flutter (24208): #3      MyPreferences.commit (package:Hasa/MyPreferences.dart:114:30)
    E/flutter (24208): #4      _SettingsPageState._handleRadioValueChange.<anonymous closure> (package:Hasa/Settings.dart:149:26)
    E/flutter (24208): #5      State.setState (package:flutter/src/widgets/framework.dart:1267:30)
    E/flutter (24208): #6      _SettingsPageState._handleRadioValueChange (package:Hasa/Settings.dart:132:5)
    E/flutter (24208): #7      _RadioState._handleChanged (package:flutter/src/material/radio.dart:404:24)
    E/flutter (24208): #8      RenderToggleable._handleTap (package:flutter/src/material/toggleable.dart:440:19)
    E/flutter (24208): #9      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
    E/flutter (24208): #10     TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:607:11)
    E/flutter (24208): #11     BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:296:5)
    E/flutter (24208): #12     BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:267:7)
    E/flutter (24208): #13     GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:157:27)
    E/flutter (24208): #14     GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:385:20)
    E/flutter (24208): #15     GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:361:22)
    E/flutter (24208): #16     RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:278:11)
    E/flutter (24208): #17     GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:316:7)
    E/flutter (24208): #18     GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:280:5)
    E/flutter (24208): #19     GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:238:7)
    E/flutter (24208): #20     GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:221:7)
    E/flutter (24208): #21     _rootRunUnary (dart:async/zone.dart:1370:13)
    E/flutter (24208): #22     _CustomZone.runUnary (dart:async/zone.dart:1265:19)
    E/flutter (24208): #23     _CustomZone.runUnaryGuarded (dart:async/zone.dart:1170:7)
    E/flutter (24208): #24     _invoke1 (dart:ui/hooks.dart:180:10)
    E/flutter (24208): #25     PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:276:7)
    E/flutter (24208): #26     _dispatchPointerDataPacket (dart:ui/hooks.dart:96:31)
    E/flutter (24208): 

     

     

    طريقة الحفظ كانت كالتالي:

      void _handleRadioValueChange(int value) {
        _radioValue = value;
        setState(() {
          switch (_radioValue) {
            case 1:
              SaveLang=1;
              context.locale = Locale('en', 'US');
       
              _myPreferences.lang = SaveLang;
              _myPreferences.commit();
        
              break;
            case 2:
              SaveLang=2;
              context.locale = Locale('ar', 'SA');
              _myPreferences.lang = SaveLang;
              _myPreferences.commit();
              break;
    
          }
        });
    
      }

    اما كلاس الحفظ فهو كالتالي:

    
    
    
    class MyPreferences {
    
      static const LANG = "lang";
    
    
      static final MyPreferences instance = MyPreferences._internal();
    
      static SharedPreferences _sharedPreferences;
    
    
      int lang;
    
    
    
    
    
      MyPreferences._internal() {}
    
      factory MyPreferences() => instance;
    
      Future<SharedPreferences> get preferences async {
    
        if (_sharedPreferences != null) {
          return _sharedPreferences;
        } else {
    
          _sharedPreferences = await SharedPreferences.getInstance();
    
          lang = _sharedPreferences.getInt(LANG);
     
    
    
    
          return _sharedPreferences;
        }
      }
    
      Future<bool> commit() async {
    
    
        _sharedPreferences ??= await SharedPreferences.getInstance();
    
        await _sharedPreferences.setInt(LANG, lang);
     
    
      }
      Future<MyPreferences> init() async {
        _sharedPreferences = await preferences;
        return this;
      }
    
    
    
    }

     

    احد ممكن يفيدنا عن سبب المشكله او سبق وحصلت لديه وكيف طريقة الحل لها

    • أعجبني 1
  21. بتاريخ 7 ساعات قال بلال زيادة:

    ايضا يمكنك تجربة وضع [] في السطر التالي إذا كان الاستعلام الأول لجلب جميع الحقول وليس حقل محدد حسب id الخاص به

    
    $set[] = $link;

     

    هلا بيك اخي

    قمت بتجربه هو اعطاني مصفوفه جديده بالاسم الذي ننشاه ولكن المشكله انه ايضا جلب ال 4 صور المرتبطه بالموضوع 

    لدي فكره مختلفه ولكن لا اعلم مدى نجاحها ولكن قد يكون من الاسهل اضافة حقل فريد اضافي في جدول الصور 

    بمعنى لو قام المستخدم باضافة اربع صور يجب عليه اختيار صوره اساسيه وسيكون هذا الحقل يحتوي على الرقم 1 مثلا بمعنى احنا رح ناخذ التي يتوفر هذا الشرط بها ولكن الامر معقد شويه بنسبه الى فلاتر 

     

    شاكر لك اخي الكريم

    • أعجبني 1
  22. بتاريخ 12 دقائق مضت قال بلال زيادة:

    يمكنك تنفيذ الاستعلام الأول ومن ثم بداخل حلقة التكرار تنفيذ الاستعلام الثاني بناء على id من الاستعلام الأول لكل موضوع سأقوم بوضع مثال ربما يساعدك. 

    
    $query = "SELECT p.id, p.name FROM product p WHERE p.product_id ='".$_GET['product_id']."'";
    $resouter = mysqli_query($connect, $query);
    
    $set = array();
    $total_records = mysqli_num_rows($resouter);
    if($total_records >= 1) {
    	while ($link = mysqli_fetch_array($resouter, MYSQLI_ASSOC)){
    		$set = $link;
    	}
      $set1 = [];
      $q="select img,id from album where id = " . $set['id'];
      $results = mysqli_query($connect, $q);
      while ($row = mysqli_fetch_array($results, MYSQLI_ASSOC)) {
          $set1[] = $row['img_url'];
      }
      $set['album'] = $set1;
    
    }
    
    
    header('Content-Type: application/json; charset=utf-8');
    echo $val = str_replace('\\/', '/', json_encode($set));

     

    اهلا بك اخي الكريم ساقوم بتجربة الطريقة وافادتك

    • أعجبني 1
  23. السلام عليكم ورحمة الله وبركاته 

    تحيه طيبه للجميع 

    قمت بتطوير تطبيق من خلال flutter  التطبيق يسمح للمستخدمين القيام بنشر مواضيع عربيه مختلفه وكل موضوع يحتوي على مجموعة صور 4 صور بتحديد

    في المقابل عملت جدولين في قاعدة البيانات 

    الاول لحفظ المواضيع كالتالي:

    topic table : 
    
    --------------------------------------------------------------
    id | subject_id | user_id | title
    ---------------------------------------------------------------
    1  | 1          | 1       | مرحبا لدى عدة اسئله على هذا الموضوع
    ---------------------------------------------------------------
    2  | 1          | 2       | مرحبا اخى الكريم هل لديك وقت كافى  

    وثاني لحفظ صور المواضيع (تم ربطه لدى الجدول الاولى من خلال id  المواضيع )

    Image table : 
    
    --------------------------------------------------------------
    id |  subject_id | imageUrl
    ---------------------------------------------------------------
    1  |    1        | ************************
    ---------------------------------------------------------------
    2  |    2        | ************************ 

     

    المشكله الان كل موضع مثلا يمتلك 4 صور مختلفه في جدول الصور وموضع واحد في جدول المواضيع / الان انا احتاج الى جلب الموضع مع صوره واحده فقط حتى لا يحدث تكرار للمواضيع واحصل على نفس الموضوع 4 مرات اعتمادنا على عدد الصور 

     

    قمت بعمل الاستعلام التالي ولكن ما حدث انه قام بجلب المواضيع بشكل متكرر بحسب عدد الصور يعني كل موضوع تكرر لدي اربع مرات وهذا الامر ليس صحيحا انا احتاج الى ان يتم جلب الموضوع لمره واحده فقط وليكون مرتبط ب اعلى id صور متواجد :

    <?php
    
    include 'con.php';
    
    
    $sql = "SELECT * FROM topic INNER JOIN Image ON topic.subject_id=Image.subject_id WHERE topic.sta = 1 AND topics.Country = 'EG'" ;
    
    
    
    $result = $con->query($sql);
    
    
    $stmt = $con->prepare($sql); 
    
    
    
    $stmt->execute();
    
    $result = $stmt->get_result();
    
    
    
     
    if ($result->num_rows >0) {
     
     
    	 while($row[] = $result->fetch_assoc()) {
    	 
    	 $item = $row;
    	 
    	 $json = json_encode($item, JSON_NUMERIC_CHECK);
    	 
    	 }
     
    } else {
     // http_response_code(404);
        $json = json_encode(["result" => "No Data Foun"]);
    }
     echo $json;
    $con->close();
    
    ?>

    كيف يمكن حل هذا المشكله حقيقه لم يسبق لي تطبيق هذا الامر لذلك الامر ليس واضح لدي

    • أعجبني 1
×
×
  • أضف...