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

Flutter Dev

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

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

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

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

    2

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

  1. بتاريخ 17 ساعات قال Wael Aljamal:

    لا مانع من عمل استعلام مع قاعدة البيانات للتحقق من وجود تحديثات أو عدم وجودهم.

    كيف نعلم أن البيانات لدينا محدًثة؟ 

    • إما عن طريق timestamp أي وقت يحوي توقيت آخر تعديل حدث على المستخدمين في قاعدة البيانات و نمرره مع البيانات و نخزنه في التطبيق يعمل backend على التحقق منه 
    • أو طريقة أخرى مثلاً من خلال ID آخر مستخدم مضاف..

    سوف يرسل التطبيق توقبت آخر تحديث للملف

    
    final _baseUrl = 'https://website.com/data.php?last_update=time...';
                                                   ^^^^^^^^^^^^^^^^^^^

    تقوم بتخزين last_update في shared_preferencs 

    يرسل الخادم البيانات على الشكل التالي مثلاً

    • يوجد تحديث: نستخلص new_data ونخزنها في الملف
    
    {
    	status: need_update,  => تحديث الملف في التطبيق
    	time_stamp: time      =>   آخر تحديث في قاعدة البيانات
    	new_data = [users]
    }
    • لايوجد تحديث: نستعمل الملف المحلي
    
    {
    	status: uptodate
    }

     

    توقيت آخر تعديل لجدول قواعد بيانات MySQL

    
    SELECT UPDATE_TIME
    FROM   information_schema.tables
    WHERE  TABLE_SCHEMA = 'dbname'
       AND TABLE_NAME = 'tabname'

     

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

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

    لا مانع من عمل استعلام مع قاعدة البيانات للتحقق من وجود تحديثات أو عدم وجودهم.

    كيف نعلم أن البيانات لدينا محدًثة؟ 

    • إما عن طريق timestamp أي وقت يحوي توقيت آخر تعديل حدث على المستخدمين في قاعدة البيانات و نمرره مع البيانات و نخزنه في التطبيق يعمل backend على التحقق منه 
    • أو طريقة أخرى مثلاً من خلال ID آخر مستخدم مضاف..

    سوف يرسل التطبيق توقبت آخر تحديث للملف

    
    final _baseUrl = 'https://website.com/data.php?last_update=time...';
                                                   ^^^^^^^^^^^^^^^^^^^

    تقوم بتخزين last_update في shared_preferencs 

    يرسل الخادم البيانات على الشكل التالي مثلاً

    • يوجد تحديث: نستخلص new_data ونخزنها في الملف
    
    {
    	statue: need_update,  => تحديث الملف في التطبيق
        time_stamp: time      =>   آخر تحديث في قاعدة البيانات
        new_data = [users]
    }
    • لايوجد تحديث: نستعمل الملف المحلي
    
    {
    	statue: uptodate
    }

     

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

    اعتذر لك ولكن حقيقه لم افهم لا الطريقة الأولى ولا الثانية ياليت تشرح الفكرة اكثر لو امكن

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

    قمت بعمل صفحة تجلب البيانات للمستخدمين من خلال api  ثم قمت بتخزينها في  path_provider من بعد اول مره تنعرض فيها حتى يتم تحميلها في المرة التأليه بشكل اسرع للمستخدم. تم تخزين البيانات وجلبها من  path_provider بشكل ممتاز ولكن احتاج الى معرفة كيف يمكنني تحديث الملف التي تم تخزين البيانات فيه ك مثال انا اجلب قائمة أسماء القائمة هذا تم تخزينها مسبقاً في  path_provider ولكن بعد فترة تم إضافة أسماء جديده او تم حذف أسماء من القائمة بمعنى تم تحديث هذا القائمة في قاعدة البيانات الان كيف يمكن ابلاغ path_provider ان القائمة التي لدينا أصبحت قديمة واحنا محتاجين الى ان تعمل لتحفظ القائمة الجديدة ؟

    كود الصفحة كامل لدي كالتالي:

    void main() {
      runApp( proviedsave());
    }
    
    class proviedsave extends StatefulWidget {
      const proviedsave({Key? key}) : super(key: key);
    
      @override
      _proviedsaveState createState() => _proviedsaveState();
    }
    
    class _proviedsaveState extends State<proviedsave> {
    
      final _baseUrl = 'https://jsonplaceholder.typicode.com/posts';
    
      @override
      void initState() {
        _firstLoad();
    
      }
    
      bool _isFirstLoadRunning=false;
    
      _firstLoad() async {
        setState(() {
          _isFirstLoadRunning = true;
        });
    
        String fileName = "userdata.json";
        var dir = await getTemporaryDirectory();
        File file = new File(dir.path + "/" + fileName);
        if (file.existsSync()) {
          print("Loading from cache");
          //var jsonData = file.readAsStringSync();
          var jsonData = await jsonDecode(await file.readAsString());
          // var responsebody = json.encode(jsonData);
    
          return jsonData;
    
        } else {
          print("Loading from API");
          var response = await http.get(Uri.parse('$_baseUrl'));
    
          if (response.statusCode == 200) {
    
            var responsebody = jsonDecode(response.body);
    
            //save json in local file
            // file.writeAsStringSync(responsebody.toString(), flush: true, mode: FileMode.write);
            file.writeAsStringSync(jsonDecode(responsebody), flush: true, mode: FileMode.write);
            return responsebody;
    
          }
        }
    
    
        setState(() {
          _isFirstLoadRunning = false;
        });
      }
    
    
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(home: Scaffold(body: ListView(shrinkWrap: true,scrollDirection: Axis.vertical,children: [
          Text('sssssssss'),
    
          FutureBuilder(
              future: _firstLoad(),
              builder: (context, AsyncSnapshot snapshot) {
                if (snapshot.hasData) {
                  return ListView.builder(
                      physics: ScrollPhysics(),
                      scrollDirection: Axis.vertical,
                      shrinkWrap: true,
                      itemCount: snapshot.data.length.clamp(0, 6),
                      itemBuilder: (context, index) {
    
                        print('2=====================${json.encode(snapshot.data)}');
                        print('1=====================${snapshot.data}');
    
    
                        return
                          Card(
                            // elevation: 1,
                            child: Container(
                              padding: EdgeInsets.all(6),
                              child: ListTile(
                                title: Text(snapshot.data[index]['title'].toString()),
                                // subtitle: Text(snapshot.data[index]['body'].toString()),
                              ),
                            ),
                          );
                      }
                  );
    
                } else if (snapshot.hasError) {
    
                  return Center(
                      child: Image.asset(
                        'assets/no_dataa.png',
                        fit: BoxFit.contain,
                        width: 180,
                        height: 180,
                      ));
    
                }
                // By default, show a loading spinner.
                return _isFirstLoadRunning? SizedBox(
                  height: MediaQuery.of(context).size.height / 1.3,
                  child: Center(
                    child: CircularProgressIndicator(
                      valueColor:
                      new AlwaysStoppedAnimation<Color>(Colors.black),
                    ),
                  ),
                ):Center(
                    child: SizedBox(
                      height: MediaQuery.of(context).size.height / 1.3,
                      child: Image.asset(
                        'assets/EmptyFavorite.png',
                        fit: BoxFit.contain,
                        width: 370,
                        height: 370,
                      ),
                    ));
              }
    
          )
        ]),),);
      }
    }

     

    شكرا لكم

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

    طيب، ممكن عرض التالي:

    
    print(snapshot.data[0])
    print(snapshot.data[0]['title'])

    لنتأكد أي index يسبب المشكلة

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

    jsonDecode

    حقيقه انني لست خبير ب موضوع نوع البيانات هذا ولكن الي شفته من خلال عمل الطباعة ان النوع الذي قدمته سابقا يحتوي على كوتيشن او فواصل في بداية المصفوفة وعلى ما يبدو هذا سبب المشكلة قمت بتغير النوع في وقت الحفظ والاستدعاء ايضا لما قمت بحفظ البيانات من نوع json.encode البيانات وقت الاستدعاء تحتوي على // من هذا النوع بين كل كلمة وأخرى بشكل كبير 

    وهذا الصفحة كاملة اشاركها للفائدة 

    
    void main() {
      runApp( proviedsave());
    }
    
    class proviedsave extends StatefulWidget {
      const proviedsave({Key? key}) : super(key: key);
    
      @override
      _proviedsaveState createState() => _proviedsaveState();
    }
    
    class _proviedsaveState extends State<proviedsave> {
    
      final _baseUrl = 'https://jsonplaceholder.typicode.com/posts';
    
      @override
      void initState() {
        _firstLoad();
    
      }
    
      bool _isFirstLoadRunning=false;
    
      _firstLoad() async {
        setState(() {
          _isFirstLoadRunning = true;
        });
    
        String fileName = "userdata.json";
        var dir = await getTemporaryDirectory();
        File file = new File(dir.path + "/" + fileName);
        if (file.existsSync()) {
          print("Loading from cache");
          //var jsonData = file.readAsStringSync();
          var jsonData = await jsonDecode(await file.readAsString());
          // var responsebody = json.encode(jsonData);
    
          return jsonData;
    
        } else {
          print("Loading from API");
          var response = await http.get(Uri.parse('$_baseUrl'));
    
          if (response.statusCode == 200) {
    
            var responsebody = jsonDecode(response.body);
    
            //save json in local file
            // file.writeAsStringSync(responsebody.toString(), flush: true, mode: FileMode.write);
            file.writeAsStringSync(jsonDecode(responsebody), flush: true, mode: FileMode.write);
            return responsebody;
    
          }
        }
    
    
        setState(() {
          _isFirstLoadRunning = false;
        });
      }
    
    
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(home: Scaffold(body: ListView(shrinkWrap: true,scrollDirection: Axis.vertical,children: [
          Text('sssssssss'),
    
          FutureBuilder(
              future: _firstLoad(),
              builder: (context, AsyncSnapshot snapshot) {
                if (snapshot.hasData) {
                  return ListView.builder(
                      physics: ScrollPhysics(),
                      scrollDirection: Axis.vertical,
                      shrinkWrap: true,
                      itemCount: snapshot.data.length.clamp(0, 6),
                      itemBuilder: (context, index) {
    
                        print('2=====================${json.encode(snapshot.data)}');
                        print('1=====================${snapshot.data}');
    
    
                        return
                          Card(
                            // elevation: 1,
                            child: Container(
                              padding: EdgeInsets.all(6),
                              child: ListTile(
                                title: Text(snapshot.data[index]['title'].toString()),
                                // subtitle: Text(snapshot.data[index]['body'].toString()),
                              ),
                            ),
                          );
                      }
                  );
    
                } else if (snapshot.hasError) {
    
                  return Center(
                      child: Image.asset(
                        'assets/no_dataa.png',
                        fit: BoxFit.contain,
                        width: 180,
                        height: 180,
                      ));
    
                }
                // By default, show a loading spinner.
                return _isFirstLoadRunning? SizedBox(
                  height: MediaQuery.of(context).size.height / 1.3,
                  child: Center(
                    child: CircularProgressIndicator(
                      valueColor:
                      new AlwaysStoppedAnimation<Color>(Colors.black),
                    ),
                  ),
                ):Center(
                    child: SizedBox(
                      height: MediaQuery.of(context).size.height / 1.3,
                      child: Image.asset(
                        'assets/EmptyFavorite.png',
                        fit: BoxFit.contain,
                        width: 370,
                        height: 370,
                      ),
                    ));
              }
    
          )
        ]),),);
      }
    }

    الف شكر لك اخي 

    فقط احتاج الى معلومة اخيره اخي لو تكرمت

    في حالة حدث إضافة او حذف للبيانات في api  او نقول قاعدة البيانات كيف يتم تحديث هذا الملف بالبيانات الجديدة؟ 

    @Wael Aljamal

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

    ربما تخزين كامل البيانات تم على شكل مصفوفة، 

    جرب التالي:

    
    snapshot.data[0][index]['title'].toString()
                ^^^^^

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

    
    print(json.encode(snapshot.data));
    
    print(snapshot.data) 
    
    print(variable.toString())

     

    قمت بأول خيار ولم تحل المشكلة 

    بالنسبة الى الطباعة هذا ما احصل عليه:

      itemBuilder: (context, index) {
    
                        print('2=====================${json.encode(snapshot.data)}');
                        print('1=====================${snapshot.data}');
    I/flutter ( 2677): Loading from cache
    I/flutter ( 2677): Loading from cache
    I/flutter ( 2677): 2====================="\"[{userId: 1, id: 1, title: sunt aut facere repellat provident occaecati excepturi optio reprehenderit, body: quia et suscipit\\nsuscipit recusandae consequuntur expedita et cum\\nreprehenderit molestiae ut ut quas totam\\nnostrum rerum est autem sunt rem eveniet architecto}, {userId: 1, id: 2, title: qui est esse, body: est rerum tempore vitae\\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\\nqui aperiam non debitis possimus qui neque nisi nulla}, {userId: 1, id: 3, title: ea molestias quasi exercitationem repellat qui ipsa sit aut, body: et iusto sed quo iure\\nvoluptatem occaecati omnis eligendi aut ad\\nvoluptatem doloribus vel accusantium quis pariatur\\nmolestiae porro eius odio et labore et velit aut}, {userId: 1, id: 4, title: eum et est occaecati, body: ullam et saepe reiciendis voluptatem adipisci\\nsit amet autem assumenda provident rerum culpa\\nquis hic commodi nesciunt rem tenetur dolore
    I/flutter ( 2677): 1====================="[{userId: 1, id: 1, title: sunt aut facere repellat provident occaecati excepturi optio reprehenderit, body: quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto}, {userId: 1, id: 2, title: qui est esse, body: est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla}, {userId: 1, id: 3, title: ea molestias quasi exercitationem repellat qui ipsa sit aut, body: et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut}, {userId: 1, id: 4, title: eum et est occaecati, body: ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iu

     

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

    المشكلة لديك في كتابة البيانات لأول مرة على التخزين المحلي في الملف، يجب كتابة البيانات بصيغة JSON 

    أي بدل السطر التالي:

    
    file.writeAsStringSync(responsebody.toString(), flush: true, mode: FileMode.write);
                           ^^^^^^^^^^^^^^^^^^^^^^

    سوف يكون:

    
    file.writeAsStringSync(json.encode(responsebody), flush: true, mode: FileMode.write);
                           ^^^^^^^^^^^^^^^^^^^^^^^^^

    وللقراءة نعمل التعديل:

    
    data = await json.decode(await file.readAsString());

     

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

    قمت بتعديل ذلك الكود كامل كالتالي الان:

    
    
    
    void main() {
      runApp( proviedsave());
    }
    
    class proviedsave extends StatefulWidget {
      const proviedsave({Key? key}) : super(key: key);
    
      @override
      _proviedsaveState createState() => _proviedsaveState();
    }
    
    class _proviedsaveState extends State<proviedsave> {
    
      final _baseUrl = 'https://jsonplaceholder.typicode.com/posts';
    
      @override
     void initState() {
        _firstLoad();
    
      }
    
    bool _isFirstLoadRunning=false;
    
       _firstLoad() async {
        setState(() {
          _isFirstLoadRunning = true;
        });
    
        String fileName = "userdata.json";
        var dir = await getTemporaryDirectory();
        File file = new File(dir.path + "/" + fileName);
        if (file.existsSync()) {
          print("Loading from cache");
          //var jsonData = file.readAsStringSync();
          var jsonData = await json.encode(await file.readAsString());
          // var responsebody = json.encode(jsonData);
          return jsonData;
    
        } else {
          print("Loading from API");
          var response = await http.get(Uri.parse('$_baseUrl'));
    
          if (response.statusCode == 200) {
    
            var responsebody = jsonDecode(response.body);
    
            //save json in local file
           // file.writeAsStringSync(responsebody.toString(), flush: true, mode: FileMode.write);
            file.writeAsStringSync(json.encode(responsebody), flush: true, mode: FileMode.write);
            return responsebody;
    
          }
        }
    
    
        setState(() {
          _isFirstLoadRunning = false;
        });
      }
    
    
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(home: Scaffold(body: ListView(shrinkWrap: true,scrollDirection: Axis.vertical,children: [
    Text('sssssssss'),
    
          FutureBuilder(
              future: _firstLoad(),
              builder: (context, AsyncSnapshot snapshot) {
                if (snapshot.hasData) {
                  return ListView.builder(
                      physics: ScrollPhysics(),
                      scrollDirection: Axis.vertical,
                      shrinkWrap: true,
                      itemCount: snapshot.data.length.clamp(0, 6),
                      itemBuilder: (context, index) {
                        return
                        Card(
                          // elevation: 1,
                          child: Container(
                            padding: EdgeInsets.all(6),
                            child: ListTile(
                              title: Text(snapshot.data[index]['title'].toString()),
                             // subtitle: Text(snapshot.data[index]['body'].toString()),
                            ),
                          ),
                        );
                      }
                  );
    
                } else if (snapshot.hasError) {
    
                  return Center(
                      child: Image.asset(
                        'assets/no_dataa.png',
                        fit: BoxFit.contain,
                        width: 180,
                        height: 180,
                      ));
    
                }
                // By default, show a loading spinner.
                return _isFirstLoadRunning? SizedBox(
                  height: MediaQuery.of(context).size.height / 1.3,
                  child: Center(
                    child: CircularProgressIndicator(
                      valueColor:
                      new AlwaysStoppedAnimation<Color>(Colors.black),
                    ),
                  ),
                ):Center(
                    child: SizedBox(
                      height: MediaQuery.of(context).size.height / 1.3,
                      child: Image.asset(
                        'assets/EmptyFavorite.png',
                        fit: BoxFit.contain,
                        width: 370,
                        height: 370,
                      ),
                    ));
              }
    
          )
        ]),),);
      }
    }

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

     

    Loading from cache
    
    ======== Exception caught by widgets library =======================================================
    The following _TypeError was thrown building:
    type 'String' is not a subtype of type 'int' of 'index'

     

     

    لما أقوم بتحديد موقع الخطاء يظهر في السطر التالي:

    ListTile(
                              title: Text(snapshot.data[index]['title'].toString()),

     

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

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

    ولكن كل ما حاولت اعمل تشغيل لتطبيق احصل على الخطاء التالي:

    I/flutter ( 4119): Loading from cache
    E/flutter ( 4119): [ERROR:flutter/lib/ui/ui_dart_state.cc(209)] Unhandled Exception: FormatException: Unexpected character (at character 3)
    E/flutter ( 4119): [{userId: 1, id: 1, title: sunt aut facere repellat provident occaecati exc...
    E/flutter ( 4119):   ^
    E/flutter ( 4119): 

     

     

    الصفحة كامل كالتالي:

    void main() {
      runApp( proviedsave());
    }
    
    class proviedsave extends StatefulWidget {
      const proviedsave({Key? key}) : super(key: key);
    
      @override
      _proviedsaveState createState() => _proviedsaveState();
    }
    
    class _proviedsaveState extends State<proviedsave> {
    
      final _baseUrl = 'https://jsonplaceholder.typicode.com/posts';
    
      @override
     void initState() {
        _firstLoad();
    
      }
    
    bool _isFirstLoadRunning=false;
    
       _firstLoad() async {
        setState(() {
          _isFirstLoadRunning = true;
        });
    
        String fileName = "userdata.json";
        var dir = await getTemporaryDirectory();
        File file = new File(dir.path + "/" + fileName);
        if (file.existsSync()) {
          print("Loading from cache");
          var jsonData = file.readAsStringSync();
          var responsebody = jsonDecode(jsonData);
          return responsebody;
    
        } else {
          print("Loading from API");
          var response = await http.get(Uri.parse('$_baseUrl'));
    
          if (response.statusCode == 200) {
    
            var responsebody = jsonDecode(response.body);
    
            //save json in local file
            file.writeAsStringSync(responsebody.toString(), flush: true, mode: FileMode.write);
    
            return responsebody;
    
          }
        }
    
    
        setState(() {
          _isFirstLoadRunning = false;
        });
      }
    
    
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(home: Scaffold(body: ListView(shrinkWrap: true,scrollDirection: Axis.vertical,children: [
    Text('sssssssss'),
    
          FutureBuilder(
              future: _firstLoad(),
              builder: (context, AsyncSnapshot snapshot) {
                if (snapshot.hasData) {
                  return ListView.builder(
                      physics: ScrollPhysics(),
                      scrollDirection: Axis.vertical,
                      shrinkWrap: true,
                      itemCount: snapshot.data.length.clamp(0, 6),
                      itemBuilder: (context, index) {
                        return
                        Card(
                          // elevation: 1,
                          child: Container(
                            padding: EdgeInsets.all(6),
                            child: ListTile(
                              title: Text(snapshot.data[index]['title'].toString()),
                             // subtitle: Text(snapshot.data[index]['body'].toString()),
                            ),
                          ),
                        );
                      }
                  );
    
                } else if (snapshot.hasError) {
    
                  return Center(
                      child: Image.asset(
                        'assets/no_dataa.png',
                        fit: BoxFit.contain,
                        width: 180,
                        height: 180,
                      ));
    
                }
                // By default, show a loading spinner.
                return _isFirstLoadRunning? SizedBox(
                  height: MediaQuery.of(context).size.height / 1.3,
                  child: Center(
                    child: CircularProgressIndicator(
                      valueColor:
                      new AlwaysStoppedAnimation<Color>(Colors.black),
                    ),
                  ),
                ):Center(
                    child: SizedBox(
                      height: MediaQuery.of(context).size.height / 1.3,
                      child: Image.asset(
                        'assets/EmptyFavorite.png',
                        fit: BoxFit.contain,
                        width: 370,
                        height: 370,
                      ),
                    ));
              }
              
          )
        ]),),);
      }
    }
    

     

     

    كيف يمكن حل هذا المشكلة هل يوجد احد لديه فكره عن الامر؟

     

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

    إن path_provider مختلفة عن sharedpreferences 

    path_provider هي plugin توفر واجهة برمجية تعطينا مسارات ضمن نظام الملفات الخاص بنظام التشغيل أي file system لإنشاء وتخزين ملفات / كبيرة الحجم نسبياً / نحتاجها بشكل متكرر في التطبيق و لكي نقلل وقت تحميلها من الشبكة لكل مرة نطلبها. (بدون عمل قاعدة بيانات محلية)

    بشكل رئيسي نستعملها مثل ذاكرة تخزين مؤقت كاش cache memory لتوفير الملفات محلياً بدل طلبها من السيرفر.

    sharedpreferences تستخدم لحفظ بيانات على شكل ثنائية (مفتاح / قيمة) وتكون البيانات حجمها صغير نسبياً مع إمكانية حفظ قائمة من السلاسل النصية List Of Strings وليس ملفات كبيرة، مثلاً نستعملها لحفظ تفضيلات المستخدم مثل theme التطبيق هل هو فاتح أو غامق light - dark أو بيانات المستخدم البسيطة مثل اسمه رقمه أي شيء متوسط إلى صغير. هي تخزين دائم لكن لانضع به بيانات خاصة و حرجة لأنها تحذف مع بيانات التطبيق مثلاً إن عملت clear data 

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

    اذن نستعمل مع الاتصالات وتخزين القوائم وما شابه الأفضل path_provider حسب ما فهمت منك بالإضافة الى ان كل المكاتب هذا لو قام المستخدم بحذف التطبيق سوف يتم حذف البيانات المخزنة فيهم 

    الان فهمت لماذا التطبيق لدي اصبح يعمل كراش كل ما عملت hot restart 

    انا استعملت sharedpreferences لتخزين قوائم كبيره لتسريع عمل التطبيق للمستخدم ولكن مع الأسف الفكرة كانت خطاء واصبح التطبيق يعمل كراش بسبب طول القوائم واحتواها على الصور والنصوص الكبيرة

     

    لك كل الشكر والعرفان اخي الكريم

    • أعجبني 1
  9. تحيه طيبه للجميع 

    ارغب بالاستفسار عن مكتبة path_provider لو كنت ارغب بتخزين بيانات التي احصل على من السيرفر واعرضها بطبيعة الحال من خلال listview  ايهم افضل لفعل ذلك هل path_provider او sharedpreferences   وما الفرق بينهم لو امكن التوضيح وايهم افضل لفعل ذلك؟

     

    شكرا للجميع

    • أعجبني 1
  10. بتاريخ 6 ساعات قال Wael Aljamal:

    أي تفصد قراءة قيمة _limit ؟

    تتعلق قيمتها بالمفتاح results ونجلبه في PHP حسب نوع الطلب ولديك http.get 

    
    <?php
    
    $limit = $_GET['results'];
    
    
    include 'con.php';
    
    ..

     

    اكتب url

    
    final String url = "*********************.php?results=$_limit";

     

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

    نعم الان فهمت الامر وكلامك صحيح 100% انا لم اكن اعلم بوجود القواعد الثابتة limit and offset هذا اول مره اتعامل معهم لذلك كنت تائه اعتذر على ذلك 

    كل الشكر لك اخوي

  11. بتاريخ منذ ساعة مضت قال Mahmoud Seed:

    انا لاحظت ان vs code افضل من حيث الشرح للاخطاء والاقتراحات  

    هل هي اعدادات يمكن اضلفتها لاندرويد ستوديو

    اهلا اخي

    حقيقه ان في جانب الإضافة لا يوجد أي وجه للمقارنة صحيح ان الاندرويد ستوديو هو البيئة الرسمية لجوجل ولكن vs code  المطورين من خلال الإضافة عاملين شغل كبير فيه  

  12. بتاريخ 7 ساعات قال Wael Aljamal:

    علامة & غير ضرورية لأنها توضع بين ثنائيتين مختلفتين. x=1&y=2 أما في حالتك لديك مفتاح وحيد حتى = بعد اشارة الاستفهام غير ضرورية x=1?

     

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

    احتاج اعرف كيف يمكنني تحديد هذا keyword 

     

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

    إن Text widget تقبل معامل القيمة من نوع ?String

    حاول طباعة:

    
    print(users)
    
    print(users[index]['name'])

    على الأغلب المشكلة في index 

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

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

    المشكلة كالتالي:

    لدي  عمود يحتوي على كل من الاسم نص + ارقام 

    لو عملت الكود بشكل أعلاه بدون إضافة تحويل الى نص او tostring  سيعمل اول صفحة ثم يتوقف مع الصفحة الثانية بسب وجود الأرقام مع النصوص ولو قمت بإضافة tostring  احصل على القائمة كامل بدون استقطاع كنت أتوقع ان هذا الامر خطاء ولكن في الحقيقة هو صحيح فقد كان يجلب النصوص + الارقام وهذا الامر صحيح من خلال السطر التالي:

    ListTile(title: Text((users[index]['name'].toString()))) ;

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

     

    final String url = "*********************.php?=" + "&results=$_limit";

    السطر هذا  يحتوي على URL  بالإضافة الى  limit وهو الذي نستطيع من خلاله تحديد كمية الاقتصاص المشكلة تكمن هنا في الكلمة  "&results= اذا لم تكن الكلمة صحيحه لا يتم الاقتصاص 

     

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

    انا اجلب البيانات من خلال ملف php التالي لا اعلم اذا كان من المفترض ان تكون من خلاله 

    <?php
    
    
    include 'con.php';
    
    
    
    $sql = "SELECT  name FROM User ORDER BY id DESC";
    
    
    $stmt = $con->prepare($sql); 
    
    
    $stmt->execute();
    $result = $stmt->get_result();
    $json = [];
    if ($result) {
        while ($row = $result->fetch_assoc()) {
            $json[] = $row;
        }
    }
    echo json_encode($json, JSON_NUMERIC_CHECK);
    
    ?>

     

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

    كنت أقوم بعملية تطبيق تقسيم القائمة  Lazy Loading ولكن واجهتني مشكلة لم اجد حل لها 

    لو قمت بتشغيل التطبيق سيقوم التطبيق بتحميل الصفحة الأولى حسب العدد الذي قمت بتحديده مسبقا من خلال Lazy Loading وليكن 15 مستخدم او 15 اسم 

    ولكن لو حاولت الانتقال الى الجزء التالي من القائمة وهيا ال 15 التالي سوف احصل على الخطاء التالي:

    type 'int' is not a subtype of type 'String'

    قمت بمحاولة وضع toString كالتالي:

    ListTile(title: Text((users[index]['name'].toString()))) ;

    ولكن المشكلة في هذا الحالة يتم تحميل القائمة كاملة ولا يتم تقسيمها بمعنى خاصية Lazy Loading لا تعمل في هذا الحالة

     

    الكود المستعمل كالتالي:

    class HomeState extends State<Home> {
      static int page = 0;
      ScrollController _sc = new ScrollController();
      bool isLoading = false;
      List users = [];
    
      @override
      void initState() {
        this._getMoreData();
        super.initState();
        _sc.addListener(() {
          if (_sc.position.pixels == _sc.position.maxScrollExtent) {
            _getMoreData();
          }
        });
      }
    
      @override
      void dispose() {
        _sc.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              title: const Text("Lazy Load Large List"),
            ),
            body: Container(
              child: _buildList(),
            ),
            resizeToAvoidBottomInset: false,
          ),
        );
      }
    
    
      Widget _buildList() {
        return ListView.builder(
          itemCount: users.length + 1,
    
          padding: EdgeInsets.symmetric(vertical: 8.0),
          itemBuilder: (BuildContext context, int index) {
            if (index == users.length) {
              return _buildProgressIndicator();
            } else {
              return ListTile(title: Text((users[index]['name']))) ;
    
            }
          },
          controller: _sc,
        );
      }
    
    
      final int _limit = 15;
      Future _getMoreData() async {
        if (!isLoading) {
          setState(() {
            isLoading = true;
          });
    
          final String url = "*********************.php?=" + "&results=$_limit";
          final response = await http.get(Uri.parse(url));
    
          print(url);
    
          List tList = [];
          var responsebody = jsonDecode(response.body);
          for (int i = 0; i < responsebody.length; i++) {
            tList.add(responsebody[i]);
            print(tList);
    
          }
          setState(() {
            isLoading = false;
            users.addAll(tList);
            page++;
          });
        }
      }
    
      Widget _buildProgressIndicator() {
        return new Padding(
          padding: const EdgeInsets.all(8.0),
          child: new Center(
            child: new Opacity(
              opacity: isLoading ? 1.0 : 00,
              child: new CircularProgressIndicator(),
            ),
          ),
        );
      }
    }

     

     

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

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

    حقيقه ذهب مني وقت كبير جدا وانا أحاول تطبيق lazy loading مع FutureBuilder  ولكن لم تنجح لدي ولا اعلم ما هو السبب وانا لا ارغب باستعمال مكتبة جاهزة لفعل ذلك 

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

    شكرا للجميع 

    صفحة الكود لدي كالتالي:

    class _HomePageState extends State<YourPost> {
    
    
    
    
      @override
      void initState() {
        super.initState();
      }
    
    
      Future ApiUserPost() async {
    
        final String url = '*************';
        var response = await http.post(Uri.parse(url));
        var responsebody = jsonDecode(response.body);
    
        if (responsebody.length > 0) {
          return responsebody;
        } else {
    
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            body: Center(
          child: Container(
            child: SingleChildScrollView(
              child: Column(
                children: <Widget>[
                  FutureBuilder(
                      future: ApiUserPost(),
                      builder: (context, AsyncSnapshot snapshot) {
                        if (snapshot.hasData) {
                          return ListView.builder(
                              physics: ScrollPhysics(),
                              scrollDirection: Axis.vertical,
                              shrinkWrap: true,
                              itemCount: snapshot.data.length.clamp(0, 6),
                              itemBuilder: (context, index) {
                                return Card(
                                  // elevation: 1,
                                  child: Container(
                                    padding: EdgeInsets.all(6),
                                    child: Row(
                                      children: <Widget>[
                                        Flexible(
                                          child: Column(
                                            crossAxisAlignment:
                                                CrossAxisAlignment.start,
                                            children: <Widget>[
                                              MergeSemantics(
                                                child: Row(
                                                  children: <Widget>[
                                                    Flexible(
                                                      child: Text(
                                                        "${snapshot.data[index]['name']}",
                                                      ),
                                                    )
                                                  ],
                                                ),
                                              ),
                                              SizedBox(height: 5),
                                            ],
                                          ),
                                        )
                                      ],
                                    ),
                                  ),
                                );
                              });
                        } else if (snapshot.hasError) {
                          Center(
                              child: CircularProgressIndicator(
                                valueColor:
                                new AlwaysStoppedAnimation<Color>(Colors.black),
                              ),);
                        }
    
                        return SizedBox(
                          height: MediaQuery.of(context).size.height / 1.3,
                          child: Center(
                            child: CircularProgressIndicator(
                              valueColor:
                                  new AlwaysStoppedAnimation<Color>(Colors.black),
                            ),
                          ),
                        );
                      })
                ],
              ),
            ),
          ),
        ));
      }
    }

     

  16. بتاريخ 5 ساعات قال Wael Aljamal:

    لقد وجدت فكرة خلال البحث، وهي حقن ال divider لمصفوفة widgets بالاستعانة بمفهوم الإضافة extention من خلال عمل مؤشر يتحرك على القائمة ثم يبني واحدة أخرى ديناميكياً بإضافة widget هي separator

    
    extension IterableExt<T> on Iterable<T> {
      Iterable<T> superJoin(T separator) {
        final iterator = this.iterator;
        if (!iterator.moveNext()) return [];
    
        final _l = [iterator.current];
        while (iterator.moveNext()) {
          _l..add(separator)..add(iterator.current);
        }
        return _l;
      }
    }

    وطريقة الاستدعاء تكون:

    
    return Row(
          children: <Widget>[
            const Text('Foo'),
            const Text('Foo'),
            const Text('Bar'),
          ].superJoin(const SizedBox(width: 10,)).toList(),
          
        );

    نقوم باستدعاء superJoin وتمرير الفاصل له ليقوم بحقنه بين كل عنصرين.

    لطريقة أخرى، عليكا بناء دالة تعيد 

    
    List<Widget>

    وتنفيذ الفكرة كالتالي:

    • دالة تعيد قائمة من الواجهات
    • تستقبل قائمة من العناصر 
    • نمرر لها separator
    • نقوم بالمرور على القائمة الأساسية و نأخذ منها العناصر ونضيفهم للقائمة الثانية مع ال separator بالتناوب
    • سوف يمثل استدعاء هذه الدالة قيمة children
    
    
    List<Widget> createRowChildrenWithPadding(List<Widget> widgets, Padding pading) {
        var joined = List<Widget>();
    
        for (var i = 0;  i < widgets.length; i++) {
            if ( i != widgets.length - 1)  {
                joined.add(widgets[i]);
                joined.add(padding);
            } else {
                joined.add(widgets[i]);
            }
        }
        return joined;
    }

    بالنسبة للخطأ السابق،

    أعتقد أن حله يكون كالتالي:

    
    return Column(
                      children: <Widget>
                        snapshot.data[index].forEach((key, value){
                          return Container(
                            margin:  EdgeInsets.only(left: 20.0, right: 20.0),
                            child:  Text(snapshot.data[index]['name']),
                          );
    
                        }
                        ),
    
                    );

     

    اهلا اخي الكريم اعتذر على تأخر الرد

    بالنسبة الي الطريقة الأولى حقيقه تبدو معقده قليلا علي 

    اما التعديل الثاني ما تزال نفس المشكلة 

    type 'Null' is not a subtype of type 'Widget'

     

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

    الصورة التقريبية التي أرفقتها للتصميم

    كيف يظهر التصميم حالياً.

    نعم اخي

    شكل القائمة المعتاد ما أحاول القيام به 

    عناصر متاليه 

    كل واحد يأتي بعد الذي أعلاه

    هو شغال بشكل هذا ولكن الفواصل لا تظهر بينهم

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

    ما السطر الذي يعيد null؟

    لقد فهمت من سؤالك الأول أنك تريد وضع القيم بجانب بعضها مع مسافات أفقية بينها..

    ما الفائدة من استخدام ListView.separated إن كان لديك عنصر واحد؟ أليس من الأفضل عمل التصميم بشكل عمود بسيط

    هذا السطر الذي يعيد قيمة فارغة :

        snapshot.data[index].forEach((key, value){

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

    ما الفائدة من استخدام ListView.separated إن كان لديك عنصر واحد؟ أليس من الأفضل عمل التصميم بشكل عمود بسيط

    التصميم عمودي اخي وليس افقي 

    يعني الاسم

    ثم ياتي اسفله

    العمر

    ثم ياتي اسفل العمر

    الموقع .. الخ

     

    ولكني ارغب بوضع بين هذا الاسطر الفاصله

  19. بتاريخ الآن قال Wael Aljamal:

    عملت تعديل للكود الأول،

    الفكرة أن الفاصل العمودي عملته هوامش خارجية للحاوبة container و كل قيمة ضمن Text ويتم المرور على snapshot.data[index] و استخلاص القيم حسب عدد المفاتيح فيها

    مرحبا اخي نعم اعتذر كنت أحاول تطبيق فكرة 

    قمت بإرجاع الكود ولكن نفس المشكلة تظهر

  20.  ListView.separated(
                  separatorBuilder: (BuildContext context, int index) {
                    return Divider(
                      thickness: 1,
                    );
                  },
                  shrinkWrap: true,
                  itemCount: snapshot.data.length,
                  scrollDirection: Axis.vertical,
                  physics: ScrollPhysics(),
                  itemBuilder: (BuildContext context, int index) {
                    return Column(
                      children: <Widget>[
                        snapshot.data[index].forEach((key, value){
                          return Container(
                            margin:  EdgeInsets.only(left: 20.0, right: 20.0),
                            child:  Text(snapshot.data[index]['name']),
                          );
    
                        }
                        )],
    
                    );
                  });

    قمت بتطبيق الكود اخي بشكل أعلاه ثم حصلت على الخطاء التالي:

    type 'Null' is not a subtype of type 'Widget'

     

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

    لدي جدول بيانات اون لاين أحاول عرض اسطر منه في قائمة ListView.separated وارغب ان اضع بين هذا البيانات فواصل separated على سبيل المثال 

    يوجد في الجدول الاسم ثم العمر ثم الموقع .. الخ

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

    ولكن  ListView.separated لا تعمل في هذا الحالة.  انا اعلم لو كنت اجلب بيانات مجموعة من Row  متعددة من قاعدة البيانات سوف يتم وضع فواصل بين كل Row  ولكن الان  انا أحاول ان اعمل العكس المطلوب عرضه Row واحد ثم اجلب بيانات كل الاعمدة فيه ثم اضع فواصل بين بيانات الاعمدة هذا 

     

    هذا الجدول:

    +---------+--------------+---------------------+
    | user_id | Name         | Age       | Lcation |
    +---------+--------------+-----------+---------+
    |    1    | Irene        | 22        |   A     |
    +---------+--------------+---------------------+

     

    وهذا الكود:

     

    Container(
          child:
    
          FutureBuilder(
        future: ApiService().TopOne(ID),
        builder: (context, AsyncSnapshot snapshot) {
          if (snapshot.hasData) {
            return ListView.separated(
              separatorBuilder: (BuildContext context, int index) {
                return Divider(
                  thickness: 1,
                );
              },
              scrollDirection: Axis.vertical,
              shrinkWrap: true,
              physics: ScrollPhysics(),
              itemCount: snapshot.data.length,
              itemBuilder: (context, index) {
                return Container(
                    child: Column(
                  children: [
                    Text("${snapshot.data[index]['Name']}"),
                    Text("${snapshot.data[index]['Age']}"),
                    Text("${snapshot.data[index]['Location']}"),
                    Text("${snapshot.data[index]['Others']}"),
                  ],
                ));
              },
            );
          } else if (snapshot.hasError) {
            return Center(
                child: Image.asset(
              'assets/nodata.png',
              fit: BoxFit.contain,
              width: 180,
              height: 180,
            ));
          }
    
     
          return Center(
              child: SizedBox(
            height: MediaQuery.of(context).size.height / 1.3,
            child: Image.asset(
              'assets/no_dataa.png',
              fit: BoxFit.contain,
              width: 180,
              height: 180,
            ),
          ));
        },
      ));
    }

     

    هل توجد طريقة لفعل ذلك؟

    طبعا انا لا ارغب بوضع Divider() بشكل يدوي بعد كل سطر احتاج ان يكون الامر بشكل تلقائي مثل طبيعة عمل ListView.separated 

     

    صورة توضح ما أحاول القيام به:

    screenshot-2022-08-06_13_35_21_23.thumb.png.83971c3a87ac6106bba2ac9a6ec06c35.png

  22. بتاريخ 18 ساعات قال Wael Aljamal:

    حاول طباعة كل من:

    
    قبل الحلقة
    لمرة واحدة
    print(UserSimplePreferences.getNotficationAppID());
    
    ثم ضمن الحلقة:
    
    print(responsebody[i]['id']);

    على كل حال، لم أفهم الفائدة من الحلقة، سوف تختبر أكثر من id من response وفي حال كان نفسه تعمل break وإن كان غيره تعمل على تعديله؟

    وماذا يحوي responsebody هل يمكنك طباعته بشكل منسق لفهم بنيته؟

    وما هي برمجة الدالة getNotficationAppID؟

    ربما الاختلاف أن هنالك نمط مختلف بين البيانات (نصية و رقمية) و عملية المقارنة لا تتم بشكل صحيح.

    
    responsebody[i]['id'].toString() == UserSimplePreferences.getNotficationAppID()
                        ^^^^^^^^^^^^

     

    اهلا اخي الكريم كنت أحاول احل مشكلة badges بحيث اعمل استعلام عن القديم والحديث حتى اظهر اشعار badges 

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

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

    قمت بتخزين البيانات مسبقا على قاعدة البيانات وقمت بتخزين بيانات مسابقا أيضا على حفظ محلي Shared Preferences

    الان انا أحاول ان اعمل مقارنه بين البيانات التي احصل عليها من قاعدة البيانات والبيانات التي قمت بتخزينها في Shared Preferences اذا كانت متطابقة او لا 

    استعمل الكود التالي:

    Future ApiNotficationApp() async {
        final String url = '****************';
        var response = await http.post(Uri.parse(url));
        var responsebody=jsonDecode(response.body);
        if (responsebody.length >0){
          for (int i = 0; i < responsebody.length; i++) {
              if (responsebody[i]['id'] == UserSimplePreferences.getNotficationAppID() ){
    
                print('\x1B[31m${'Equal'}\x1B[0m');
                break;
              }else{
                UserSimplePreferences.setNotficationApp('');
                UserSimplePreferences.setNotficationAppID(responsebody[i]['id'].toString());
                print('\x1B[33m${'Not Equal'}\x1B[0m');
                print('\x1B[33m${UserSimplePreferences.getNotficationAppID()}\x1B[0m');
                break;
              }
          }
          return responsebody;
        }else{
    
        }
      }

    ولكني في كل مره احصل على نتيجة ليس متطابقة او Not Equal برغم من ان البيانات متطابقة لا اعلم لماذا 

    أتمنى من لديه فكره يخبرنا عن طريقة حل المشكلة 

    • أعجبني 1
  24. تحيه طيبه للجميع

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

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

    الدي استفسار لو تكرمتم 

    هل توجد طريقة لتحكم ب Drawer من صفحة مختلفة ؟

    على سبيل المثال انا واضع Drawer في صفحة ازار  Button Bar وهيا تحتوي على 4 صفحات يتم عرضهم في داخلها

    الان انا ارغب بتحكم او فتح  Drawer من صفحة ال home  

    هل يمكن فعل ذلك؟

    طبعا حاولت اضع Drawer في صفحة home   ولكن تحدث مشكلة وهيا ان Drawer    يأتي تحت ازار  Button Bar لذلك الحل الوحيد لدي ان اضع Drawer في صفحة Button Bar ثم أقوم باستخدامه من خلال صفحة Home 

    مثل الصورة التأليه:

     63652350-21999c80-c77f-11e9-9da6-56b47c36ed27.thumb.jpg.e4a07e4ba562842d6ffb5b077c29dc26.jpg

     

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