في هذا المقال ستتعلم كيفية تخزين البيانات في قاعدة الفايربيز السحابية Cloud Firestore ثم استعراضهم داخل تطبيق فلاتر , اي استخدام قاعدة فايربيز كمستودع Backend لبياناتك الخاصة ,يتميز هذا التطبيق بتصميم جذاب و يتكون من واجهة مستخدم رئسية تتضمن ستة انواع من بيانات وعند الضغط عليها تنتقل الى واجهة مستخدم متفرعة تتضمن البيانات المتفرعة داخلها , الكود سورس وقاعدة البيانات متوفرة , سيتم تقسيم الشرح الى عدة اقسام .
- انشاء حساب في Firebase اذا لم يكن لديك حساب مسبق
- انشاء تطبيق فلاتر جديد
- متطلبات التطبيق
- اعدادات ربط تطبيقك مع Firebase
- اضافة الحزم المطلوبة الى ملف pubcpec.yaml
- تسمية الملفات الخاصة بالتطبيق
- انشاء قاعدة البيانات على Cloud Firestore
- كتابة الاكواد
1- انشاء حساب في Firebase اذا لم يكن لديك حساب مسبق
2- إنشاء مشروع Flutter جديد خاص بك وتهيئة ملفات التطبيق [الفيديو]
- افتح برنامج اندرويد استوديو
- اضغط على New flutter project .
- حدد تطبيق Flutter في النافذة التي تظهر بعد ذلك ثم اضغط Next .
- سمي تطبيقك الاسم الذي تريده . قد تضطر أيضًا إلى تحديد المسار إلى SDK اذا لم يكن محدد .
- أدخل اسم الحزمة com.your name.app name .
- اختر اللغة البرمجية لتطبيق [ kotlin - Swift ]فقط.
- اختر منصة التطبيق [ Android - IOS ] فقط , ثم اضغط Finish ,وانتظر جتى يقوم التطبيق بانهاء اعداد التطبيق.
- قم باعداد مكونات التطبيق.
- شغل التطبيق على محاكي الاندرويد او على هاتفك الخاص,للتاكيد ان كل شي جاهز.
- انتقل لملف build.gradle في ملف android وتاكد من رقم targetSdkVersion ثم قم بضبط ال sdk بالضغط على File في اعلى الشاشة لليمين في تطبيق الاندرويد استوديو ثم انتقل الى Project Structure ستظهر لك شاشة Project Setting قم ب اختيار قسم Project ثم عدل اصدار ال Project sdk نفس رقم targetSdkVersion , ثم انتقل الى قسم Model في يمين الشاشة ستظهر شاشة جديدة قم بالانتقل الى Dependencies في الخيارات بالاعلى ثم عدل Mpdle sdk الى نفس رقم targetSdkVersion , ثم انتقل الى قسم Problems اسفل الشاشة Project Setting وقم بحل المشاكل اذا ظهرت بالضغط على fix جوار كل مشكلة.
3- متطلبات إنشاء التطبيق
ضروريات :
- Flutter environment sdk >=2.7.0 <3.0.0
- minSdkVersion 21
- targetSdkVersion 30 is favorite
- اضافة اذن استخدام الانترنت الى ملف AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
4- اعدادات ربط تطبيقك مع Firebase [شرح الموضوع]
- في احد الخطوات في شرح الفيديو لاتقم بتحديد [ Enable Google Analytics for this project]. قم بتخطي هذا الخطوة
- بعد الضغط على انشاء قاعدة Cloud Firestore قم بالضغط على Rules ثم قم بتعديل السطر كما في الصورة بالاسفل:
allow read, write;
اضغط على الفيديو لمشاهد الشرح :
5-اضافة الحزم المطلوبة الى ملف pubcpec.yaml
cloud_firestore: ^0.14.0+2
provider: ^4.3.2+1
firebase_core: ^0.5.0
url_launcher: ^5.4.2
6- تسمية الملفات الخاصة بالتطبيق
7- انشاء قاعدة البيانات على Cloud Firestore
عادةً ما تحتوي هذه المستندات على مفتاح فريد تم إنشاؤه (يُعرف أيضًا باسم document Id ) في قاعدة البيانات ، بحيث يقوم بتخزين البيانات في حقول fields على شكل ازواج من مفتاح key و قيمة value بالاضافة الى اسم القيمة [ References ] الذي سيتم استخدامه في بناء كلاس ال model .
8- كتابة الاكواد البرمجية
ستجد شرح كل كود في تعليق في الاعلاء محصور بين هذه العلامة /// /// ...... /// ///
انتقل الى التطبيق الخاص بك ثم انتقل الى ملف main.dart قم بحذف جميع الاكواد السابقة ثم الصق هذا الكود.
هذا الملف يتضمن:
- كود لتهيئة وتاكيد الربط بين منصة فايربيز مع تطبيق فلاتر
- اضافة ChangeNotifierProvider والهدف منه انه يضيف إمكانية الاستماع إلى التغييرات في قيم الProvider ويقوم تلقائيًا بإعادة إنشاء عناصر واجهة المستخدم عندما يحصل اي تغير في هذه القيم .
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:firebase_core/firebase_core.dart';
import 'home_screen.dart';
import 'my_providers.dart';
void main() async {
/// TO Initialize the Firebase for ensure binding between flutter widget layer and Firebase plugin /// ///
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
/// ChangeNotifierProvider use to adds the possibility to listen to changes in the provider value and it automatically re-builds the widgets when the provider needs to.
/// return to display the latest value:
return ChangeNotifierProvider<MyProviders>(
create: (context) => MyProviders(),
child: MaterialApp(
theme: ThemeData(
primaryColor: Color(0xff746bc9),
iconTheme: IconThemeData(color: Colors.black),
),
debugShowCheckedModeBanner: false,
home: HomeScreen(),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'home_model.dart';
import 'colors.dart';
import 'category_model.dart';
import 'list_page.dart';
import 'my_providers.dart';
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
/// to Define Provider class ///
MyProviders myProviders;
/// /// ui of every image and name that come from firebase /// ///
Widget _buildCategoryProduct({String image, String name}) {
return Padding(
padding: const EdgeInsets.only(
top: 20,
),
child: Column(
children: [
Container(
margin: EdgeInsets.only(
left: 28,
),
padding: EdgeInsets.all(25),
width: 150,
height: 125,
decoration: BoxDecoration(
shape: BoxShape.rectangle,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
lightPrimary,
darkPrimary,
],
),
borderRadius: BorderRadius.circular(25),
boxShadow: [
BoxShadow(
offset: Offset(5, 5),
spreadRadius: 1,
blurRadius: 10,
color: darkShadow,
),
]),
child: Image.network(image),
),
SizedBox(
height: 10,
),
Container(
padding: EdgeInsets.only(left: 30),
child: Text(
name,
// textAlign: TextAlign.right,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
),
],
),
);
}
/// code to get data of sport home [ image and name ] and category content inside list [ image & name & web ] from Provider class to home screen ///
Widget _buildSportIcon() {
/// to call model of home data and category content
List<HomeModel> sportIcon = myProviders.getSportIcon;
List<CategoryModel> sport = myProviders.getSportList;
return Row(
children: sportIcon.map((e) {
/// code to control where to navigation ///
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (ctx) => ListPage(
name: "Sport",
snapShot: sport,
),
),
);
},
/// To call home data widget and show them ///
child: _buildCategoryProduct(image: e.image, name: e.name),
);
}).toList());
}
Widget _buildNewsIcon() {
List<HomeModel> newsIcon = myProviders.getNewsIcon;
List<CategoryModel> news = myProviders.getNewsList;
return Row(
children: newsIcon.map((e) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (ctx) => ListPage(
name: "News",
snapShot: news,
),
),
);
},
child: _buildCategoryProduct(image: e.image, name: e.name),
);
}).toList());
}
Widget _buildTechnicalIcon() {
List<HomeModel> technicalIcon = myProviders.getTechnicalIcon;
List<CategoryModel> technical = myProviders.getTechnicalList;
return Row(
children: technicalIcon.map((e) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (ctx) => ListPage(
name: "Technical",
snapShot: technical,
),
),
);
},
child: _buildCategoryProduct(image: e.image, name: e.name),
);
}).toList());
}
Widget _buildFashionIcon() {
List<HomeModel> fashionIcon = myProviders.getFashionIcon;
List<CategoryModel> fashion = myProviders.getFashionList;
return Row(
children: fashionIcon.map((e) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (ctx) => ListPage(
name: "Fashion",
snapShot: fashion,
),
),
);
},
child: _buildCategoryProduct(image: e.image, name: e.name),
);
}).toList());
}
Widget _buildEducationIcon() {
List<HomeModel> educationIcon = myProviders.getEducationIcon;
List<CategoryModel> education = myProviders.getEducationList;
return Row(
children: educationIcon.map((e) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (ctx) => ListPage(
name: "Education",
snapShot: education,
),
),
);
},
child: _buildCategoryProduct(image: e.image, name: e.name),
);
}).toList());
}
Widget _buildMedicalIcon() {
List<HomeModel> medicalIcon = myProviders.getMedicalIcon;
List<CategoryModel> medical = myProviders.getMedicalList;
return Row(
children: medicalIcon.map((e) {
return GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (ctx) => ListPage(
name: "Medical",
snapShot: medical,
),
),
);
},
child: _buildCategoryProduct(image: e.image, name: e.name),
);
}).toList());
}
/// to call all functions with it's parameters ///
void getCallAllFunction() {
myProviders.getNewsIconData();
myProviders.getNewsData();
myProviders.getSportIconData();
myProviders.getSportData();
myProviders.getMedicalIconData();
myProviders.getMedicalData();
myProviders.getFashionIconData();
myProviders.getFashionData();
myProviders.getEducationIconData();
myProviders.getEducationData();
myProviders.getTechnicalIconData();
myProviders.getTechnicalData();
}
@override
Widget build(BuildContext context) {
/// to call Provider value
myProviders = Provider.of<MyProviders>(context);
/// to pass and return all functions with it's parameters in the class ///
getCallAllFunction();
return Container(
/// ui of AppBar
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
lightPrimary,
darkPrimary,
])),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
actions: [
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: Icon(
Icons.menu,
color: Colors.black,
),
)
],
title: Text(
"Home",
style: TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
),
backgroundColor: Colors.transparent,
elevation: 0.0,
),
/// to show the data home in home screen as grid view ///
body: GridView.count(
crossAxisCount: 2,
children: [
_buildSportIcon(),
_buildNewsIcon(),
_buildFashionIcon(),
_buildEducationIcon(),
_buildMedicalIcon(),
_buildTechnicalIcon(),
],
),
),
);
}
}
import 'package:flutter/material.dart'; /// colors of app /// const Color lightPrimary = Colors.tealAccent; const Color darkPrimary = Colors.teal; const Color black = Colors.black; const Color darkShadow = Colors.black; const Color lightShadow = Colors.black;
import 'package:aboody/list_contene.dart';
import 'package:flutter/material.dart';
import 'colors.dart';
import 'category_model.dart';
class ListPage extends StatelessWidget {
/// to Define variables for name of category and snapShot data that come with it ///
final String name;
final List<CategoryModel> snapShot;
ListPage({
this.name,
this.snapShot,
});
@override
Widget build(BuildContext context) {
/// ui of all lists ///
return Padding(
padding: const EdgeInsets.all(2.0),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
lightPrimary,
darkPrimary,
]),
borderRadius: BorderRadius.circular(20),
),
child: Scaffold(
backgroundColor: Colors.transparent,
appBar: AppBar(
centerTitle: true,
/// name of category ///
title: Text(
name,
style:
TextStyle(color: Colors.black, fontWeight: FontWeight.bold),
),
backgroundColor: (lightPrimary),
),
body: Container(
padding: EdgeInsets.all(20),
/// ui to show snapShot data in grid view as lists ///
child: GridView.count(
scrollDirection: Axis.vertical,
shrinkWrap: true,
crossAxisCount: 1,
childAspectRatio: 4,
mainAxisSpacing: 8,
children: snapShot
.map(
(e) => GestureDetector(
onTap: () {},
child: ListContent(
image: e.image,
name: e.name,
web: e.web,
),
),
)
.toList(),
),
),
),
),
);
}
}
import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:url_launcher/url_launcher.dart'; import 'colors.dart'; class ListContent extends StatelessWidget { /// to Define variables from snapShot data /// final String image; final String web; final String name; ListContent({ this.image, this.name, this.web, }); /// to allow Url launcher /// Future<void> _launchInApp(String url) async { if (await canLaunch(url)) { await launch( url, /// to allow Url launcher inside app /// forceWebView: true, headers: <String, String>{'my_header_key': 'my_header_value'}, ); } else { throw 'Could not launch $url'; } } @override Widget build(BuildContext context) { /// ui of every content inside list /// return Padding( padding: const EdgeInsets.all(8.0), child: Container( padding: EdgeInsets.only(left: 20), decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ lightPrimary, darkPrimary, ]), borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( offset: Offset(3, 4), spreadRadius: 1, blurRadius: 4, color: darkShadow, ), ]), child: Scaffold( backgroundColor: Colors.transparent, body: Row( children: <Widget>[ /// A constant that is true if the application was compiled to run on the web. kIsWeb ? Container( height: 75, width: 60, decoration: BoxDecoration( /// ui to show image inside list /// image: DecorationImage( fit: BoxFit.contain, image: NetworkImage(image), ), ), ) : Container( height: 75, width: 60, decoration: BoxDecoration( image: DecorationImage( fit: BoxFit.contain, image: NetworkImage(image), ), ), ), Expanded( child: Container( alignment: Alignment.center, /// name of category content in the list /// child: Text( name, style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), ), ), /// ui of icon inside list /// IconButton( /// code to launch the url after click iconButton onPressed: () { _launchInApp(web); }, icon: Icon(Icons.touch_app), ), ], ), ), ), ); } }
import 'package:flutter/material.dart'; /// class of Define variables of home data in firebase collection /// class HomeModel { final String image; final String name; HomeModel({@required this.image, @required this.name}); }
import 'package:flutter/material.dart'; /// class of Define variables of category content data in firebase collection /// class CategoryModel { final String name; final String image; final String web; CategoryModel({ @required this.image, @required this.name, @required this.web, }); }
import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter/material.dart'; import 'home_model.dart'; import 'category_model.dart'; /// Provider class is allow to expose a value,create/listen/dispose it that come from firebase database. class MyProviders with ChangeNotifier { /// /// to fetch all data home [image & name] from Cloud Firestore /// /// // to fetch data of sport home [image & name ] // List<HomeModel> sportIcon = []; HomeModel sporticonData; Future<void> getSportIconData() async { List<HomeModel> newList = []; QuerySnapshot sportSnapShot = await FirebaseFirestore.instance.collection("sport").get(); sportSnapShot.docs.forEach( (element) { sporticonData = HomeModel( image: element.data()["image"], name: element.data()["name"]); newList.add(sporticonData); }, ); sportIcon = newList; notifyListeners(); } List<HomeModel> get getSportIcon { return sportIcon; } // to fetch data of news home [image & name ] // List<HomeModel> newsIcon = []; HomeModel newsiconData; Future<void> getNewsIconData() async { List<HomeModel> newList = []; QuerySnapshot newsSnapShot = await FirebaseFirestore.instance.collection("news").get(); newsSnapShot.docs.forEach( (element) { newsiconData = HomeModel( image: element.data()["image"], name: element.data()["name"]); newList.add(newsiconData); }, ); newsIcon = newList; /// This call tells the widgets that are listening to this model to rebuild. /// notifyListeners(); } List<HomeModel> get getNewsIcon { return newsIcon; } // to fetch data of education home [image & name ] // List<HomeModel> educationIcon = []; HomeModel educationiconData; Future<void> getEducationIconData() async { List<HomeModel> newList = []; QuerySnapshot educationSnapShot = await FirebaseFirestore.instance.collection("education").get(); educationSnapShot.docs.forEach( (element) { educationiconData = HomeModel( image: element.data()["image"], name: element.data()["name"]); newList.add(educationiconData); }, ); educationIcon = newList; notifyListeners(); } List<HomeModel> get getEducationIcon { return educationIcon; } // to fetch data of fashion home [image & name ] // List<HomeModel> fashionIcon = []; HomeModel fashionIconData; Future<void> getFashionIconData() async { List<HomeModel> newList = []; QuerySnapshot fashionSnapShot = await FirebaseFirestore.instance.collection("fashion").get(); fashionSnapShot.docs.forEach( (element) { fashionIconData = HomeModel( image: element.data()["image"], name: element.data()["name"]); newList.add(fashionIconData); }, ); fashionIcon = newList; notifyListeners(); } List<HomeModel> get getFashionIcon { return fashionIcon; } // to fetch data of medical home [image & name ] // List<HomeModel> medicalIcon = []; HomeModel medicaliconData; Future<void> getMedicalIconData() async { List<HomeModel> newList = []; QuerySnapshot medicalSnapShot = await FirebaseFirestore.instance.collection("medical").get(); medicalSnapShot.docs.forEach( (element) { medicaliconData = HomeModel( image: element.data()["image"], name: element.data()["name"]); newList.add(medicaliconData); }, ); medicalIcon = newList; notifyListeners(); } List<HomeModel> get getMedicalIcon { return medicalIcon; } // to fetch data of technical home [image & name ] // List<HomeModel> technicalIcon = []; HomeModel technicaliconData; Future<void> getTechnicalIconData() async { List<HomeModel> newList = []; QuerySnapshot technicalSnapShot = await FirebaseFirestore.instance.collection("technical").get(); technicalSnapShot.docs.forEach( (element) { technicaliconData = HomeModel( image: element.data()["image"], name: element.data()["name"]); newList.add(technicaliconData); }, ); technicalIcon = newList; notifyListeners(); } List<HomeModel> get getTechnicalIcon { return technicalIcon; } /// /// to fetch all data in category content [image & name and web ] from Cloud Firestore /// /// // to fetch data of medical content [image & name and web ] // List<CategoryModel> medical = []; CategoryModel medicalData; Future<void> getMedicalData() async { List<CategoryModel> newList = []; QuerySnapshot medicalSnapShot = await FirebaseFirestore.instance .collection("categorycontent") .doc("2IicqBs0kMR6J4T9PgoD") .collection("midecal") .get(); medicalSnapShot.docs.forEach( (element) { medicalData = CategoryModel( image: element.data()["image"], name: element.data()["name"], web: element.data()["web"], ); newList.add(medicalData); }, ); medical = newList; notifyListeners(); } List<CategoryModel> get getMedicalList { return medical; } // to fetch data of sport content [image & name and web ] // List<CategoryModel> sport = []; CategoryModel sportData; Future<void> getSportData() async { List<CategoryModel> newList = []; QuerySnapshot sportSnapShot = await FirebaseFirestore.instance .collection("categorycontent") .doc("2IicqBs0kMR6J4T9PgoD") .collection("sport") .get(); sportSnapShot.docs.forEach( (element) { sportData = CategoryModel( image: element.data()["image"], name: element.data()["name"], web: element.data()["web"], ); newList.add(sportData); }, ); sport = newList; notifyListeners(); } List<CategoryModel> get getSportList { return sport; } // to fetch data of news content [image & name and web ] // List<CategoryModel> news = []; CategoryModel newsData; Future<void> getNewsData() async { List<CategoryModel> newList = []; QuerySnapshot newsSnapShot = await FirebaseFirestore.instance .collection("categorycontent") .doc("2IicqBs0kMR6J4T9PgoD") .collection("news") .get(); newsSnapShot.docs.forEach( (element) { newsData = CategoryModel( image: element.data()["image"], name: element.data()["name"], web: element.data()["web"], ); newList.add(newsData); }, ); news = newList; notifyListeners(); } List<CategoryModel> get getNewsList { return news; } // to fetch data of fashion content [image & name and web ] // List<CategoryModel> fashion = []; CategoryModel fashionData; Future<void> getFashionData() async { List<CategoryModel> newList = []; QuerySnapshot fashionSnapShot = await FirebaseFirestore.instance .collection("categorycontent") .doc("2IicqBs0kMR6J4T9PgoD") .collection("fashion") .get(); fashionSnapShot.docs.forEach( (element) { fashionData = CategoryModel( image: element.data()["image"], name: element.data()["name"], web: element.data()["web"], ); newList.add(fashionData); }, ); fashion = newList; notifyListeners(); } List<CategoryModel> get getFashionList { return fashion; } // to fetch data of education content [image & name and web ] // List<CategoryModel> education = []; CategoryModel educationData; Future<void> getEducationData() async { List<CategoryModel> newList = []; QuerySnapshot educationSnapShot = await FirebaseFirestore.instance .collection("categorycontent") .doc("2IicqBs0kMR6J4T9PgoD") .collection("education") .get(); educationSnapShot.docs.forEach( (element) { educationData = CategoryModel( image: element.data()["image"], name: element.data()["name"], web: element.data()["web"], ); newList.add(educationData); }, ); education = newList; notifyListeners(); } List<CategoryModel> get getEducationList { return education; } // to fetch data of technical content [image & name and web ] // List<CategoryModel> technical = []; CategoryModel technicalData; Future<void> getTechnicalData() async { List<CategoryModel> newList = []; QuerySnapshot technicalSnapShot = await FirebaseFirestore.instance .collection("categorycontent") .doc("2IicqBs0kMR6J4T9PgoD") .collection("technical") .get(); technicalSnapShot.docs.forEach( (element) { technicalData = CategoryModel( image: element.data()["image"], name: element.data()["name"], web: element.data()["web"], ); newList.add(technicalData); }, ); technical = newList; notifyListeners(); } List<CategoryModel> get getTechnicalList { return technical; } }
في حال واجهتك اي مشكلة لاتتردد في كتابة في تعليق ^_^
GooD LooK FoR All
مقالات متعلقة :
في هذا المقال ستتعلم كيفية اضافة ايقونة البحث والبحث عن بياناتك المخزنة في الفايربيز لتطبيق فلاتر
في هذه المقالة ، ستتعلم اضافة Cloud Firestore مع Flutter وانشاء تطبيق من خلالة ستفهم عمليات التعامل مع البيانات وتخزينها في قاعدة الفايربيز وهذا النوع من التطبيقات يسمى CRUD operations وتعني هذه الاحرف المختصره (create, read, update and delete) , من خلال هذه العمليات الاربع سيصبح عندك القدرة في التحكم في تخزين البيانات وادارتها بشكل صحيح.
ستتعلم في هذا المقال كيفية ربط تطبيق فلاتر مع منصة فايربيز بعدة خطوات وهي:
انشاء حساب في Firebase - انشاء مشروع Firebase - اضافة Firebase الى Android - اضافة ملف Firebase الى Android - اضافة Firebase الى مشروعك Flutter - انشاء Cloud Firestore لتخزين بياناتك
تعليقات
إرسال تعليق