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

بلال زيادة

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

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

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

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

    30

كل منشورات العضو بلال زيادة

  1. هل يمكنك إرفاق الأحداثيات لموقعك و الموقع الأخر , أي كيف تكون شكل الأحداثيات ؟
  2. أن باستخدامك mysqli سوف تجد أنه هناك متغير تم إضافته لدوال الاستعلامات ففي مثلاً دالة mysqli_query سترى أنها تأخذ متغيرين المتغير الأول معامل الأتصال و المعامل الثاني هو معامل الاستعلام , لذلك $con هي معامل الأتصال أي يجب أن يكون $con = mysqli_query("localhost", "username", "password", "dbname"); ومن ثم يجب تمرير $con إلى دالة mysqli_query بهذا الشكل $query = mysqli_query($con, "SELECT * FROM table");
  3. الطريقة التي ذكرتها لك هي طريقة عامة بحيث كيف نحمي الريكويستات الخاصة بِ api إذا تم الوصول إليها من أشخاص أخرين , يعني عندما نضع كود دخول يكون عام لكل الريكويستات هنا نضع عامل حماية للريكويست.
  4. ربما تقصد أنه لديك وقت ما يكون بصيغة timestamp بهذا الشكل 86400 لاحظ ان الرقم السابق هو عدد الثواني في 24 ساعة ,فيمكنك برمجة دالة تقوم بهذا الأمر , لأن الدالة ستسمح لك باستخدامها في أي مكان في مشروعك فنقوم بإنشاء دالة وليكن اسمها secondsToString بهذا الشكل function secondsToString( $seconds ){ } لاحظ أننا مررنا لها رقم الثواني , الأن يمكننا الاستفادة من رقم الثواني هذا في تحويله إلى ثواني و دقائق و ساعات , نقوم باستخدام دالة intval لنحصل على القيمة الصحيحة للرقم $seconds = intval( $seconds ); ثم نقوم باستخراج الساعة عن طريق تمرير عدد الثواني مقسوم على 3600 و هو عدد الثواني في الساعة الواحدة $h = floor( $seconds / 3600 ); ثم نقوم باستخراج عدد الدقائق من خلال جلب باقي قسمة الثواني على 3600 ثم قسمة الرقم الناتج على 60 $m = floor( $seconds % 3600 / 60 ); ثم عدد الثواني من خلال جلب باقي قسمة الثواني على 60 $s = floor( $seconds % 60 ); ثم نقوم بإرجاع القيم هذه من خلال دالة return و نضع بين المتغيرات النص ":" بهذه الطريقة return $h.":".( $m < 10 ? "0" : "" ).$m.":".( $s < 10 ? "0" : "" ).$s; فسيكون الناتج بهذا الشكل 23:59:59 فتكون كامل الدالة function secondsToString( $seconds ){ $seconds = intval( $seconds ); $h = floor( $seconds / 3600 ); $m = floor( $seconds % 3600 / 60 ); $s = floor( $seconds % 60 ); return $h.":".( $m < 10 ? "0" : "" ).$m.":".( $s < 10 ? "0" : "" ).$s; }
  5. يمكنك استخدام طريقة أغلب المبرمجين يتعاملون بها , وهي الاعتماد على access_token المرجع و ايضا كود دخول ثاني يتم ارسالها في كل ريكويست بحيث كود الدخول هذا يكون ثابت و يكون متعارف عليه بين مبرمج الأندرويد و مبرمج الباك أند , بحيث إذا كان الريكويست يسمح بعرض البيانات من غير أن يكون هناك عملية تسجيل الدخول , ففي الحالة التالي يكون يجب الوضول إليه باستخدام كود الدخول المتعارف عليه , بحيث يتم إرسال هذا الكود في Header و يتم التعامل معه في الباك أند بأنه إذا لا يوجد كود دخول لن يتم عرض البيانات في حالة لا يوجد تسجيل دخول , وكذلك في حالة كان الريكويست يحتاج عملية تسجيل دخول ( لتوفير access_token ) أيضا لا يعرض بيانات إلا في حالة أن يكون مرسل إليه كود الدخول و access_token . هذا أسلوب يوفر عليك حماية api.
  6. في البداية يمكنك وضع زر تذكرني في واجهة الدخول بالتطبيق الخاص بك مثلاً يمكن وضع حقل الاختيار أسفل حقل إدخال كلمة المرور و يكون كود الزر Column( children: <Widget>[ Row( children: <Widget>[ Checkbox( checkColor: Colors.greenAccent, activeColor: Colors.blue, value: this.valuefirst, onChanged: (bool value) { setState(() { this.valuefirst = value; }); }, ), Text('Remember me ',style: TextStyle(fontSize: 17.0), ), ], ), ], ), و من ثم يمكنك تعريف متغير valuefirst بهذه الطريقة bool valuefirst = false; وتكون قيمته false أي حقل الأختيار غير معلم , ثم يمكننا إرسال قيمة هذا الحقل إلى الدالة التي نقوم بالتحقق منها من عملية تسجيل الدخول مثل الكود التالي await Provider.of<AuthProvider>(context, listen: false).login(username, password, this.valuefirst); فإذا كانت قيمة valuefirst هي false أي لا يحفظ بيانات تسجيل الدخول , أما إذا كانت true فيجب حفظ هذه البيانات لعمل تسجيل دخول تلقائي في المرة القادمة التي يدخل بها العضو التطبيق. ثم في دالة login في نقوم باستخدام قيمة المتغير valuefirst بهذا الشكل Future<bool> login(String email, String password, bool savePassword) async { final url = 'هنا رابط مسار login '; Map<String, String> body = { 'email': email, 'password': password, }; final response = await http.post( Uri.parse(url), body: body, ); if (response.statusCode == 200) { Map<String, dynamic> apiResponse = json.decode(response.body); if (apiResponse['code'] == 200) { _token = apiResponse['user']['token']; _id = apiResponse['user']['id']; _email = apiResponse['user']['email']; _name = apiResponse['user']['name']; _savePassword = savePassword; await storeUserData(apiResponse, _savePassword); notifyListeners(); return true; } else { _status = Status.Unauthenticated; _token = null; _email = null; _name = null; _savePassword = false; notifyListeners(); return false; } } if (response.statusCode == 401) { notifyListeners(); return false; } notifyListeners(); return false; } فلاحظ أيضا قمنا بإرسال قيمة هذا المتغير مرة أخرى في دالة storeUserData storeUserData(apiResponse, bool savePassword) async { SharedPreferences storage = await SharedPreferences.getInstance(); await storage.setString('token', apiResponse['user']['token']); await storage.setString('email', apiResponse['user']['email']); await storage.setString('name', apiResponse['user']['name']); await storage.setBool('savePassword', savePassword); } لكي نخزن قيمة هذا الحقل في SharedPrefrence و نستطيع جلب هذه القيمة من أي مكان في التطبيق.
  7. سوف نقوم باستخدام inner join للربط بين الجدولين فيمكنك استخدام inner join بهذا الشكل "SELECT c.*, u.* FROM `chat` as c inner join users as u WHERE u.id = c.sender_id ORDER BY `id` DESC LIMIT 50"; ففي حالة الكود الخاص بك يتم تنفيذ هذا الاستعلام $stmt = $db->prepare("SELECT c.*, u.* FROM `chat` as c inner join users as u WHERE u.id = c.sender_id ORDER BY `ID` DESC LIMIT 50"); ومن ثم يمكنك الوصول إلى جميع الحقول بداخل الجدولين أو يمكنك تحديد مثلاً جميع حقول جدول ما و حقل من الجدول الثاني بهذا الشكل $stmt = $db->prepare("SELECT c.*, u.name FROM `chat` as c inner join users as u WHERE u.id = c.sender_id ORDER BY `ID` DESC LIMIT 50");
  8. فلنفرض أن هناك بيانات مرجعة من api على شكل json بهذا الشكل [ { "weight": 92, "week": "2021-08-15" }, { "weight": 87, "week": "2021-08-15" }, { "weight": 80, "week": "2021-08-15" }, { "weight": 81, "week": "2021-08-15" }, { "weight": 71, "week": "2021-08-15" }, { "weight": 60, "week": "2021-08-15" }, { "weight": 58, "week": "2021-08-15" } ] فيمكننا إنشاء Class Model لهذه البيانات بهذا الشكل class UserWeightModel { UserWeightModel(this.week, this.weight); final String week; final double weight; factory UserWeightModel.fromJson(Map<String, dynamic> json) { return UserWeightModel( json['week'].toString(), json['weight'].toDouble(), ); } } ثم يمكننا عمل دالة جلب البيانات من رابط مسار api بهذا الشكل Future loadChartData2() async { var data; final url = "هنا الرابط; final response = await http.get(Uri.parse(url), headers: { 'Accept': 'application/json', 'Authorization': 'Bearer ${token}', }); if (response.statusCode == 200) { data = json.decode(response.body); final dynamic result = json.decode(response.body); for (Map<String, dynamic> i in result) { chartData.add(UserWeightModel.fromJson(i)); } } return data; } لاحظ في السطر التالي 'Authorization': 'Bearer ${token}', أننا اعتمدنا على token المخزن من عملية تسجيل الدخول للعضو , حتى نستطيع جلب الأوزارن و التاريخ الخاصة به , ثم قمنا بإسناد البيانات المرجعة من api إلى List chartData , لأننا سنستخدم هذه List في التعامل مع الشارت chartData.add(UserWeightModel.fromJson(i)); ثم الأن سنقوم باستخدام مكتبة توفر لنا شارت جاهز للتعامل معه, فقمت باختيار مكتبة syncfusion_flutter_charts: ^19.2.49 يمكنك تثبيتها في ملف pubspec.yaml من هنا , ثم تقوم بتشغيل الأمر التالي flutter pub get ليتم تضمين المكتبة إلى مشروعنا, ثم في Widget Build الخاصة بنا يمكننا عرض الشارت من خلال الكود التالي return FutureBuilder( future: loadChartData2(), builder: (context, snapshot){ if(snapshot.hasData){ return SfCartesianChart( zoomPanBehavior: _zoomPanBehavior, primaryXAxis: CategoryAxis(), title: ChartTitle(text: ""), //legend: Legend(isVisible: true), tooltipBehavior: _tooltipBehavior, series: <ChartSeries<UserWeightModel, String>>[ LineSeries<UserWeightModel, String>( dataSource: chartData, xValueMapper: (UserWeightModel user, _) => user.week.substring(5), yValueMapper: (UserWeightModel user, _) => user.weight, dataLabelSettings: DataLabelSettings(isVisible: true), enableTooltip: true, ), ], /*primaryXAxis: NumericAxis( edgeLabelPlacement: EdgeLabelPlacement.shift),*/ ); } return Center(child: CircularProgressIndicator(),); }, ), فلاحظ أنه تم استخدام FutureBuilder لأننا نتعامل مع بيانات تأخذ وقت في عملية جلبها و ايضا في future قمنا باستدعاء الدالة التي قمنا بإنشائها مسبقا وهي loadChartData2() ثم لاحظ أنه تم عرض في المحور x التاريخ xValueMapper: (UserWeightModel user, _) => user.week.substring(5), و في المحور y تم عرض الوزن yValueMapper: (UserWeightModel user, _) => user.weight, و كانت dataSource الخاصة بهذا الشارت هي List chartData dataSource: chartData,
  9. هل يمكنك إرفاق نص المشكلة إذا كان يظهر لك , أو صورة للمشكلة ؟
  10. يمكنك جلب امتداد الملف عن طريق تقسيم اسم الملف بهذا الشكل $name = explode('.', $_FILES["file"]["name"]); ثم يمكنك برمجة كود ما ليقوم بوضع اسم بشكل عشوائي ويمكن استخدام الكود الذي أرفقته أنت بهذا الشكل $fileName = substr(md5(uniqid(rand(), true)),3,10); ثم يمكنك استخدام دالة end لجلب أخر عنصر في المصفوفة بهذا الشكل $end = end($name); ثم يمكنك دمج المتغيرات ببعضها بهذا الشكل $name = explode('.', $_FILES["file"]["name"]); $fileName = substr(md5(uniqid(rand(), true)),3,10); $end = end($name); $newName = $fileName . '.' . $end; ثم في دالة move_uploaded_file يمكنك وضع الاسم الجديد move_uploaded_file($_FILES["file"]["tmp_name"], $newName);
  11. يجب عليك في البداية تعريف الجلسة من خلال دالة session_start(); لنخبر المتصفح بأنه سوف نقوم بتخزين بعض البيانات في المتصفح, و من ثم يمكنك بعد تسجيل عملية الدخول القيام بحفظ اسم المستخدم أو ربما تقوم بحفظ بريده الألكتروني من خلال الجلسة مثل $_SESSION['username'] = "Hsoub"; أو $_SESSION['email'] = "test@email.com"; ومن ثم يمكنك في صفحات الموقع يمكنك التحقق من أن هناك جلسة مخزنة مثلاً تسمح لليوزر بالولوج إلى هذه الصفحة مثلاً if(! $_SESSION['username']){ // يتم توجيه اليوزر لصفحة الموقع الرئيسية إذا كان لا يوجد جلسه header("Location: index.php"); } //// هنا معلومات الصفحة التي تظهر لليوزر إذا كان مخزن جلسة هل يمكنك إرفاق ألية الكود الخاص بك ليتم النظر إليه و نساعدك في حل هذه المشكلة .
  12. يمكنك من هنا الدخول إلى هذا القسم إذا كنت مشترك في دورة دورة تطوير تطبيقات الويب باستخدام لغة PHP .
  13. يتم تحديث الدورات خلال كل فترة لتبقى الدروس جديدة و إضافة دروس جديدة و لغات ايضا و هذه الإضافات لا تلغي أي لغة و لكن يتم وضع لغات و دروس للغات التي تكون مستخدمة بشكلٍ كبير في مجال البرمجة مثل بايثون و جافاسكريبت . يمكنك مراسلة الدعم الفني من هنا , وتختار اكاديمية حسوب و تقوم بفتح تذكرة دعم فني و سيقوم الدعم الفني بحل مشكلتك بأسرع وقت ممكن.
  14. بالإضافة إلى إجابة المدرب عبدالمجيد , ايضا يمكنك إزالة الأسطر من بين الكلمات في السطر التالي let mark = ' <div class= "card"> <div class= "child"> <h1> TITLE</h1> <P> HELLO FREINDS</P> </div> </div> '; وجعل الكلمات و الوسوم متلاصقة لأن جافاسكريبت حساسة لحالة الأحرف و الفراغات فيمكنك جعل الوسوم متلاصقة ببعضها في سطر واحد مثل الكود التالي let mark = '<div class= "card"><div class= "child"><h1> TITLE</h1><P> HELLO FREINDS</P></div></div>'; ثم تقوم بتجربة تنفيذ الكود مرة أخرى ستجد أن الكود يعمل بشكلٍ جيد.
  15. لا هذا خطأ يجب ان لا تحتوي خاصية src على قيمة فارغة , لم أفهم كيف تقوم بجلب الصورة و وسم src فارغ , هل ممكن توضح آليه عمل الكود كاملاً لأنه يبدو هناك نقص ما في الكود.
  16. كل شيء سليم ولكن لا توجد صورة في خاصية src في السطر التالي echo '<img style="max-height: 100px !important; margin: 0 auto;" class="Sleeve-image" src="" alt="#">'; حاول وضع صورة حتى تظهر و الخطأ الذي ظهر لك بسبب أنه لا توجد صورة , حاول رجاء وضع صورة و أخبرني بالنتيجة.
  17. يمكنك في كل درس تواجه به مشكلة معينة أو لم تفهم كود ما, يمكنك وضع سؤال و هناك الكثير من المدربين الذين سيعملون على إجابتك و توضيح ما لم تفهمه و يساعدونك على تجاوز أي مشكلة تواجهك , يمكنك وضع السؤال تحت كل درس تشاهده في قسم التعليقات حتى يظهر تحت الفيديو الذي تشاهده. ايضا يمكنك طلب شرح أي جزئية او كود و سنعمل على شرحه بإذن الله لك .
  18. يجب تنزيله يدوياً من خلال الذهاب إلى المسار التالي Android\Sdk\extras\intel\Hardware_Accelerated_Execution_Manager ومن ثم يوجد برنامج intelhaxm-android , ثم تقوم بتثبيته بالضغط على البرنامج و إكمال خطوات تثبيته ومن ثم تقوم بإعادة تشغيل الحاسوب الخاص بك .
  19. باقي الملفات الموجودة هي ملفات تكميلية للواجهة و لكن يمكنك الاعتماد على الاكواد الخاصة ب tabbar و widget الخاصة بعرض المنتجات.
  20. هناك طريقة وهي استخدام StreamController بحيث هي طريقة من طرق إدارة الحالة في فلاتر. و الأمر معقد جداً و يجب أن تكون على دراية جيدة بكيفية استخدام StreamController. لاحظ بأنه يجب أن تقوم بتعلم StreamController حتى تفهم الإجابة جيداً لأنه في الوهلة الأولى ستجد أن الأمر قد يكون فيه صعوبة يمكنك إنشاء ملف وليكن مسؤول عن التنصت على أي حدث جديد و يعكسه على الواجهة أي يقوم بتنفيذ هذا الحدث على الواجهة و الميزة في ذلك إنها تقوم بتنفيذ الحدث بدون اللجوء إلى بناء نفسها من الصفر و هذا الملف فليكن اسمه StreamProductsBloc.dart ويمكننا تعريف عدة متغيرات في هذا الملف بهذا الشكل List<ProductsModel>? products; final StreamController<List<ProductsModel>> _productsController = StreamController<List<ProductsModel>>.broadcast(); final StreamController<int> _categoryController = StreamController<int>.broadcast(); Stream<List<ProductsModel>> get productsStream => _productsController.stream; StreamSink<int> get fetchProducts => _categoryController.sink; Stream<int> get getProductsForCategoriescategory => _categoryController.stream; APIService? apiService; int categoryID = 0; List<ProductsModel>? products; بحيث أول شيء نقوم بتعريف الكلاس مودل الخاص بالمنتجات. final StreamController<List<ProductsModel>> _productsController = StreamController<List<ProductsModel>>.broadcast(); و من ثم نقوم بتعريف StreamController ليقوم بالتحكم في العملية , ويتم توفير واجهات مختلفة لإنشاء تدفقات أحداث متنوعة. final StreamController<int> _categoryController = StreamController<int>.broadcast(); وحدة تحكم حيث يمكن الاستماع إليه أكثر من مرة . Stream<List<ProductsModel>> get productsStream => _productsController.stream; ثم نقوم بالحصول على البث للمراقبة. StreamSink<int> get fetchProducts => _categoryController.sink; ثم نقوم بإدخال حدث إضافي عن طريق StreamSink. StreamProductsBloc() { //this.categoryID = categoryID; this.products = []; apiService = APIService(); _productsController.add(products!); _categoryController.add(this.categoryID); _categoryController.stream.listen(_fetchCategoriesFromApi); } Future<void> _fetchCategoriesFromApi(int category) async { this.products = await apiService!.getProductsForCategories(category); _productsController.add(this.products!); } @override void dispose(){ _productsController.close(); _categoryController.close(); } نقوم بإنشاء دالة جلب بيانات التصنيف عند تمرير id التصنيف و نقوم بإرجاع جميع المنتجات و من ثم نقوم بإضافة هذه البيانات إلى وحدة التحكم. Future<void> _fetchCategoriesFromApi(int category) async { this.products = await apiService!.getProductsForCategories(category); _productsController.add(this.products!); } ثم نقوم بإضافة المنتجات و id التصنيف إلى وحدة التحكم في دالة StreamProductsBloc ثم نقوم بمراقبة الحدث عن طريق استخدام listen StreamProductsBloc() { //this.categoryID = categoryID; this.products = []; apiService = APIService(); _productsController.add(products!); _categoryController.add(this.categoryID); _categoryController.stream.listen(_fetchCategoriesFromApi); } ثم نقوم بإنشاء ملف واجهة عرض البيانات وليكن مثلاً باسم products_screen.dart و نقوم أولاً بإنشاء نسخة جديدة من الملف الذي قمنا بإنشائه مسبقاً StreamProductsBloc streamProductsBloc = StreamProductsBloc(); ثم ننشأ عدة متغيرات و نعرف TabController لوضع التصنيفات في هذه TabController bool get wantKeepAlive => true; HelperApi helperApi = new HelperApi(); int currentIndex = 0; late TabController _tabController; ثم نقوم بصنع Widget خاصة بعرض التصنيفات في TabController Widget _categories(List<CategoryModel> categoryModel, BuildContext context) { _tabController = TabController( initialIndex: 0, length: categoryModel.length, vsync: this, ); return Scaffold( appBar: AppBar( title: Text(S.of(context).products), bottom: TabBar( indicatorColor: Color(0XFF117182), indicatorWeight: 5, controller: _tabController, tabs: _tabs(categoryModel), isScrollable: true, onTap: (int index) { streamProductsBloc.fetchProducts .add(this.productsCategories[index].id!); }, ), ), body: Container( child: StreamBuilder( stream: streamProductsBloc.productsStream, builder: (context, AsyncSnapshot<List<ProductsModel>>? snapshot) { switch (snapshot!.connectionState) { case ConnectionState.none: // ignore: todo // TODO: Handle this case. return Center( child: CircularProgressIndicator(), ); case ConnectionState.waiting: // ignore: todo // TODO: Handle this case. return ShimmerGrid(); case ConnectionState.done: case ConnectionState.active: // ignore: todo // TODO: Handle this case. if (snapshot.hasError) { return Center( child: ShimmerGrid(), ); } else { if (!snapshot.hasData) { return ShimmerGrid(); } else { return _drawProducts(snapshot.data!, context); } } } }, ), ), ); } لاحظ أننا نقوم بجلب منتجات كل تصنيف عند الضغط عليها عن طريق استخدام id هذا التصنيف streamProductsBloc.fetchProducts.add(this.productsCategories[index].id!); و من ثم نقوم بإضافة هذا الحدث إلى streamProductBloc . ثم نقوم برسم Widget الخاص بالمنتجات Widget _drawProducts(List<ProductsModel> products, BuildContext context) { final cart = Provider.of<Cart>(context, listen: false); var initModel = Provider.of<InitModel>(context, listen: false,); var auth = Provider.of<AuthProvider>(context, listen: false); return Consumer<WishListProvider>( builder: (BuildContext context, wishListData, child) { return Container( padding: EdgeInsets.only(top: 24), child: Column( children: [ Flexible( child: GridView.builder( gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 200, childAspectRatio: 1 / 2, crossAxisSpacing: 20, mainAxisSpacing: 20), itemCount: products.length, itemBuilder: (context, index) { return Card( clipBehavior: Clip.hardEdge, elevation: 0.9, child: AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 375), child: SlideAnimation( verticalOffset: 50.0, child: FadeInAnimation( child: Padding( padding: const EdgeInsets.all(8.0), child: InkWell( onTap: () { _gotoSingleProduct(products[index], context); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Expanded( // ignore: unnecessary_null_comparison child: Stack( children: [ (products[index].images![0].src != null) ? Image.network( products[index].images![0].src!, width: MediaQuery .of(context) .size .width, height: MediaQuery .of(context) .size .height, fit: BoxFit.cover, ) : Image.asset( "assets/images/1.jpeg", width: MediaQuery .of(context) .size .width, height: MediaQuery .of(context) .size .height, fit: BoxFit.fill, ), Positioned.directional( textDirection: Directionality.of( context), child: Container( margin: EdgeInsets.symmetric( horizontal: 12.0, vertical: 10.0), decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(50.0)), child: IconButton( icon: Icon( Icons.favorite_outline), onPressed: () { //wishListData.getCount(products[index].id!, auth.id!); wishListData.addNewWishList( price: products[index] .price! .toString(), product: products[index] .name!, image: products[index] .images![0] .src!, productId: products[index] .id, userId: auth.id); ScaffoldMessenger.of(context) .showSnackBar( SnackBar( content: Text( S .of(context) .add_product_to_wishlist, ), duration: Duration( seconds: 2), ), ); }, iconSize: 20.0, ), ), ) ], ) ), Padding( padding: const EdgeInsets.all(8.0), child: Text( products[index].name!.substring(0, 10), style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 18.0, ), ), ), Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: const EdgeInsets.all(8.0), child: Text(initModel.currency! + products[index].price!, style: TextStyle( color: Colors.black, fontSize: 15.0, ), ), ), IconButton( icon: Icon( Icons.add_shopping_cart_outlined), onPressed: () { //if(auth.status == Status.Authenticated){ cart.addItem( products[index].id.toString(), 10.0, products[index].name!.toString(), products[index].images![0].src!, 1, products[index].id.toString()); ScaffoldMessenger.of(context) .hideCurrentSnackBar(); ScaffoldMessenger.of(context) .showSnackBar( SnackBar( content: Text( S .of(context) .product_added_to_the_cart, ), duration: Duration(seconds: 2), action: SnackBarAction( label: S .of(context) .undo, onPressed: () { cart.reomveSingleItem( products[index] .id .toString()); }, ), ), ); }, ), ], ), Text(products[index].stockStatus!, style: TextStyle( color: Colors.orange, fontWeight: FontWeight.bold, fontSize: 15, ),), Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ RatingBar.builder( initialRating: products[index] .ratingCount! .toDouble(), minRating: 1, direction: Axis.horizontal, allowHalfRating: true, itemSize: 15.0, itemCount: 5, itemPadding: EdgeInsets.symmetric(horizontal: 4.0), itemBuilder: (context, _) => Icon( Icons.star, color: Colors.amber, ), onRatingUpdate: (rating) { print(rating); }, ), Text(products[index] .ratingCount! .toDouble() .toString()), ], ), ], ), ), ), ), ), ), ); }, ), ), ], ), ); }); } List<Tab> _tabs(List<CategoryModel> categoryModel) { List<Tab> tabs = []; for (CategoryModel category in categoryModel) { tabs.add(Tab( text: category.name, )); } return tabs; } وهذه كامل الملفات , ملف stream_products.dart import 'dart:async'; import '../../common/api_service.dart'; import '../../pages/contracts/contracts.dart'; import '../../models/Product.dart'; class StreamProductsBloc implements Disposable { List<ProductsModel>? products; final StreamController<List<ProductsModel>> _productsController = StreamController<List<ProductsModel>>.broadcast(); final StreamController<int> _categoryController = StreamController<int>.broadcast(); Stream<List<ProductsModel>> get productsStream => _productsController.stream; StreamSink<int> get fetchProducts => _categoryController.sink; Stream<int> get getProductsForCategoriescategory => _categoryController.stream; APIService? apiService; int categoryID = 0; StreamProductsBloc() { //this.categoryID = categoryID; this.products = []; apiService = APIService(); _productsController.add(products!); _categoryController.add(this.categoryID); _categoryController.stream.listen(_fetchCategoriesFromApi); } Future<void> _fetchCategoriesFromApi(int category) async { this.products = await apiService!.getProductsForCategories(category); _productsController.add(this.products!); } @override void dispose(){ _productsController.close(); _categoryController.close(); } } و ملف الواجهة ملف products_screen.dart import 'package:bistore/providers/auth.dart'; import 'package:bistore/services/wishlist_provider.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:provider/provider.dart'; import '../../models/init_model.dart'; import '../../widgets/shimmer/shimmer_grid.dart'; import '../../generated/l10n.dart'; import '../../models/cart_model.dart'; import '../products/single_product.dart'; import '../../models/Product.dart'; import '../../models/category.dart'; import '../../utils/helper_api.dart'; import '../../pages/home/stream_products.dart'; class Products extends StatefulWidget { const Products({Key? key}) : super(key: key); @override _ProductsState createState() => _ProductsState(); } class _ProductsState extends State<Products> with AutomaticKeepAliveClientMixin, TickerProviderStateMixin { @override bool get wantKeepAlive => true; HelperApi helperApi = new HelperApi(); int currentIndex = 0; late TabController _tabController; var refreshKey = GlobalKey<RefreshIndicatorState>(); StreamProductsBloc streamProductsBloc = StreamProductsBloc(); late List<CategoryModel> productsCategories; @override void initState() { // ignore: todo // TODO: implement initState super.initState(); HelperApi().fetchCategories(1); } @override void dispose() { // ignore: todo // TODO: implement dispose super.dispose(); _tabController.dispose(); streamProductsBloc.dispose(); } @override // ignore: must_call_super Widget build(BuildContext context) { return FutureBuilder<List<CategoryModel>>( future: helperApi.fetchCategories(1), builder: (BuildContext context, AsyncSnapshot snapshot) { switch (snapshot.connectionState) { case ConnectionState.none: return Center( child: Text('No Connection'), ); case ConnectionState.waiting: case ConnectionState.active: return Scaffold( body: Center( child: CircularProgressIndicator(), ), ); case ConnectionState.done: if (snapshot.hasError) { print(snapshot.error); } else { if (!snapshot.hasData) { print('Data Not Found'); } else { this.productsCategories = snapshot.data; streamProductsBloc.fetchProducts .add(this.productsCategories[0].id!); return _categories(snapshot.data, context); } } break; } return Container(); }, ); } Widget _categories(List<CategoryModel> categoryModel, BuildContext context) { _tabController = TabController( initialIndex: 0, length: categoryModel.length, vsync: this, ); return Scaffold( appBar: AppBar( title: Text(S.of(context).products), bottom: TabBar( indicatorColor: Color(0XFF117182), indicatorWeight: 5, controller: _tabController, tabs: _tabs(categoryModel), isScrollable: true, onTap: (int index) { streamProductsBloc.fetchProducts .add(this.productsCategories[index].id!); }, ), ), body: Container( child: StreamBuilder( stream: streamProductsBloc.productsStream, builder: (context, AsyncSnapshot<List<ProductsModel>>? snapshot) { switch (snapshot!.connectionState) { case ConnectionState.none: // ignore: todo // TODO: Handle this case. return Center( child: CircularProgressIndicator(), ); case ConnectionState.waiting: // ignore: todo // TODO: Handle this case. return ShimmerGrid(); case ConnectionState.done: case ConnectionState.active: // ignore: todo // TODO: Handle this case. if (snapshot.hasError) { return Center( child: ShimmerGrid(), ); } else { if (!snapshot.hasData) { return ShimmerGrid(); } else { return _drawProducts(snapshot.data!, context); } } } }, ), ), ); } Widget _drawProducts(List<ProductsModel> products, BuildContext context) { final cart = Provider.of<Cart>(context, listen: false); var initModel = Provider.of<InitModel>(context, listen: false,); var auth = Provider.of<AuthProvider>(context, listen: false); return Consumer<WishListProvider>( builder: (BuildContext context, wishListData, child) { return Container( padding: EdgeInsets.only(top: 24), child: Column( children: [ Flexible( child: GridView.builder( gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( maxCrossAxisExtent: 200, childAspectRatio: 1 / 2, crossAxisSpacing: 20, mainAxisSpacing: 20), itemCount: products.length, itemBuilder: (context, index) { return Card( clipBehavior: Clip.hardEdge, elevation: 0.9, child: AnimationConfiguration.staggeredList( position: index, duration: const Duration(milliseconds: 375), child: SlideAnimation( verticalOffset: 50.0, child: FadeInAnimation( child: Padding( padding: const EdgeInsets.all(8.0), child: InkWell( onTap: () { _gotoSingleProduct(products[index], context); }, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Expanded( // ignore: unnecessary_null_comparison child: Stack( children: [ (products[index].images![0].src != null) ? Image.network( products[index].images![0].src!, width: MediaQuery .of(context) .size .width, height: MediaQuery .of(context) .size .height, fit: BoxFit.cover, ) : Image.asset( "assets/images/1.jpeg", width: MediaQuery .of(context) .size .width, height: MediaQuery .of(context) .size .height, fit: BoxFit.fill, ), Positioned.directional( textDirection: Directionality.of( context), child: Container( margin: EdgeInsets.symmetric( horizontal: 12.0, vertical: 10.0), decoration: BoxDecoration( color: Colors.grey[200], borderRadius: BorderRadius.circular(50.0)), child: IconButton( icon: Icon( Icons.favorite_outline), onPressed: () { //wishListData.getCount(products[index].id!, auth.id!); wishListData.addNewWishList( price: products[index] .price! .toString(), product: products[index] .name!, image: products[index] .images![0] .src!, productId: products[index] .id, userId: auth.id); ScaffoldMessenger.of(context) .showSnackBar( SnackBar( content: Text( S .of(context) .add_product_to_wishlist, ), duration: Duration( seconds: 2), ), ); }, iconSize: 20.0, ), ), ) ], ) ), Padding( padding: const EdgeInsets.all(8.0), child: Text( products[index].name!.substring(0, 10), style: TextStyle( color: Colors.black, fontWeight: FontWeight.bold, fontSize: 18.0, ), ), ), Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: const EdgeInsets.all(8.0), child: Text(initModel.currency! + products[index].price!, style: TextStyle( color: Colors.black, fontSize: 15.0, ), ), ), IconButton( icon: Icon( Icons.add_shopping_cart_outlined), onPressed: () { //if(auth.status == Status.Authenticated){ cart.addItem( products[index].id.toString(), 10.0, products[index].name!.toString(), products[index].images![0].src!, 1, products[index].id.toString()); ScaffoldMessenger.of(context) .hideCurrentSnackBar(); ScaffoldMessenger.of(context) .showSnackBar( SnackBar( content: Text( S .of(context) .product_added_to_the_cart, ), duration: Duration(seconds: 2), action: SnackBarAction( label: S .of(context) .undo, onPressed: () { cart.reomveSingleItem( products[index] .id .toString()); }, ), ), ); }, ), ], ), Text(products[index].stockStatus!, style: TextStyle( color: Colors.orange, fontWeight: FontWeight.bold, fontSize: 15, ),), Row( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start, children: [ RatingBar.builder( initialRating: products[index] .ratingCount! .toDouble(), minRating: 1, direction: Axis.horizontal, allowHalfRating: true, itemSize: 15.0, itemCount: 5, itemPadding: EdgeInsets.symmetric(horizontal: 4.0), itemBuilder: (context, _) => Icon( Icons.star, color: Colors.amber, ), onRatingUpdate: (rating) { print(rating); }, ), Text(products[index] .ratingCount! .toDouble() .toString()), ], ), ], ), ), ), ), ), ), ); }, ), ), ], ), ); }); } List<Tab> _tabs(List<CategoryModel> categoryModel) { List<Tab> tabs = []; for (CategoryModel category in categoryModel) { tabs.add(Tab( text: category.name, )); } return tabs; } void _gotoSingleProduct( ProductsModel singleProductModel, BuildContext context) { Navigator.push( context, MaterialPageRoute( builder: (context) => SingleProduct(singleProductModel)), ); } } class ProductsList extends StatelessWidget { final List<ProductsModel> productsModel; ProductsList({Key? key, required this.productsModel}) : super(key: key); @override Widget build(BuildContext context) { return Column( children: [ Expanded( child: ListView.builder( scrollDirection: Axis.vertical, itemCount: productsModel.length, itemBuilder: (context, index) { return Text('${productsModel[index].name}'); }, ), ), ], ); } }
  21. يجب إرجاع جميع الأقسام بما فيها محتواها و من ثم عرضها في listView و لكن هذا سيسبب عندك تكدس المحتوى في الواجهة. هل يمكنك إرفاق كود PHP الذي يقوم بإرجاع الأقسام و بيانات كل قسم؟
  22. المشكلة تكمن بأنك غير مفعل أو مثبت haxm أو أن الذاكرة الأفتراضية للمحاكي عالية جداً مما يسبب هذه المشكلة يجب تثبيت haxm فستجد البرنامج في المسار التالي Sdk\extras\intel\Hardware_Accelerated_Execution_Manager ستجد برنامج hxam قم بتنصيبه ومن ثم إعادة تشغيل الحاسوب, ايضا حاول تثبيت محاكي يعمل على x86 أفضل بكثير من أي محاكي أخر .
  23. لاحظ أن السطر التالي <link rel="stylesheet" href="style.css"> هو السطر المسؤول عن جلب ملف التنسيقات و كل شيء جيد ما عدا أنه مسار الملف style.css غير صحيح لأنه بداخل مجلد css فيجب وضع المسار الصحيح للملف بهذا الشكل <link rel="stylesheet" href="css/style.css">
  24. هنا الخطأ يخبرك أنه حقل id يجب أن يحتوي على قيمة وبذلك لا يمكن ان يكون فارغ و يجب أن يكون حقل id عبارة عن Auto Increment بحيث يتم تعبئته تلقائياً , حاول رجاء تغيير الحقل id و جعله auto incement.
  25. يمكنك فتح المسار التالي storage/logs/ ثم فتح الملف بداخل هذا المسار و قراءة ما هي الأخطاء التي تم التعرف عليها و يمكنك حلها , أو مشاركتها معنا , أو أيضا يمكنك فتح ملف error.log على الاستضافة الخاصة بك و قراءة الأخطاء و العمل على حلها. لأنه خطأ 500 Internal Server Error يكون خطأ داخلي ولا يمكن تحديده إلا بالرجوع إلى ملفات logs.
×
×
  • أضف...