0% found this document useful (0 votes)
486 views113 pages

Flutter - المهندس فيصل الاسود

Uploaded by

Bishoy Hanna
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
486 views113 pages

Flutter - المهندس فيصل الاسود

Uploaded by

Bishoy Hanna
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 113

3

38
‫البرمجة المتقدمة في دارت‬
Advanced Programming Dart

‫تأليف فيصل الأسود | مهندس برمجيات‬


You never lose a dream , it‫على‬
Youtube ‫قناة الـ‬ just
‫الكتاب‬incubates as ‫الحصول‬
‫على شرح لكامل محتويات‬ a hobby
‫يمكنك‬

Larry Page
StackOverFlow Arabi
‫‪39‬‬
‫البرمجة المتقدمة في دارت‬
‫‪ -1‬الأصناف‪:‬‬

‫دارت هي لغة كائنية تتعامل مع الأصناف والعمليات عليها‪.‬‬


‫كل كائن هو نسخة من صنف أساسي‪.‬‬
‫لنبسط الامر الصنف هو عبارة عن نوع جديد أريد أنا إنشاءه وبعد الانتهاء من بناءه آخذ منه نسخ‬
‫تسمى كائنات‪.‬‬
‫مثال‪ :‬لنفرض أننا أردنا إنشاء صنف لنقطة ونعلم أن هذه النقطة تتكون من احداثيات ‪ x,y‬وهنا لابد‬
‫أن نذكر أن الكلاس يتكون من خصائص أفعال (طرق) وباني‪.‬‬

‫صنف نقطة هو‪:‬‬


‫{ ‪class Point‬‬
‫;‪num x‬‬
‫;‪num y‬‬
‫)‪point (num x, num y‬‬
‫{‬
‫;‪this.x = x‬‬
‫;‪this.y = y‬‬
‫}‬
‫)(‪void printXAndY‬‬
‫{‬
‫;)‪print(x‬‬
‫;)‪print(y‬‬
‫}‬
‫}‬

‫بعد أن شكلنا الصنف يمكن استخدامه عدد لا نهائي من المرات بتشكيل كائن منه كما يلي‪:‬‬
‫;)‪Point p1 = new Point(5,3‬‬
‫وهنا أنشأنا كائن ‪ p1‬من نوع ‪ Point‬وله القيمة ‪x=5, y=3‬‬
‫وفي حال كتبنا ‪:‬‬
‫;)(‪p1.printXAndY‬‬

‫سوف يقوم بطباعة القيم كما يلي‪:‬‬


‫‪5‬‬
‫‪6‬‬
‫في ‪ dart‬يمكن كتابة الباني بالشكل المختصر‪:‬‬
‫‪Point (this.x, this.y):‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪40‬‬
‫أي القيمة الأولى الواردة عند انشاء الكائن هي نفسها قيمة ‪ x‬للصنف ‪ point‬والقيمة الثانية هي‬
‫قيمة ‪ y‬للصنف‪.‬‬

‫كما رأينا يستدعي الفعل (الطريقة) في ‪ dart‬عن طريق اسم الكائن ‪ p1‬ثم اسم الطريقة المطلوبة‪.‬‬
‫مثال‪ :‬ننشأ صنف لسيارة لها (سرعة – لون) وعند إعطائها مساحة تقوم بتقدير الزمن لك عن طريق‬
‫فعل خاص لها‪.‬‬
‫{ ‪class Car‬‬
‫;‪num speed‬‬
‫;‪String color‬‬
‫;)‪car (this.speed, this.color‬‬
‫)‪double giveMeTime(int distance‬‬
‫{‬
‫;‪return speed/distance‬‬
‫}‬
‫}‬

‫وفي البرنامج الرئيسي ننشأ سيارتين‬


‫;)'‪Car c1 = new Car(50, 'Red‬‬
‫;)‪var z1 = c1.giveMeTime(25‬‬
‫;)‪print (z1‬‬
‫;)'‪Car c2 = new Car(30, 'Green‬‬
‫;‪c2.speed = 20‬‬
‫;)‪var z2 = c2.giveMeTime(20‬‬
‫;)‪print (z2‬‬

‫أنشأنا كائنين لسيارتين الأولى بسرعة ‪ 51‬ولون أحمر والثانية بسرعة ‪ 31‬ولون أخضر غيرنا سرعة السيارة‬
‫الثانية لـ ‪11‬‬

‫الخرج سيكون‬
‫‪2‬‬
‫‪1‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪41‬‬
‫التعليمة ‪ return‬مهمتها إرجاع قيمة من الطريقة إلى التابع‪ .‬كما رأينا عندما طلبت ‪ c1‬الزمن‬
‫بطريقة )(‪ giveMeTime‬تم ارجاع القيمة للبرنامج الرئيسي عن طريق ‪ return‬وهناك بعض‬
‫الطريق لا ترجع شيء‪.‬‬

‫‪ -2‬معرفات الوصول‪:‬‬

‫على خلاف الكثير من لغات البرمجة ‪ dart‬لا تحوي معرفات وصول الكلاسيكية انما تحوي فقط‬
‫معرف الخصوصية ‪ private‬على مستوى الحزمة ككل‪.‬‬
‫أي إذا أردنا إنشاء متحول ضمن صنف ‪ class‬وهذا المتحول لا يوجد أي كلاس اخر يمكنه معرفة‬
‫قيمته نكتب المتحول بالشكل‪:‬‬
‫;‪var _ x = 5‬‬

‫بحيث نضع قبل اسمه _ وهذا الكلام ينطبق أيضا على التوابع والطرق بحيث لا استطيع استدعاء أي‬
‫طريقة من خازنة المكتبة في حال بدأت بالمعرف _ مثلا‬
‫)( ‪int _giveMeTime‬‬

‫‪ -3‬المتحول الستاتيكي‪:‬‬

‫الكثير يعاني من فهم المتحول الستاتيكي انما هو يحمل مفهوما بسيطا جدا‪ .‬لنعود لمثالنا السابق‬
‫بخصوص إنشاء صنف سيارة ونفرض أننا عرفنا متحول السرعة بشكل ‪ static‬كما يلي‪:‬‬
‫{ ‪class car‬‬
‫;‪static num speed‬‬
‫;‪String color‬‬
‫;)‪car (this.speed, this.color‬‬
‫)‪double giveMeTime (int distance‬‬
‫{‬
‫;‪return speed/distance‬‬
‫}‬
‫}‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪42‬‬
‫هنا يكون شكل الكائنات عند إنشاء سيارتين كما يلي‪:‬‬

‫فنلاحظ أنه عند تعريف متحول ستاتيكي يصبح قيمة نفسها لكل الكائنات بحيث إذا عدلتها لأي‬
‫كائن (سيارة) سوف تتعدل للجميع‪.‬‬
‫طبعا يوجد أيضا التوابع الستاتيكية ويجب أن نعلم أن التابع الستاتيكي يتعامل حصرا مع متحول‬
‫ستاتيكي‪.‬‬

‫‪ -4‬الوراثة‪:‬‬

‫الوراثة مفهوم بسيط يساعدني على بناء الأصناف بالاعتماد على أصناف سابقة ويريحني من عناء‬
‫صناعتها من جديد‪.‬‬
‫لنفرض أريد إنشاء صنف سيارة لديها (سرعة‪ ,‬لون‪ ,‬وزن) وتابع ليعطيني الوقت كما في المثال السابق‬
‫هنا نلاحظ أنه لدينا الصنف السيارة القديم فيه كل من السرعة واللون والتابع المطلوب ولكننا بحاجة‬
‫لإضافة خاصة الوزن‪ .‬فهنا نستخدم الوراثة ويكون الصنف الجديد كما في الشكل‪:‬‬
‫{ ‪class car2 extends car‬‬
‫‪car2(num speed, String color) :‬‬
‫;)‪super(speed, color‬‬
‫;‪var weight‬‬
‫}‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪43‬‬
‫أي صنف السيارة ‪ Car2‬يرث ‪ extends‬من صنف السيارة ‪ Car‬ويأخذ جميع خصائصها وأفعالها‪.‬‬

‫فلو أنشأنا الآن الكائنين الأول من نوع ‪ Car‬والثاني من ‪ Car2‬يكونا بالشكل‪:‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪44‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
4

45
3

‫برمجة التطبيقات باستخدام فلاتر‬


Applications programming with
flutter

‫تأليف فيصل الأسود | مهندس برمجيات‬


11The Biggest risk
Youtube is not
‫قناة الـ‬ taking
‫الكتاب على‬ any
‫لكامل محتويات‬ ‫شرح‬risk
‫يمكنك الحصول على‬

Mark Zuckerberg
StackOverFlow Arabi
‫‪46‬‬
‫التعرف على بيئة فلاتر‬
‫مقدمة‪:‬‬

‫فلاتر هي منصة تطوير تطبيقات جوال مفتوحة المصدر أنشأت من قبل جوجل وتستخدم هذه‬
‫المنصة من اجل تطوير تطبيقات أنظمة اندرويد و ‪ ios‬وكذلك نظام فوشيا الجديد‪.‬‬
‫النسخة الأولى من فلاتر والمعروفة بـ ‪ sky‬السماء قد بنيت لتطوير تطبيقات الاندرويد عام ‪1125‬‬
‫وهكذا تطورت حتى باقي أنظمة التشغيل‪.‬‬
‫يجب أن ننوه أن أي شيء في فلاتر هو ‪ widget‬على سبيل المثال‪ :‬النص‪ ,‬الصورة والزر هي ‪.widget‬‬
‫بعد أن نكون جهزنا أحد الـ ‪ )Android studio, IntliJ IDEAِ( IDE‬كما يمكنك زيارة التوثيق‬
‫الخاص ب ‪ Flutter‬من خلال الموقع التالي ‪:‬‬
‫‪https://flutter.io‬‬
‫سوف نستخدم في كتابة البرامج بيئة العمل ‪ IntliJ IDEA‬لأنها تمكننا من بناء تطبيقات نظامي الـ‬
‫‪ ios‬والاندرويد كما ننوه أيضا أن دارت هي اللغة التي ستكون لنظام فوشيا البديل من جوجل‪.‬‬
‫كما يجب التنويه إلى ضرورة تنصيب ‪ Android studio‬في جميع الأحوال للاستفادة من المحاكيات‬
‫المتضمنة مع حزمته‪.‬‬

‫المشروع األول‪:‬‬

‫‪ .2‬بعد فتح ‪ IntliJ IDEA‬نختار ‪ Create new project‬ونختار من الشريط الأيسر الخيار‬
‫‪ Flutter‬كما في الصورة‪.‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪47‬‬
‫‪ .1‬نعطي المشروع اسم ومسار محدد ثم نختار أساس عمل النظام ويفضل أن يبقى كما هو‬
‫محدد في الصورة وثم ‪.Finish‬‬

‫‪ .3‬بعد فتح المشروع سنلاحظ قالب لأي مشروع ‪ Flutter‬بالشكل التالي‪.‬‬

‫‪ .4‬في الشريط الأيسر من مجموعة المجلدات والملفات التابعة لمشروع ‪ Flutter‬والتي لن‬
‫نتطرق لها كثيرا‪ ,‬ما يهمنا هو بعض المجلدات مثل ‪ lib‬الذي يحوي المكتبات في المشروع‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪48‬‬
‫التطبيق الأول‪:‬‬

‫أي تطبيق ‪ Flutter‬يجب أن يحوي الأمور الأساسية التالية‪:‬‬


‫‪ .2‬استدعاء المكتبة ‪ material.dart‬والمستخدمة في التصميم‪.‬‬
‫‪ .1‬التابع ‪ main‬لبدء المشروع منه‪.‬‬
‫‪ .3‬التابع )(‪ runAPP‬والذي يعتبر التابع المنشط (المشغل) للتصميم‪.‬‬

‫;'‪import 'package:flutter/material.dart‬‬

‫{ )(‪void main‬‬
‫(‪runApp‬‬

‫(‪new Center‬‬
‫‪child: new Text('Hello Stack Overflow Arabi',‬‬
‫‪textDirection: TextDirection.ltr),‬‬
‫)‬

‫;)‬
‫}‬

‫هنا أنشأنا برنامجنا الأول والذي يطبع العبارة التالية كما في الصورة‪.‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
49
Material Design ‫ يعتمد بشكل أساسي على‬Flutter ‫يجب أن نعلم أن تصميم واجهات تطبيقات‬
‫ الخاصة بـ‬classes ‫المدعوم أيضا من جوجل ولكي نبدأ تصميم الواجهة سوف نستخدم أصناف‬
.Material Design

import 'package:flutter/material.dart';

void main() {
runApp(
new Material(
color: Colors.greenAccent,
child: new Center(
child: new Text('Feisal Aswad',
textDirection: TextDirection.ltr,
style: new TextStyle(
fontSize: 25,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic
)),
),
)

);
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
50
‫ كما في‬italic, Bold ‫والناتج هنا هو عبارة عن خلفية بلون "أخضر" مع كتابة تظهر بالوسط بخط‬
:‫الصورة التالية‬

‫ حيث نقوم‬stateless ‫كما يمكن عزل التصميم بصنف خاص وهذا ما يسمى بعزل العناصر الثابتة‬
‫بوضع كل العناصر الثابتة والتي لا تطلب منها الاستجابة للأحداث أو التفاعل مع المستخدم بعزلها‬
:‫بصنف خاص بها واستدعاءها بالتابع الأساسي كما في الشكل‬
main.dart
import 'package:flutter/material.dart';
import 'MyUi.dart';

void main(){
runApp( new MaterialApp(
home:MyText(),
)
); // MaterialAPP
}

MyUi.dart
import 'package:flutter/material.dart';

class MyText extends StatelessWidget{


@override
Widget build (BuildContext context){
return new Material(
color : Colors.green,
child : new Center(
child : new Text('Feisal Aswad',
textDirection: TextDirection.ltr,
style : new TextStyle(fontSize : 23.0,
fontWeight : FontWeight.bold) //Text style

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪51‬‬
‫‪), //Text‬‬
‫‪) //center‬‬
‫‪); //Material‬‬
‫}‬
‫}‬

‫والناتج سوف يكون مماثل للتطبيق السابق مع الاستفادة من كوننا أصبح لدينا ‪ widget‬جاهزة‬
‫ويمكن استدعائها أكثر من مرة ويكون التعديل عليها سهل للغاية‪.‬‬
‫كما يجب الانتباه أننا وضعناه داخل المسار ‪ lib‬في ملف جديد اسمه ‪ MyUi.dart‬وهذا ما يفسر‬
‫الاستدعاء ‪ MyUi.dart‬في البرنامج الرئيسي‪.‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
52
Layouts ‫أنظمة التصميم‬

‫ ومن الأفضل استخدامها‬widget ‫ وهي أيضا‬Container ‫ مجموعة من الحاويات‬Flutter ‫توفر لنا‬


‫ بـ‬new Material ‫ الأخرى فالمستحسن في برنامجنا السابق استبدال الكلمة‬widgets ‫لاحتواء ال‬
‫ لكي تكون حاوية للنص التي بداخلها وسيبقى البرنامج كما هو بالطبع لأن‬new container
.‫ مختلفة سنراها في الدروس اللاحقة‬Layouts ‫ ويوجد‬Material ‫هنا تماثل‬Container
.Layouts ‫ أو مخطط التصميم مخططات تصميم أخرى داخلها‬Layouts ‫كما يمكن أن تحوي الـ‬
:Layouts ‫من أنواع‬
:Column ‫التخطيط العامودي‬ 
‫ هذا‬Layouts ‫ وهو أيضا‬Column ‫وبداخلها‬Container ‫في مثالنا التالي سوف ننشأ‬
‫ أن‬Layouts ‫ وهكذا نفهم أنه يمكن لأي‬text ‫ سيكون له عدة أولاد من نوع‬column ‫العمود‬
.‫يحوي عدد غير محدد من الأبناء‬
MyUi.dart
import 'package:flutter/material.dart';

class MyColumn extends StatelessWidget{


@override
Widget build (BuildContext context){
return new Container(
color : Colors.green,
child : new Column(
mainAxisAlignment : MainAxisAlignment.center,
children : <Widget>[
new Text('Feisal Aswad',
textDirection : TextDirection.ltr),
new Text('Ahmad Afandi',
textDirection : TextDirection.ltr),
],
),
);

}
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪53‬‬
‫والملف الرئيسي ‪ main.dart‬هو نفسه الملف للبرنامج السابق‪.‬‬

‫والناتج كما ظاهر بالصورة أعلاه‪.‬‬


‫التخطيط الأفقي (السطري) ‪:Row‬‬ ‫‪‬‬
‫رأينا في التخطيط السابق كيف ترتب الـ ‪ widget‬عاموديا ضمن عامود وماذا لو أردنا‬
‫ترتيبها أفقيا بجانب بعضها البعض هناك نستخدم الـ ‪ Layouts‬المسماة ‪ Row‬باستبدال‬
‫كلمة ‪ column‬في البرنامج السابق بكلمة ‪ Row‬سوف يكون ناتج البرنامج كما يلي‪:‬‬
‫ويجب الانتباه أنه لا يجب أن تمتلئ الشاشة عرضيا بحيث نضع عناصر مناسبة لحجم‬
‫الشاشة‪.‬‬

‫ملاحظة‪ :‬يمكن أن نجعل العرض النص الأخير في التخطيط يأخذ كامل المساحة المتبقية واستدعاء‬
‫التابع ‪ Expanded‬للعنصر الأخير كما يلي‪:‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
54
MyUi.dart
import 'package:flutter/material.dart';

class MyColumn extends StatelessWidget{


@override
Widget build (BuildContext context){
return new Container(
color : Colors.greenAccent,
child : new Row(
mainAxisAlignment : MainAxisAlignment.center,
children : <Widget>[
new Text('Feisal Aswad',
textDirection : TextDirection.ltr),
Expanded( child: new Text('Ahmad Afandi',
textDirection : TextDirection.ltr)
)
,
],
),
);

}
}

‫ التي ربما ستناولها في كتابنا لاحقا‬Layouts ‫يوجد المزيد من الـ‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪55‬‬
‫الـ ‪ Widget‬المقدمة مع ‪Flutter‬‬

‫هي مجموعة من العناصر المقدمة مع حزمة ‪ Flutter sdk‬الأساسية‪.‬‬


‫حيث تقدم ‪ Flutter‬نوعين من العناصر الثابتة والمرنة وهي‪:‬‬
‫‪Stateless widget‬‬ ‫‪‬‬
‫‪Stateful widget‬‬ ‫‪‬‬

‫سنبدأ مع العنصر ‪ Stateful‬كما سيمر معنا في المثال التالي‪.‬‬


‫يجب الانتباه إلى فكرة أن إنشاء زر مرن يمر بمرحلتين أو يتكون من صنيفين الأول للبناء والثاني‬
‫للتعديل كل مرة‪.‬‬

‫الان سنقوم بعرض اهم الـ‪ Widget‬المقدمة مع حزمة ‪Flutter‬‬


‫‪Raised Button‬‬ ‫‪‬‬

‫الكود التالي هو لـ إنشاء زر كما بينا في المخطط السابق‪:‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
56
main.dart
import 'Package:flutter/material.dart';
void main(){
runApp(new MaterialApp(
home: new ourWidget(),
));
}

ourWidgetFile.dart
class ourWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _Buildstate();
}

}
class _Buildstate extends State<ourWidget>
{
String names= '';

@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar : new AppBar(
backgroundColor: Colors.blue,
title: new Text ("MyAPP"),
),
body: new Container(
child: new RaisedButton(
onPressed: () => clickfunc('Hi'),
child: new Text ('click me ${names}')
)
),
);
}
void clickfunc(String txt){
setState((){
names = txt;
});
}
}

‫في الكود السابق أنشأنا زر داخل تابع البناء وعند كل استدعاء لتابع التعديل سوف يعيد بناءه وهكذا‬
‫ عن طريق تابع مجهول ويمرر له نص معين‬clickfunc ‫ نلاحظ أن الزر يطلب التابع‬.‫يكون الزر حرف‬
.‫كما أن التابع يعرف في نهاية الصنف‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
57
‫ كما يبين الشكل التالي الزر من نوع‬,‫ هو زر يأخذ شكل نص عادي فقط‬:FlatButton ‫الزر المسطح‬ 
.FlatButton

:‫ويمكن تعريف كما في الكود التالي‬


ourWidgetFile.dart
class ourWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _Buildstate();
}

class _Buildstate extends State<ourWidget>


{
String names= '';
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar : new AppBar(
backgroundColor: Colors.blue,
title: new Text ("MyAPP"),
),
body: new Container(
child: new FlatButton(
onPressed: () => clickfunc('Hi'),
child: new Text ('click me ${names}')
)
),
);

}
void clickfunc(String txt){
setState((){
names = txt;
});
}

.‫ كما في المثال السابق‬main ‫هنا الاستدعاء عن طريق التابع‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
58
:IconButton ‫الزر الأيقونة‬ 
:‫هو زر يحوي داخله صورة على شكل أيقونة كما في الشكل‬

:‫يمكن تعريفه كما يلي‬


ourWidgetFile.dart
class ourWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _Buildstate();
}

class _Buildstate extends State<ourWidget>


{
String names= '';
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar : new AppBar(
backgroundColor: Colors.blue,
title: new Text ("MyAPP"),
),
body: new Container(
child: new IconButton(
onPressed: () => clickfunc('Hi'),
icon: Icon(Icons.wifi_tethering,size: 50,),
)
),
);

}
void clickfunc(String txt){
setState((){
names = txt;
});
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
59
.‫حيث تتيح لنا فلاتر العديد من الرموز والاشكال الجاهزة‬
:TextField ‫نص الإدخال‬ 
‫هو حقل نصي يسمح للمستخدم بإدخال نص معين مثل كلمة مرور أو اسم مستخدم كما هو مبين‬
:‫في الشكل‬

‫ويمكن تعريفه كما في الكود التالي‬


ourWidgetFile.dart
class ourWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _Buildstate();
}

class _Buildstate extends State<ourWidget>


{
String names= '';
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar : new AppBar(
backgroundColor: Colors.blue,
title: new Text ("MyAPP"),
),
body: new Container(
child: new TextField(
)
),
);

}
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
60
:‫ بعض الخصائص الهامة نذكر منها‬TextField ‫كما يحوي الـ‬
‫الخاصة‬ ‫الشرح‬
Auto correct : true ‫تجعل الحقل النصي يصحح األخطاء اللغوية‬
‫تلقائيا‬
keyboardType: TextInputType )‫تسمح لنا بتحديد المدخالت (رقمي – ايميل‬
‫وتساعد في عملية اختيار الدخل‬
decoration ‫تسمح لك بتشكيل صنف إلظهار الحقل النصي‬
‫بعدة أشكال‬
:‫مثال تطبيقي‬
mobile number ‫في مثالنا التالي سوف نقوم بتصميم واجهة تحوي حقل ادخال له العنوان‬
‫وبجانبه صورة المستخدم كما أن نوع المدخلات هي من نوع رقم هاتف‬

ourWidgetFile.dart
class ourWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _Buildstate();
}
}
class _Buildstate extends State<ourWidget>
{
String names= '';
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar : new AppBar(
backgroundColor: Colors.blue,
title: new Text ("MyAPP"),
),
body: new Container(
child: new TextField(
autocorrect: true,
keyboardType: TextInputType.phone,
decoration: new InputDecoration(
icon: Icon(Icons.perm_identity),
labelText: 'mobile number',
hintText: 'Enter Here',
), // Input Decoration
) // text field
),
);
}
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
61
:Checkbox ‫مربع الاختيار‬ 
‫جميعنا نعرف مربع الاختيار المتعدد الذي يسمح لنا اختيار بعض العناصر وترك أخرى كما هو مبين‬
.‫في الصورة‬

ourWidgetFile.dart
class ourWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _Buildstate();
}

class _Buildstate extends State<ourWidget>


{
String names= '';
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar : new AppBar(
backgroundColor: Colors.blue,
title: new Text ("MyAPP"),
),
body: new Container(
child: new Column(
children: <Widget>[
new Checkbox(
value: true,
onChanged: null,
activeColor: Colors.greenAccent,),
new CheckboxListTile(value: true,
onChanged: null,
title: new Text("Choose"),
secondary: new Icon(Icons.adb),)
],
)

),
);
}
}

.CheckBoxListTile ‫ ومربع الاختيار المطور‬CheckBox ‫هنا قمنا ببناء عنصر من نوع‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
62
:switch ‫مبدل الحالة‬ 
.switch ‫ الصورة التالية توضح العنصر‬,‫ مع اختلاف في الشكل‬checkbox ‫هو مشابه تماما لـ‬

:‫ كما يلي‬switch ‫والكود المطلوب لتعريف‬


ourWidgetFile.dart
class ourWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _Buildstate();
}

class _Buildstate extends State<ourWidget>


{
String names= '';
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar : new AppBar(
backgroundColor: Colors.blue,
title: new Text ("MyAPP"),
),
body: new Container(
child: new Column(
children: <Widget>[
new SwitchListTile(
value: true,
onChanged: null,
activeColor: Colors.greenAccent,),
new SwitchListTile(value: true,
onChanged: null,
title: new Text("Humbrger"),
secondary: new Icon(Icons.adb),subtitle: new
Text("meat,mayonise,luttce..."),)
],
)
),
);
}
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
63
:Drawer ‫الشريط الجانبي‬ 
‫هو الشريط المستخدم غالبا لإظهار خيارات المستخدم وملفه الشخصي ويكون موجود في الصفحة‬
.‫ يحوي داخله بعض العناصر‬Drawer ‫ الصورة التالية تبين عنصر‬.‫الرئيسية للبرنامج‬

:‫ نكتب الكود التالي‬Drawer ‫لإنشاء عنصر من نوع‬


ourWidgetFile.dart
class ourWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _Buildstate();
}

}
class _Buildstate extends State<ourWidget>
{
String names= '';
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
drawer: new Drawer(
child: new Container(
color: Colors.blue,
padding: EdgeInsets.all(12.0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(padding: EdgeInsets.only(bottom: 25)),
new Text ('MyAcount'),
Padding(padding: EdgeInsets.only(bottom: 25)),
new Text ('Edit'),
Padding(padding: EdgeInsets.only(bottom: 25)),
new Text ('Exit'),]
),
),
),
appBar : new AppBar(
backgroundColor: Colors.blue,
title: new Text ("MyAPP"),
),
body: new Container(
),
);
}
}
‫تأليف فيصل الأسود | مهندس برمجيات‬
Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
64
:(Alert Dialog) Notification Dialog ‫مربع الحوار‬ 
.‫هو مربع رسالة لتنبيه المستخدم لأجراء معين مع إمكانية إعطائه عدة خيارات‬

,‫يجب أن تعلم ان مربع الحوار لا يستخدم إلا عند اعلام شيء ما أو سماحية المستخدم لاتخاذ القرار‬
.‫ومنه هذا المربع يستدعى دوما عن طريق تابع أو طريقة ما‬
ourWidgetFile.dart
import 'Package:flutter/material.dart';
import 'dart:io';
class ourWidget extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _Buildstate();
}

class _Buildstate extends State<ourWidget>


{
String names= '';
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar : new AppBar(
backgroundColor: Colors.blue,
title: new Text ("MyAPP"),
),
body: new Container(
child: new FlatButton(onPressed: (){DialogBuild(context);},
child: new Text("Exit"))
),
);
}

.dart:io ‫ للخروج من البرنامج موجود في المكتبة‬exit(0) ‫ننوه ان التابع‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
65
.‫ والتي تقوم ببناء مربع التنبيه عند طلبها وإظهاره‬DialogBuild(context) ‫بقي لدينا انشاء الدالة‬
ourWidgetFile.dart
Future<Null> DialogBuild(BuildContext context) async {
return showDialog<Null>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return new AlertDialog(
title: new Text("Exit"),
content: new SingleChildScrollView(
child: new ListBody(
children: <Widget>[
new Text("Do you want to Exit"),
],
),
),
actions: <Widget>[
new FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: new Text("No")),
new FlatButton(
onPressed: () {
exit(0);
},
child: new Text("Yes")),
],
);
});
}

:singleDialog ‫مربع الحوار البسيط‬ 


‫هو رسالة مشبهة لـ مربع الحوار العادي إلا أنها تقدم مجموعة من الخيارات لتساعد المستخدم على‬
.‫الاختيار ولا تتطلب تعقيد برمجي‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
66
‫والكود التالي لإنشاء تابع (دالة) يبني مربع الحوار البسيط‬
ourWidgetFile.dart

Future simpleShowSimpleDialog(BuildContext context)async {


switch (await showDialog(context: context, builder: (BuildContext
context) {
return new SimpleDialog(
title: new Text ('options'),
children: <Widget>[
new SimpleDialogOption (child: new Text ('No'),
onPressed: () {
Navigator.pop(context);
},),
new SimpleDialogOption (child: new Text('Yes'),
onPressed: () {
exit(0);
})
]

);
})) {
}
}

:Bottom sheet ‫القائمة السفلية‬ 


:‫ إلا أنها تظهر في الأسفل‬Drawer ‫تشبه الـ‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
67
‫ كما أنها تشكل ضمن تابع مستقل أيضا‬, BottomSheet ‫والكود التالي لإنشاء قائمة من نوع‬
.‫وتستدعى عند الطلب‬

ourWidgetFile.dart
Bottomsheetopen(BuildContext context) async{

await showModalBottomSheet(context: context,


builder: (BuildContext context){
return new Container(
height: 300,

child: new Row(


mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text ('First'),
new Text('Second'),],
), // Row
); // container
}
);
}

: card layout ‫ الـ‬


‫ لكونه يساعدنا على إنشاء مثل البطاقات التي تحوي تفاصيل‬Flutter ‫هو إحدى العناصر المهمة في‬
.‫عن معلومات عدة أنواع‬

.‫من المؤكد أن البطاقات يمكن ان تأخذ كامل عرض الشاشة‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
68
:‫ كما في الكود التالي‬layout ‫يمكن إنشاء مثل هذا النوع من الـ‬
ourWidgetFile.dart
class _Buildstate extends State<ourWidget> {
String names = '';
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(),
body: new Container(
child:new Column(
children: <Widget>[
new Card(

color: Colors.amber,
child: new Column(

children: <Widget>[
new Text ('card'),
new RaisedButton(onPressed: null,child: new Text("Click
me"),)
],
),

),
new Card(

color: Colors.amber,
child: new Column(
children: <Widget>[
new Text ('card'),
new RaisedButton(onPressed: null,child: new Text("Click
me"),)
],
),

),
],
)
)
);
}

Circle Avatar ‫ دائرة المستخدم‬


‫هي غالبا دائرة تستخدم لوضع صورة المستخدم مع معلوماته أو صورة أي عنصر مع معلوماته كما‬
.‫في الصورة‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
69
child: new CircleAvatar(
child: new Text('Name'),
backgroundColor: Colors.pink,
foregroundColor: Colors.white,
radius: 50,

),

:List View ‫ العنصر‬


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

‫ غالبا له الشكل‬,‫ ويأخذ شكل معين حسب تصميمك‬ListTile ‫كل عنصر في هذه القائمة يسمى‬
:‫التالي‬

:ListView ‫والكود التالي لإنشاء‬


child:new ListView(

children:<Widget>[new ListTile(
title: new Text("I'm Title 1"),
subtitle: new Text("I'm subtitle 1"),
leading: new Text("I'm leading 1")
) ,
new ListTile(
title: new Text("I'm Title 2"),
subtitle: new Text("I'm subtitle 2"),
leading: new Text("I'm leading 2")
)],// ListTile
)

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
70
Scaffold ‫القوالب الجاهزة‬
‫ تساعد في بناء واجهات قوية دون الخوض في عناء تصميم‬Flutter ‫ جاهزة تقدمها‬widgets ‫هي‬
.‫الواجهات‬
‫الصور التالية تبين مجموعة قوالب جاهزة‬

:AppBar 
AppBar ‫ القالب‬scaffold ‫المثال التالي لصنف مستخدم قوالب الـ‬
MyUi.dart
import 'package:flutter/material.dart';

class MyColumn extends StatelessWidget{


@override
Widget build (BuildContext context){
return new Scaffold(
appBar :new AppBar(
title: new Text('AppBar Title'),
backgroundColor : Colors.greenAccent,
actions : <Widget>[
new IconButton(icon :new Icon(Icons.adb),
onPressed : (){} ),
new IconButton(icon:new Icon(Icons.verified_user),
onPressed: (){}),
],
)
);
}
}

‫ التي تظهر شريط في أعلى‬.widget AppBar ‫ويكون ناتج البرنامج عبارة عن تطبيق يستخدم الـ‬
.‫البرنامج مع مجموعة اوامر معينة على شكل أيقونات كما في الشكل التالي‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪71‬‬
‫كما يمكن إضافة أحداث لتلك الأيقونات عن طريق تعريف تابع داخل الصنف واستدعاء هذا التابع‬
‫بالشكل التالي‪:‬‬

‫‪new IconButton(icon: new Icon(Icon.alarm),‬‬


‫‪onPressed: myFunction),‬‬

‫وهنا نحتاج الى دالة ‪ myFunction‬ضمن الصنف لتنفيذ الامر المطلوب ويمكن انشاء دالة مجهولة‬
‫مباشرة كما يلي‪:‬‬

‫‪new IconButton(icon: new Icon(Icon.alarm),‬‬


‫‪onPressed: (){ //write code here}),‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
72
‫ وهنا يمكن وضع أجزاء جديدة في‬Scaffold ‫ الـ‬Body ‫ داخل جسم‬widgets ‫كما يمكنك وضع‬
‫ ثم‬Body ‫ وبالتحديد خاصة‬Scaffold ‫ داخل الـ‬widget ‫الواجهة في الكود التالي جزء من كود الـ‬
‫ وهو نص عادي قابل للاستجابة للأحداث وقد تم ربطه بحيث عند‬Inkwell ‫إنشاء نص من نوع معين‬
.‫الضغط عليه ينفذ تابع معين‬
MyUi.dart
import 'package:flutter/material.dart';

class MyColumn extends StatelessWidget{


@override
Widget build (BuildContext context){
return new Scaffold(
body: new Container(
alignment : Alignment.center,
child : new Column (
mainAxisAlignment : MainAxisAlignment.center,
children:<Widget>[
new InkWell(
child: new Text ('Press Me'),
onTap: (){print('Hello');},
),]

),
),
appBar :new AppBar(

title: new Text('AppBar Title'),


backgroundColor : Colors.greenAccent,
actions : <Widget>[
new IconButton(icon :new Icon(Icons.adb),
onPressed : null ),
new IconButton(icon:new Icon(Icons.verified_user),
onPressed:null),
],
)
);
}
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
73
:Navigation Bar ‫شريط التصفح‬ 
‫ كما في الشكل وإعطاء‬scaffold ‫ بسهولة عن طريق‬Bottom Navigation Bar ‫يمكن إنشاء‬
.‫كل زر حدث يستجيب له‬

MyUi.dart
import 'package:flutter/material.dart';

class MyColumn extends StatelessWidget{


@override
Widget build (BuildContext context){
return new Scaffold(
bottomNavigationBar:new BottomNavigationBar(items:[
new BottomNavigationBarItem(icon: new
Icon(Icons.access_alarms), title: new Text('Bus')),
new BottomNavigationBarItem(icon: new
Icon(Icons.ac_unit), title:new Text('adb')) ],
onTap:(int x) => print('index $x'),
type: BottomNavigationBarType.fixed,),

appBar :new AppBar(

title: new Text('AppBar Title'),


backgroundColor : Colors.greenAccent,
actions : <Widget>[
new IconButton(icon :new Icon(Icons.adb),
onPressed : null ), // Icon1
new IconButton(icon:new Icon(Icons.verified_user),
onPressed:null),
],
)
);
}
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪74‬‬
‫والناتج يكون‪:‬‬

‫الزر العائم ‪:Floatting Button‬‬ ‫‪‬‬


‫هو زر يظهر في زاوية الشاشة وغالبا يساعد في العمليات التكرارية كما في الصورة‬
‫الكود التالي يبين طريقة إنشاء "زر عائم" كما يلي مع إعطائه استجابة لحدث معين‪:‬‬

‫عبارة ‪I am float button‬‬ ‫هنا عند الضغط على الزر سوف يطبع في الـ ‪console‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
75
:‫كما في الكود التالي‬
MyUi.dart
import 'package:flutter/material.dart';

class MyColumn extends StatelessWidget{


@override
Widget build (BuildContext context){
return new Scaffold(
floatingActionButton: new FloatingActionButton(
onPressed :(){print('I am float button');},
child : new Icon(Icons.adb),
backgroundColor: Colors.blue
), // floating ActionButton

appBar :new AppBar(


title: new Text('AppBar Title'),
backgroundColor : Colors.greenAccent,
actions : <Widget>[
new IconButton(icon :new Icon(Icons.adb),
onPressed : null ), // Icon1
new IconButton(icon:new Icon(Icons.verified_user),
onPressed:null),
],
)
);
}
}

:Online ‫التصميم‬ 
‫ على الانترنت تصميم واجهات عن طريق أدوات وازرار يمكنك‬Flutter Studio ‫تتيح لنا منصة‬
:‫الوصول لتلك المنصة عن طريق الرابط‬
https://www.flutterstudio.app

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪76‬‬
‫التصفح والتوجيه ‪Navigation‬‬

‫في هذا القسم من الكتاب سوف نركز على كيفية التنقل بين الصفحات أو الواجهات وهذا ما يسمى‬
‫بمفهوم الـ ‪.Navigation‬‬

‫في مثالنا التالي لدينا واجهتين سوف ننتقل من الواجهة الأولى (الأساسية) إلى الواجهة الثانية‬
‫باستخدام مفاهيم التصفح‪.‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
77
‫ ثم نكتب التابع‬, ‫ لكل واجهة ونبني ضمنها التصميم الصحيح‬class ‫بالبداية سوف نشكل صنف‬
:‫ كما يلي مستعينين بصنف ثالث يحدد لنا المسارات‬main
main.dart
import 'package:flutter/material.dart';
import 'package:navigationProject/PageOneFile.dart';
import 'package:navigationProject/PageTwoFile.dart';
void main(){
runApp(new MaterialApp(
home: new MyApp())
);
}
class MyApp extends StatefulWidget{

@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _MyApp();
}
}
class _MyApp extends State<MyApp>{
@override
Widget build(BuildContext context) {
// TODO: implement build
return new MaterialApp(
title: 'Navigation',
routes: <String, WidgetBuilder>{
'/First': (BuildContext context) => new PageOne(),
'/Second': (BuildContext context) => new PageTwo(),},
home: new PageOne());
}//MaterialApp
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
78
‫ونكتب كود الصنفين بحيث الأول يطلب الثاني والثاني يطلب الأول‬
:‫الصنف الأول‬
PageOneFile.dart
import 'package:flutter/material.dart';
class PageOne extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _PageOne();
}
}

class _PageOne extends State<PageOne> {


@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("page 1"),
),
body: new Container(
child: new Column(
children: <Widget>[
new RaisedButton(
onPressed: () {
Navigator.of(context).pushNamed('/Second');
},
child: new Text('next'))
],
),
),
);
}
}

:‫والصنف الثاني‬
PageOneFile.dart
import 'package:flutter/material.dart';
class PageTwo extends StatefulWidget {
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _PageTwo();
}
}

class _PageTwo extends State<PageTwo> {


@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪79‬‬
‫‪title: new Text("page 2"),‬‬
‫‪),‬‬
‫(‪body: new Container‬‬
‫(‪child: new Column‬‬
‫[>‪children: <Widget‬‬
‫(‪new RaisedButton‬‬
‫{ )( ‪onPressed:‬‬
‫;)'‪Navigator.of(context).pushNamed('/First‬‬
‫‪},‬‬
‫))'‪child: new Text('Back‬‬
‫‪],‬‬
‫‪),‬‬
‫‪),‬‬
‫;)‬
‫}‬
‫}‬

‫بنينا موجهاً للصفحات وكما في الخاصة ‪ home‬وضمنها الصفحة‬ ‫نلاحظ بالخاصية ‪routes‬‬
‫‪ PageOne‬كصفحة أولى‪.‬‬
‫نلاحظ عند الانتقال للصفحة الثانية ظهور زر الرجوع أوتوماتيكياً وهذا ما يوفره الـ ‪Scaffold‬‬
‫بالطريقة ‪.PushNamed‬‬

‫يمكن عدم إظهار هذا الزر باستخدام طريقة تصفح أخرى هي كما في الكود التالي‪:‬‬

‫{ )( ‪onPressed:‬‬

‫‪Navigator.of(context).‬‬
‫;)‪pushNamedAndRemoveUntil('/Second',(Route<dynamic> route) => false‬‬

‫‪},‬‬

‫وهنا عند الذهاب من الصفحة الأولى إلى الصفحة الثانية لن يظهر زر الرجوع أما في باقي حالات‬
‫التصفح فسوف يظهر تلقائيا‪.‬‬
‫هناك أيضا طريقة للرجوع للصفحة السابقة كما يلي‪:‬‬

‫{)( ‪onPressed:‬‬

‫;)(‪Navigator.of(context).pop‬‬

‫‪},‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪80‬‬
‫وهي تنوب تماما عن زر الرجوع في حال كانت إمكانية الرجوع مقبولة أي أرسلت بالطريقة‬
‫‪ PushNamed‬وللتحقق من هذا يجب كتابة الكود السابق بالشكل‪:‬‬

‫{ )( ‪onPressed:‬‬

‫))(‪if(Navigator.of(context).canPop‬‬
‫{‬
‫;)(‪Navigator.of(context).pop‬‬
‫}‬
‫}‬

‫ملاحظة‪ :‬تستخدم عملية منع الرجوع كما في حالات صفحة الـ ‪ Login‬حيث لا يسمح للمستخدم‬
‫بالرجوع إليها مرة أخرى‪.‬‬

‫تبادل القيم والمتغيرات بين الصفحات‪:‬‬


‫تبادل القيم والمتغيرات بين الصفحات‪:‬‬
‫رأينا في الفقرة السابقة كيف يمكن الانتقال بين الصفحات ولكن ماذا لو أردنا أن نرسل قيم من‬
‫صفحة إلى أخرى‪.‬‬
‫هناك طريقتين للقيام بذلك‪:‬‬
‫الطريقة الأولى‪ :‬ارسال القيم عن طريق الباني للصفحة ‪.constructer‬‬ ‫‪‬‬

‫في البداية يجب استخدام التابع التالي للتنقل كما في الكود‪:‬‬


‫{ )‪toPageTwo(BuildContext context‬‬
‫‪Navigator.push(context,new MaterialPageRoute(builder:‬‬
‫)‪(BuildContext context‬‬
‫)"‪=> new PageTwo("hello‬‬
‫;))‬
‫}‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
81
:‫ويكون شكل التابع للصفحة الأولى كما في الكود التالي‬
PageOneFile.dart
import 'package:firebase_tutorial/PageTwoFile.dart';
import 'package:flutter/material.dart';

class PageOne extends StatefulWidget {


@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _PageOne();
}
}

class _PageOne extends State<PageOne> {


@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("page 1"),
),
body: new Container(
child: new Column(
children: <Widget>[
new RaisedButton(
/*onPressed: () {
Navigator.of(context).pushNamed('/Second');
},*/
/*onPressed: () {

Navigator.of(context).
pushNamedAndRemoveUntil('/Second',(Route<dynamic>
route) => false);
},*/
onPressed: () {
toPageTwo(context);

},

child: new Text('next'))


],
),
),
);
}

toPageTwo(BuildContext context) {
Navigator.push(context,new MaterialPageRoute(builder:
(BuildContext context)
=> new PageTwo("hello")
));
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
82
‫" من الصفحة الأولى إلى الصفحة الثانية وعلينا الآن أن نجهز الصفحة‬hello" ‫هنا أرسلنا العبارة‬
.‫الثانية لاستقبال البيانات‬
‫ ومنه سوف‬toPageTwo ‫وهنا عند الضغط على زر معين من الصفحة الأولى سوف تنفذ الطريقة‬
.‫ باستقبالها عن طريق الباني‬second ‫" وتقوم الصفحة‬hello" ‫ترسل العبارة‬

:‫ونكتب الصنف الثاني كما في الكود التالي‬


PageTwoFile.dart
import 'package:flutter/material.dart';

class PageTwo extends StatefulWidget {


String Message;
PageTwo(this.Message);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _PageTwo(Message);
}
}

class _PageTwo extends State<PageTwo> {


String Message;
_PageTwo(this.Message);
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("page 2"),
),
body: new Container(
child: new Column(
children: <Widget>[
new RaisedButton(
onPressed: () {
Navigator.of(context).pushNamed('/First');
},
child: new Text('Back'))
],
),
),
);
}
}

‫هنا اعتبرنا ان الصفحة الأولى فقط ترسل للصفحة الثانية وإذا اردنا أن نرسل من الثانية للأولى نشكل‬
.ً‫نفس التابع في الثانية وكذلك ننشئ باني في الأولى لاستقبال البيانات كما فعلنا مسبقا‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪83‬‬
‫الطريقة الثانية‪ :‬نلخص الطريقة الثانية بإنشاء صنف خاص يحوي متحولات ستاتيكية عامة هذا‬ ‫‪‬‬
‫المتحولات يمكن تخزين البيانات فيها من قبل الصفحة المرسلة وتقوم الصفحة المستقبلة باستدعاء‬
‫هذه البيانات من الصنف وذلك عن طريق مفتاحها الخاص ‪.key‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪84‬‬
‫التعامل مع ‪Json‬‬
‫مقدمة‪:‬‬
‫في البداية دعونا نشرح ‪ Json‬وما الفائدة منها‪ ,‬تعد ‪ Json‬هي الوسيلة الأفضل للحصول من‬
‫البيانات من المخازن الالكترونية أو مواقع الويب‪ .‬بحيث علينا طلبها عن طريق رابط يدعى ‪API‬‬
‫خاص لهذه البيانات فيعيد لي البيانات مكتوبة بلغة ‪.Json‬‬
‫الحقيقة إن ‪ Json‬ليست لغة برمجة إنما هي لغة هيكلة بيانات مثل ‪ XML‬ولكن تساعد جدا في‬
‫فهم كيفية استقبال البيانات من أي موقع‪.‬‬
‫كما يجدر الذكر أن هناك العديد من المواقع التي تقدم بيانات جاهزة تساعد المبرمجين في بناء‬
‫التطبيقات منها بيانات عن أسعار العملات او بيانات الطقس‪.‬‬
‫يجب أن نعلم أن طلب هذه الخدمة عن طريق ‪ API‬الخاص بها يسمح لنا فقط باستعراضها‬
‫وعرضها ضمن تطبيقنا‪ ,‬حيث أي ملف ‪ Json‬يكتب على شكل كائنات وخصائص لكل خاصية أو‬
‫كائن مفتاح (اسم معين) وقيمة‪.‬‬
‫لدينا هنا ملف ‪ Json‬يحوي بيانات مستخدمين حيث لكل مستخدم رقم معرف ‪ ,id‬اسم ‪,name‬‬
‫عنوان ‪ ,Address‬الآباء ‪.parents‬‬
‫‪JSON File: data.json‬‬
‫[‬
‫{‬
‫‪id: 1,‬‬
‫‪name: "Ahmad",‬‬
‫‪Address: "Syria",‬‬
‫{ ‪Parents:‬‬
‫‪father: "Mohamad",‬‬
‫"‪mother: "Nour‬‬
‫}‬
‫‪},‬‬
‫{‬
‫‪id: 2,‬‬
‫‪Name: "Farah",‬‬
‫‪Address: "Damascus",‬‬
‫{‪Parents:‬‬
‫‪Father = "Feisal",‬‬
‫‪Mother = "Batoul",‬‬
‫}‬
‫}‬
‫]‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪85‬‬
‫يمكن الوصول للمعلومات بالشكل التالي ‪:‬‬
‫"‪data [0] ['Name'] = "Ahmad‬‬
‫‪data [1] ['Parents'] ['Father'] = " Feisal ",‬‬

‫حيث ‪ data‬ملف الـ ‪ Json‬الذي لدينا ‪.‬‬

‫الآن لنعد إلى ‪ Flutter‬ونتعلم كيفية جلب مثل تلك البيانات وعرضها على تطبيقنا‪.‬‬
‫بالبداية يجب جلب مكتبة ال‪ http‬التي تتعامل مع ‪ json‬الى مشروعنا كما يلي‪:‬‬
‫نفتح الملف ‪ pubspec.yaml‬من ضمن ملفات المشروع‪.‬‬ ‫‪‬‬

‫نضيف فقط السطر ‪ http: ^0.12.0+1‬كما في الصورة التالية ‪:‬‬ ‫‪‬‬

‫نضغط ‪ Package get‬لاستدعاء المكتبة من الانترنت‪:‬‬ ‫‪‬‬

‫وننتظر حتى اكتمال التحميل‪.‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
86
:‫ إلى تطبيقنا‬Json ‫الطريقة التالية مهمتها جلب البيانات بطريقة‬
Future<List> getData() async{
String url = " our API ";
http.Response r= await http.get(url);
return json.decode(r.body);
}
.‫نلاحظ انها متزامنة انها تنتظر قدوم البيانات من الشبكة‬

:‫كما يجب لتطبيق تلك الطريقة استخدام المكتبات‬

import 'dart:convert';
import 'package:http/http.dart' as http;

:‫مثال‬
.‫ تعيد لي الكثير من البيانات وأريد وصفها ضمن قائمة‬API ‫ليكن لدينا‬
.‫سنبدأ الآن بكود كامل لحل تلك المشكلة وتظهر لدي البيانات كما في الصورة‬

import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';

void main() async{


List data = await getData();
runApp(new MaterialApp(
title: 'Json Example',
home:new Scaffold(

appBar: AppBar(

title: new Text ('Json')


)
,
body: new Center(
child:new ListView.builder(
‫تأليف فيصل الأسود | مهندس برمجيات‬
Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
87
itemCount: data.length,
itemBuilder: (BuildContext context, int Position){
return new ListTile(
title:new Text("${data[Position]['name']}",style: TextStyle(
color: Colors.greenAccent,
fontSize: 22,
),)
,
leading: new Text("${data[Position]['id']}",style: TextStyle(
color: Colors.greenAccent,

fontSize: 22,
),),
);
}
)
)
)
)
);

}
Future <List> getData() async{
String url = 'https://jsonplaceholder.typicode.com/comments';

http.Response r= await http.get(url);


return json.decode(r.body);
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪88‬‬
‫استدامة البيانات‬

‫توفر فلاتر أكثر من طريقة لحفظ البيانات الخاصة بالتطبيقات أهم هذه الطرق تشمل ما يلي‪:‬‬
‫الذاكرة الداخلية (‪ :)Internal Storage‬وفيها يتم تخزين البيانات في الذاكرة الداخلية للتطبيق‪.‬‬
‫الذاكرة الخارجية (‪ )External Storage‬وفيها يتم تخزين البيانات في الذاكرة الخارجية العامة‬
‫بحيث أي تطبيق يمكن الوصول لها‬
‫التفضيلات المشتركة (‪ :)Shared Preferences‬تشمل حفظ بيانات أساسية بمفاتيح محددة (‪Key-‬‬
‫‪.)Value pairs‬‬
‫قواعد البيانات (‪ :)SQLite Databases‬وفيها يتم تخزين البيانات في قواعد بيانات خاصة‬
‫بالتطبيق‪.‬‬
‫استخدام أي من هذه الطرق يعتمد على احتياجاتك‪ ,‬وكذلك المساحة المطلوبة لتخزين البيانات‬
‫وسيتم تاليا تفصيل كل طريقة على حدى‪.‬‬

‫الذاكرة الداخلية (‪)internal Storage‬‬ ‫‪‬‬


‫يمكن تخزين البيانات مباشرة في الذاكرة الداخلية للجهاز‪ ,‬وتكون البيانات مخزنة خاصة بالتطبيق‬
‫الذي تم التخزين من خلاله ولا يمكن للتطبيقات الأخرى الوصول لهذه البيانات‪ .‬وعند حذف التطبيق‬
‫تحذف معه ملفاته وبياناته مباشرة‪.‬‬
‫لإنشاء وتخزين واسترجاع بيانات تطبيق الذاكرة نستخدم الطريقتين الرئيسيتين التاليتين‪.‬‬

‫يجب أن ننوه بالبداية أنه كي نستطيع استخدام التخزين يجب أن نضيف حزمة ‪ io‬إلى مشروعنا‬
‫بحيث نضيف في الملف ‪ Pubspec.yaml‬وتحت السطر ‪ Cupertino_icons‬سطر جديد يحتوي‬
‫‪ Path_Provider: ^0.4.1‬ثم نضغط ‪ package get‬كما فعلنا مع حزمة ‪.json‬‬

‫وأيضا نضيف المكتبة ‪ io‬للمشروع كما يلي‪:‬‬


‫;'‪import 'package:flutter/Material.dart‬‬
‫;'‪import 'package: Path_Provider/Path_provider.dart‬‬
‫;'‪import 'dart:io‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
89
:‫ والتي تعيد مسار تطبيقنا كما يلي‬AppPath ‫) الأولى المستخدم هي‬method) ‫التابع أو الطريقة‬
Future<String> AppPath()async{
final path = await getApplicationDocumentsDirectory();
return path.path;
}

:‫الطريقة الثانية تعيد الملف الذي سنستخدمه لحفظ أو استرجاع الملفات كما يلي‬
Future <File> AppFile() async {
final file = await AppPath();
return new File('$file/dart.txt');
}

:‫الطريقة الثالثة والتي تكتب على الملف تكون كما يلي‬


Future <File> writefile(String data) async
{
final file = await(AppFile());
return writefile('$data');
}

:‫الطريقة الرابعة وهي قراءة البيانات من ملف‬


Future <String> readFile()async {
try {
final File = await (AppFile());
String data = await (File.readAsString());
} catch (e) {
return 'there is N file';
}
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
90
‫ ويستدعي‬save ‫الآن سوف نقوم ببناء كود يقوم بحفظ نص معين داخل ملف عن الضغط على الزر‬
.‫ كما في الصورة‬load ‫ عند الضغط على زر‬label ‫الأمر ويظهره على‬

main.dart
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:path_provider/path_provider.dart';

Future<String> AppPath() async {


final path = await getApplicationDocumentsDirectory();
return path.path;
}

Future<File> AppFile() async {


final file = await AppPath();
return new File('$file/dart.txt');
}

Future<File> WriteFile(String data) async {


final file = await (AppFile());
return file.writeAsString('$data');
}

Future<String> ReadFile() async {


try {
final file = await (AppFile());
String data = await (file.readAsString());
return data;
} catch (e) {
return 'there is N file';
}
}

final TextEditingController input = new TextEditingController();


final TextEditingController output = new TextEditingController();

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
91
void main() async {
//List data = await getData();
runApp(new MaterialApp(
title: 'Json Example',
home: new Scaffold(
appBar: AppBar(title: new Text('Files')),
body: new Center(
child: new Column(
children: <Widget>[
new TextField(controller: input),
new TextField(controller: output),
RaisedButton(
child: new Text("Save"),
onPressed: () {
WriteFile(input.text);
},
),
RaisedButton(
child: new Text("Load"),
onPressed: () async {
output.text = await ReadFile();
})
],
)))));
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪92‬‬
‫التفضيلات المشتركة (‪:)Shared Preferences‬‬ ‫‪‬‬
‫التفضيلات المشتركة )‪ (SharedPreferences‬هي مكون يوفر حفظ واسترجاع بيانات أساسية‬
‫بسيطة ‪.‬كل معلومة يتم حفظها يجب أن تكون على شكل مفتاح وقيمة )‪ (key-value‬بحيث‬
‫يحدد المفتاح )‪ (key‬اسم مميز ليتم استعادة القيمة )‪ (value‬فيما بعد عن طريقه ‪.‬يمكن‬
‫استخدام التفضيلات المشتركة لحفظ بيانات من الأنواع البدائية )‪(primitive types‬وتشمل ‪:‬‬
‫‪booleans, floats, ints, longs, and Strings .‬يتم الاحتفاظ بالبيانات المخزنة من تطبيق ما‬
‫في التفضيلات المشتركة حتى بعد إغلاق هذا التطبيق‪.‬‬

‫ننوه بالبداية أنه كي نستطيع استخدام التخزين بالتفضيلات المشتركة يجب أن نضيف حزمة‬
‫‪ shared_preferences‬إلى مشروعنا بحيث نضيف في الملف ‪ Pubspec.yaml‬وتحت السطر‬
‫‪ Cupertino_icons‬سطر جديد يحتوي‬
‫‪shared_preferences: ^0.5.1+1‬‬
‫ثم نضغط ‪ package get‬كما فعلنا مع حزمة ‪.json‬‬

‫المثال التالي يظهر زر عند الضغط عليه سيجلب قيمة محفوظة بالمفتاح ‪ counter‬ويزيد عليها‬
‫واحد ويطبعها على الكونسول ثم يعيد حفظها ‪ ,‬واجهة البرنامج هي‪:‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
93
main.dart
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
runApp(MaterialApp(
home: Scaffold(
body: Center(
child: RaisedButton(
onPressed: _incrementCounter,
child: Text('Increment Counter'),
),
),
),
));
}

_incrementCounter() async {

SharedPreferences prefs = await SharedPreferences.getInstance();


int counter = (prefs.getInt('counter') ?? 0) + 1;
print('Pressed $counter times.');
await prefs.setInt('counter', counter);
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪94‬‬
‫قواعد البيانات ‪:Sqlite‬‬ ‫‪‬‬
‫تعتبر ‪( SQLite‬اس كيو لايت ) نظام إدارة قواعد بيانات علائقية مثل ‪ ( MySQL‬و‬
‫)‪ PostgreSQL‬مضمنة في مكتبة مبرمجة بلغة ‪ C‬صغيرة الحجم تقريبا ‪ 511‬كليوبايت‪.‬‬
‫وخلافا لأنظمة إدارة قواعد البيانات التي تتبع نظام ( عميل ‪ -‬خادم ) فإن محرك اس كيو لايت غير‬
‫مستقل عن البرنامج التي يتخاطب ويتواصل معه‪ .‬وبدلا عن ذلك في مكتبة اس كيو لايت تربط‬
‫بداخل ذلك البرنامج و هكذا تصبح متكاملة مع البرنامج‪ .‬ويقوم البرنامج باستدعاء وظائف اس كيو‬
‫لايت بواسطة باستدعاءات داخلية بسيطة مما يقلل الزمن التأخير في الوصول إلى قاعدة البيانات‪.‬‬
‫قاعدة البيانات اس كيو لايت تحفظ البيانات و التعريفات والجداول في ملف واحد (قابل للنقل بين‬
‫أنظمة التشغيل) على الجهاز المستضيف‪ .‬وهذا التصميم البسيط يسمح بقفل ملف قاعدة البيانات‬
‫عند بداية عملية نقل البيانات‪.‬‬
‫اس كيو لايت طورها الدكتور ريتشارد هب ‪ ,‬و يقدم و يبيع دورات تعلمية عليها و يقدم عقود الدعم‬
‫الفني و الإضافات مثل الضغط و التشفير‪.‬‬
‫ومصدر قاعدة البيانات اس كيو لايت مرخص تحت الترخيص الملكية العامة ‪, public domain‬‬
‫بحيث يمكنك من استعمالها بحرية من قبل أي شخص لأي غرض كان‪.‬‬

‫مميزات‪SQLite‬‬
‫‪-‬دعم معظم مقاييس ‪ SQL-92‬والتي شملت المناقلات قاعدة البيانات والتي تحوي على ثلاثة‬
‫مميزات ‪ Atomicity‬وتعي قدرة قاعدة البينات على إنجاز كافة المهام أو عدم انجازها بالكامل‬
‫مثل القدرة على نقل الودائع بشكل كامل أو فشلها بالكامل بسبب أي سبب من الأسباب‪.‬‬
‫الميزة الثانية ‪ isolated‬وهي تعني قدرة التطبيق على جعل المناقلة تظهر منفصلة عن بقية‬
‫العمليات‪ ,‬وهذا يعني أنه لا توجد عملية خارج المناقلة تستطيع باي شكل من الأشكال رؤية البيانات‬
‫في وسط المناقلة‪.‬‬
‫الميزة الثالثة ‪ durable‬وهي تعني ضمان أن المناقلات التي تمت بنجاح تبقى حية باستمرار ولا‬
‫تلغى بسبب فشل النظام‪ ,‬مثال ذلك إذا أخبر نظام قواعد البيانات لحجز الرحلات بأن مقعد ما حجز‬
‫بنجاح فإن المقعد سيبقى محجوزا حتى لو انهار النظام‪.‬‬
‫‪-‬صغر حجمها‪.‬‬
‫‪-‬سهولة التركيب‪.‬‬
‫‪-‬سهولة نقل البيانات من مزود إلى آخر‪.‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪95‬‬
‫‪-‬لا توجد مشاكل بالترميز لا سيما مع اللغة العربية‪.‬‬
‫‪-‬قاعدة البيانات عبارة عن ملف واحد فقط‪.‬‬
‫‪-‬تدعم حجم قاعدة البيانات إلى ‪ 1‬تيرابايت (‪ 1142‬جيجابايت) ‪ -‬ماقبل الإصدارة ‪ 1.2‬كان الحد‬
‫الأقصى‪ 1 :‬جيجابايت‪.‬‬
‫‪(-‬شيفرة الاتصال والاستعلام بها سهلة( مشابهة لـ ‪ MySQL‬على نحو أبسط‪.‬‬
‫‪-‬يمكن استخدامها على المواقع التي لا تدعم‪MySQL.‬‬

‫استدعاء مكتبة ‪:SQLite‬‬


‫نفتح الملف ‪ pubspec.yaml‬من ضمن ملفات المشروع‪.‬‬ ‫‪‬‬
‫نضيف السطر ‪ sqflite: any‬ثم نضغط ‪.packages get‬‬ ‫‪‬‬

‫هرمية العمل‪:‬‬
‫‪ N-tier‬او الـ‪ Frame Entity‬للتعامل مع قواعد‬ ‫سوف نعتمد في هذا الكتاب على هرمية‬
‫البيانات ‪ SQlite‬والتي تعتبر من أفضل وابسط هرميات هندسة البرمجيات والتي تعتمد على‬
‫تقسيم الكود إلى طبقات حسب وظيفة كل جزء من الكود‪.‬‬
‫الشكل التالي يبين الهرمية المتبعة‪.‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪96‬‬
‫كما ننشئ ملفات المشروع بشكل يوافق الهرمية كما يلي‪:‬‬

‫شرح الطبقات‪:‬‬
‫الطبقة ‪ :DAL‬يتم وضع فيها كل كافة توابع الاتصال مع قاعدة البيانات بالإضافة لتابع إنشاء‬
‫القاعدة أول مرة‪.‬‬
‫الطبقة ‪ :BL‬يتم وضع فيها كل الأصناف والتوابع التي تقوم بالعمليات من نوع ‪ CRUD‬والتي هي‬
‫(‪ )create, read, update, delete‬أي جميع العمليات التي يمكن تطبيقها على قاعدة البيانات‬
‫لصنف معين مثلا (مستخدم‪ ,‬موظف‪ ,‬مخزن)‪.‬‬
‫الطبقة ‪ :PL‬يتم فيها بناء واجهة المستخدم التي ستتعامل مع الجدول والأصناف المقابلين لها‪.‬‬
‫الطبقة ‪ :Models‬هي طبقة مساعدة تسهل التعامل مع بيانات نوع الجداول فمثلا لدي جدول‬
‫الموظفين فهنا أنشأنا في طبقة الـ ‪ Models‬صنف موظف بخصائص معينة‪.‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
97
‫الآن لنطبق هذه الهرمي بشكل مفصل وعلى جدول واحد هو جدول المستخدمين ولتسهيل الأمر‬
)password ‫ كلمة مرور‬,username ‫ اسم مستخدم‬, Id ‫فرضنا لديهم فقط (معرف مستخدم‬
:‫ كما يلي‬user ‫بالبداية ننشئ الصنف‬
User.dart
class User {
int _Id;
String _Username;
String _Password;
User(this._Id, this._Username, this._Password);

int getId() => _Id;


set Id(id) => _Id = id;

String getUsername() {
return _Username;
}

setUsername(String username) {
_Username = username;
}

String getPassword() {
return _Password;
}

setPassword(String password) {
_Password = password;
}
}

‫ والذي يقوم بجميع عمليات الاتصال والإنشاء‬DataBaseHelper.dart ‫ تحوي الصنف‬DAL ‫الطبقة‬


.‫لقاعدة البيانات‬
.‫سنقوم بشرح كل جزء من الكود على حدى‬
:‫ حيث نجد في بداية الصنف استدعاء لعدة مكتبات كما في الكود‬,‫أولا استدعاء المكتبات‬
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

‫والتي ستساعدنا في التعامل مع الملفات – قواعد البيانات و التزامن التابع الأول الذي سنتحدث عنه‬
getdb ‫وهو‬
static Future<Database> getdb() async {
if (_db != null) {
return _db;
} else {
_db = await OpenDb();
return _db;
}
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
98
‫والذي مهمته أن يقوم بإرجاع قاعدة البيانات في حال كانت موجودة أما في حال كانت غير موجودة‬
OpenDb ‫سيقوم بتحميلها مستدعيا تابع اخر وهو‬
static Future<Database> OpenDb() async {
Directory dir = await getApplicationDocumentsDirectory();
String path = join(dir.path, 'Userdb.db');

var Userdb = await openDatabase(path, version: 1, onCreate:


_FirstCreate);

return Userdb;
}

‫ من مسارها في حال وجودها مسبقا‬Database ‫وهذا بدوره سيقوم بالتقاط مسار التطبيق ثم تحميل‬
‫ والذي يقوم بإنشاء قاعدة البيانات‬FirstCreate ‫وعدا ذلك يقوم باستدعاء مرة ثانية لتابع هو‬
.‫المطلوبة‬
static void _FirstCreate(Database db, int version) async {
var CreateUserTableStatment =
"create table User(Id INTEGER PRIMARY KEY AUTOINCREMENT NOT
NULL,Username Text,Password Text)";

await db.execute(CreateUserTableStatment);
}

:‫والتابع الكلي يكون كما يلي‬


DataBaseHelper.dart
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'dart:io';

class DataBaseHelper {
static Database _db;

static Future<Database> getdb() async {


if (_db != null) {
return _db;
} else {
_db = await OpenDb();
return _db;
}
}

static Future<Database> OpenDb() async {


Directory dir = await getApplicationDocumentsDirectory();
String path = join(dir.path, 'Userdb.db');

var Userdb = await openDatabase(path, version: 1, onCreate:


_FirstCreate);

return Userdb;
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
99
static void Restart() async {
Directory dir = await getApplicationDocumentsDirectory();
String path = join(dir.path, 'Userdb.db');
File f = new File(path);

if (!f.existsSync()) {
deleteDatabase(path);
print("DataBase has deleted");
}
}

static void _FirstCreate(Database db, int version) async {


var CreateUserTableStatment =
"create table User(Id INTEGER PRIMARY KEY AUTOINCREMENT NOT
NULL,Username Text,Password Text)";

await db.execute(CreateUserTableStatment);
}

static Future<void> CloseDb() async {


var db = await getdb();
db.close();
_db = null;
}
}

‫إلى هنا نكون قد بنينا تابع مهمته الاتصال والتحكم بقاعدة المعطيات والمطلوب الآن بناء تابع‬
‫ والذي مهمته إجراء العمليات على قاعدة البيانات كما ذكرنا وهو موجود ضمن الطبقة‬UserAPI
:‫ ويحوي جميع التوابع الخاصة بالتعامل مع المستخدم كما يلي‬BL

UserAPI.dart
import 'package:flutter_app2/DAL/DataBaseHelper.dart';
import 'package:flutter_app2/Models/User.dart';

class UserAPI {
static Future<int> AddUser(User user) async {
var db = await DataBaseHelper.getdb();
Map<String, dynamic> UserMap = new Map();
UserMap["Username"] = user.getUsername();
UserMap["Password"] = user.getPassword();
int result = await db.insert("User", UserMap);

return 0;
}

static Future<List> GetAllUser() async {


var db = await DataBaseHelper.getdb();
String SelectAllUserStatment = "Select * from User";
List result = await db.rawQuery(SelectAllUserStatment);

return result.toList();
}

static Future<int> EditUser(User user) async {


var db = await DataBaseHelper.getdb();

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
100
Map<String, dynamic> UserMap = new Map();
UserMap["Username"] = user.getUsername();
UserMap["Password"] = user.getPassword();
UserMap["Id"] = user.getId();
int result = await db
.update("user", UserMap, where: "Id=?", whereArgs:
[user.getId()]);

return result;
}

static Future<int> DeleteUser(int Id) async {


var db = await DataBaseHelper.getdb();

int result = await db.delete("user", where: "Id=${Id}");


return result;
}

static Future<void> DeleteAllUser() async {


var db = await DataBaseHelper.getdb();
db.execute("delete from user");
}
}

‫ وعند طلبه للبيانات نضع‬async ‫يجب أن نذكر أن أي تابع ينتظر أمرا من خارج التطبيق نضع له‬
.Future Method ‫ ويكون مستقبلي أي‬await
:‫ فتكون كما يلي‬PL ‫أما بخصوص الطبقة‬
MyUi.dart
import 'package:flutter/material.dart';
import 'package:flutter_app2/BL/UserAPI.dart';
import 'package:flutter_app2/Models/User.dart';
import 'package:flutter_app2/AddEditUIFile.dart';

class MyListView extends StatefulWidget {


@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _MyList();
}
}

class _MyList extends State<MyListView> {


List Users = new List();

@override
void initState() {
super.initState();
UserAPI.GetAllUser().then((usrs) {
setState(() {
usrs.forEach((usr) {
Users.add(usr);
});
});
});
}

@override
‫تأليف فيصل الأسود | مهندس برمجيات‬
Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
101
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
floatingActionButton: new FloatingActionButton(
onPressed: () => _NavigateToAdd(context),
child: new Icon(Icons.add),
backgroundColor: Colors.blue),
appBar: new AppBar(
iconTheme: new IconThemeData(
color: Colors.green,
),
title: new Text('AppBar Title'),
backgroundColor: Colors.greenAccent,
),
body: new Column(
children: <Widget>[
new TextField(
decoration: new InputDecoration(hintText:
"Search")),
new Expanded(
child: new ListView.builder(
itemCount: Users.length,
itemBuilder: (BuildContext context, int
position) {
return new Card(
color: Colors.white,
child: new ListTile(
title: new Text(

"${Users[position]["Username"]}"),
subtitle:
new Text("Id:
${Users[position]["Id"]}"),
trailing: new Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
new IconButton(
onPressed: () {
_NavigateToEdit(context,
position);
},
icon: new Icon(Icons.edit,
color:
Colors.lightBlue)),
new IconButton(
onPressed: () =>
_deleteUser(context,
position),
icon: new Icon(Icons.delete,
color:
Colors.redAccent)),
],
)));
}))
],
)));
}

void _NavigateToEdit(BuildContext context, int position) async {


String result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddEditUI(
"Edit",
new User(Users[position]["Id"],

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
102
Users[position]["Username"],
Users[position]["Password"]))));
RefreshList();
}

void _NavigateToAdd(BuildContext context) async {


String result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddEditUI("Add", new User(0, "",
""))));
RefreshList();
}

void RefreshList() {
Users.clear();
UserAPI.GetAllUser().then((usrs) {
setState(() {
usrs.forEach((usr) {
Users.add(usr);
});
});
});
}

void _deleteUser(BuildContext context, int position) {


UserAPI.DeleteUser(Users[position]["Id"]);
setState(() {
Users.removeAt(position);
});
}

void _AddUser(BuildContext context, int position) {


UserAPI.DeleteUser(Users[position]["Id"]);
setState(() {
Users.removeAt(position);
});
}

void _EditUser(BuildContext context, int position) {


UserAPI.DeleteUser(Users[position]["Id"]);
setState(() {
Users.removeAt(position);
});
}
}

Main.dart
import 'package:flutter/material.dart';
import 'MyUi.dart';
import 'package:flutter_app2/MyUi.dart';

List Users;
void main() async {
runApp(new MaterialApp(home: new MyListView()));
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
103
:‫ويكون المخطط لمثالنا كما يلي‬

:‫صنف واجهة الإضافة والتعديل كما يلي‬


AddEditUIFile.dart
import 'package:flutter/material.dart';
import 'package:flutter_app2/BL/UserAPI.dart';
import 'package:flutter_app2/Models/User.dart';

class AddEditUI extends StatefulWidget {


User user;
String command;
AddEditUI(this.command, this.user);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _AddEditUI(command, user);
}
}

class _AddEditUI extends State<AddEditUI> {


User user;
String command;
_AddEditUI(this.command, this.user);

final _txtUsername = new TextEditingController();


final _txtPassword = new TextEditingController();

@override
void initState() {
super.initState();

if (command == "Edit") {
setState(() {
_txtUsername.text = user.getUsername();
_txtPassword.text = user.getPassword();
});
}
‫تأليف فيصل الأسود | مهندس برمجيات‬
Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
104
}

@override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text(command),
backgroundColor: Colors.greenAccent,
),
body: new Center(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Padding(
padding: EdgeInsets.all(5),
),
new Text(
"Username:",
style: TextStyle(
fontSize: 25,
),
textAlign: TextAlign.left,
),
Padding(
padding: EdgeInsets.all(5),
),
new TextField(
controller: _txtUsername,
decoration: new InputDecoration(hintText: "Username"),
style: TextStyle(fontSize: 20, color: Colors.black),
),
Padding(
padding: EdgeInsets.all(5),
),
Padding(
padding: EdgeInsets.all(5),
),
new Text(
"Password:",
style: TextStyle(
fontSize: 25,
),
textAlign: TextAlign.left,
),
Padding(
padding: EdgeInsets.all(5),
),
new TextField(
controller: _txtPassword,
decoration: new InputDecoration(hintText: "Password"),
style: TextStyle(fontSize: 20, color: Colors.black),
),
Padding(
padding: EdgeInsets.all(5),
),
new RaisedButton(
onPressed: () => AddOrEdit(),
child: new Text(command),
color: Colors.greenAccent,
)
],
),
),
));

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
105
}

void AddOrEdit() {
if (_txtUsername.text.length > 0 && _txtPassword.text.length > 0) {
if (command == "Add") {
UserAPI.AddUser(new User(0, _txtUsername.text,
_txtPassword.text));
} else {
UserAPI.EditUser(
new User(user.getId(), _txtUsername.text,
_txtPassword.text));
}
Navigator.pop(context);
}
}
}

:‫وواجهة التطبيق بالكامل تكون‬

‫واجهة عرض المستخدمين‬ ‫واجهة الإضافة والتعديل‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪106‬‬
‫التعامل مع الصور‬

‫مقدمة‪:‬‬
‫تتيح لنا فلاتر بسهولة الية للتعامل مع الصور سواء اكانت مخزنة في الاستديو او التقاطها من‬
‫الكاميرا مباشرة‪.‬‬
‫كما يوفر المحاكي في الاندرويد استديو كاميرا افتراضية وهمية لاختبار عمل الكاميرا داخل التطبيق‪.‬‬

‫استدعاء مكتبة ‪:picker‬‬

‫قد يتيح لنا الصنف ‪ imagepicker‬في ‪ flutter‬التعامل وجلب أي نوع من الصور أو التقاطها من‬
‫الكاميرا ويجب أن نعلم أن التوابع ضمنه متزامنة لذلك يجب تكتب أيضا ضمن توابع متزامنة‪.‬‬

‫مثال تطبيقي‪:‬‬
‫التابع التالي بسيط جدا يتيح عند استدعاءه جلب صورة من الاستديو ووضعها ضمن كائن ‪ file‬ومن‬
‫ثم وضعه في ‪ widget‬لعرض الصور‪.‬‬
‫{‪picker() async‬‬
‫;)‪File img =await ImagePicker.pickImage(source: ImageSource.gallery‬‬
‫{)‪if(img !=null‬‬
‫;‪image=img‬‬
‫{ )((‪setState‬‬

‫;)}‬
‫}‬
‫}‬
‫نلاحظ أن التابع تعامل مع ملفات النظام وهنا يجب أن تقوم بتضمين مكتبة الـ ‪ io‬في استدعاء‬
‫المكتبات كما يلي‪:‬‬
‫;'‪import 'package:flutter/material.dart‬‬
‫;'‪import 'dart:io‬‬
‫;'‪import 'package:image_picker/image_picker.dart‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
107
‫الان نكتب ملف الواجهة كاملا وعند الضغط على الزر ضمنها سيأخذنا إلى الاستديو لجلب الملف‬
:‫ كما في الكود التالي‬image ‫ لإظهار الصور وهي‬widget ‫وسيضعه في‬
CameraUi.dart
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:image_picker/image_picker.dart';

class CameraUi extends StatefulWidget {


@override
State<StatefulWidget> createState() {
// TODO: implement createState
return _CameraUi();
}
}

class _CameraUi extends State<CameraUi> {


File image;
picker() async {
File img = await ImagePicker.pickImage(source:
ImageSource.gallery);
if (img != null) {
image = img;
setState(() {});
}
}

@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(title: new Text("ImageDemo")),
body: new Center(
child: image == null ? Text("There Is No Image") :
Image.file(image),
),
floatingActionButton: new FloatingActionButton(
onPressed: picker,
child: new Icon(Icons.add),
),
);
}
}

:‫ بالشكل التالي‬main ‫والتابع‬


main.dart
import 'package:flutter/material.dart';
import 'package:image_demo/CameraDeal.dart';

void main() {
runApp(new MaterialApp(home: new CameraUi(),));
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪108‬‬
‫الآن ماذا لو أردنا أن نتعامل مع الكاميرا بشكل مباشر هنا يجب أن نستدعي نفس الصنف والتابع‬
‫لكن بدلا من استدعاءه من أجل الاستديو نختار الـ ‪ camera‬ويكون بالشكل التالي‬
‫{ ‪picker() async‬‬
‫;)‪File img = await ImagePicker.pickImage(source: ImageSource.camera‬‬
‫{ )‪if (img != null‬‬
‫;‪image = img‬‬
‫;)}{ )((‪setState‬‬
‫}‬
‫}‬

‫نود أن ننوه أن المحاكي يتيح لنا بيئة تصوير افتراضية فعند تشغيل البرنامج والتقاط صورة‬
‫باستخدام الكاميرا ستظهر بالشكل التالي وكأن الكاميرا تعمل‪:‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
109
‫الاشعارات الداخلية‬
Flutter-local-notification ‫ إمكانية اشعار المستخدم بسلاسة كبيرة عن طريق حزمة‬flutter ‫يتيح‬
:‫والتي تقوم عند حدث معين بإشعار المستخدم بالشكل التالي‬

:‫مثال تطبيقي‬
‫ كذلك‬,‫في مثالنا التالي سنقوم بإنشاء زر عند الضغط عليه سيقوم بإشعارنا بانه تم الضغط على الزر‬
.‫سنقوم بوضع الاستجابة عند الضغط على هذا الاشعار‬

IOS ‫ و‬Android ‫التابع التالي هو لتهيئة عمليات الاستشعار لكلا النظامين‬

FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;

@override
void initState() {
// TODO: implement initState
super.initState();
flutterLocalNotificationsPlugin = new
FlutterLocalNotificationsPlugin();
var android = new
AndroidInitializationSettings('@mipmap/ic_launcher');
var ios = new IOSInitializationSettings();
var initSettings = new InitializationSettings(android, ios);
flutterLocalNotificationsPlugin.initialize(
initSettings, onSelectNotification: clickOnNotification );
}

:‫ فيمكن بناءه بعدة طرق ولكننا سنبنيه بالشكل التالي‬showNotification ‫أما التابع‬
showNotification() async{
var android = new AndroidNotificationDetails(
"channelId", "channelName", "channelDescription"
,priority: Priority.High,importance: Importance.Max);
var iOS = new IOSNotificationDetails();

var platform = new NotificationDetails(android, iOS);


await flutterLocalNotificationsPlugin.show(
0, 'Hello World, 'StackOver Flow Arabi, platform,payload:
Notification);

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
110
‫نلاحظ في الصورة النصوص المكتوبة في الأعلى ومواضعها في الإشعار أما في خصوص العبارة‬
‫ فهي ستظهر عند الضغط على هذا الاشعار ويكون تنفيذ طلب محتوى الاشعار بالتابع‬Payload
‫التالي‬
showNotification() async {
var android = new AndroidNotificationDetails(
"channelId", "channelName", "channelDescription",
priority: Priority.High, importance: Importance.Max);
var iOS = new IOSNotificationDetails();
print("jhj");
var platform = new NotificationDetails(android, iOS);
await flutterLocalNotificationsPlugin.show(
0, 'Notification 1', 'Notification 2', platform,
payload: 'Send Messages');
}

.ً‫وهكذا نكون قد فهمنا كل جزء من أجزاء البرنامج المطلوب ويكون برنامجنا كاملا‬
import
"package:flutter_local_notifications/flutter_local_notifications.dart";
import 'package:flutter/material.dart';
import 'dart:async';

class MyHomePage extends StatefulWidget {


@override
_MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {


FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;

@override
void initState() {
// TODO: implement initState
super.initState();
flutterLocalNotificationsPlugin = new
FlutterLocalNotificationsPlugin();
var android = new
AndroidInitializationSettings('@mipmap/ic_launcher');
var ios = new IOSInitializationSettings();
var initSettings = new InitializationSettings(android, ios);
flutterLocalNotificationsPlugin.initialize(initSettings,
onSelectNotification: clickOnNotification);

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
111
}

Future clickOnNotification(String payload) {


debugPrint('print payload : $payload');
showDialog(
context: context,
builder: (_) => AlertDialog(
title: new Text('Notification'),
content: new Text('$payload'),
),
);
}

@override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text('Local Notification'),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'Click Button',
),
new RaisedButton(
child: Text('Click me'), onPressed: showNotification)
],
),
),
);
}

showNotification() async {
var android = new AndroidNotificationDetails(
"channelId", "channelName", "channelDescription",
priority: Priority.High, importance: Importance.Max);
var iOS = new IOSNotificationDetails();
print("jhj");
var platform = new NotificationDetails(android, iOS);
await flutterLocalNotificationsPlugin.show(
0, 'Notification 1', 'Notification 2', platform,
payload: 'Send Messages');
}
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪112‬‬
‫الحركة ‪Animation‬‬
‫مقدمة‪:‬‬
‫تقدم حزمة ‪ Animation Flutter‬مكتبات جاهزة معها لإنجاز حركات قوية‪ ,‬حيث تعطي الحركة‬
‫للواجهة قوة وحيوية‪.‬‬

‫مفهوم الحركات‪:‬‬
‫معظم الحركات في ‪ Flutter‬تعتمد على الصنف ‪ Animation‬الموجود في المكتبة المضمنة‬
‫افتراضيا‪:‬‬
‫'‪'package: Flutter/animation.dart‬‬
‫والتي تحوي الكثير من الحركات الجاهزة والتوابع القوية‪.‬‬

‫إن النمط الهيكلي لأي حركة يتم بتقسيم متطلباتها إلى ثلاث أجزاء كما في المخطط التالي‪:‬‬

‫التصريح‬

‫اإلعداد‬

‫التنفيذ‬

‫وأيضا يجب أن نذكر أن لكل حركة متحكم وهو كائن من نوع ‪ AnimationController‬والذي يقوم‬
‫بالتحكم بالحركة من (بدء – توقف – إعادة)‬
‫سنستعرض الآن هرمية بناء الحركات في ‪ Flutter‬وسننتقل لأمثلة ننفذ فيها عدة حركات‪.‬‬
‫في البداية نصرح عن الكائنات الخاصة بالحركة كما يلي‪:‬‬
‫;‪Animation animation‬‬
‫;‪AnimationController animationController‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
113
‫ وسيتم ضبط فيه كل اعدادات‬initAnimation ‫الان سنقوم بتجهيز حركتنا في تابع خاص سنسميه‬
:‫الحركة المطلوبة‬
void initAnimation(){
animationController=AnimationController(duration: Duration(seconds:
1),vsync: this);
flipAnimation=Tween(begin: 0.0,end:
1.0).animate(CurvedAnimation(parent: animationController, curve:
Curves.fastOutSlowIn));
animationController.forward();
}

:initState ‫وسنقوم باستدعاء هذا التابع ضمن التابع الافتراضي‬


void initState(){
super.initState();
initAnimation();
}

:‫السطر الأول‬
animationController=AnimationController(duration: Duration(seconds:
1),vsync: this);
.‫وذلك يقوم بتعيين المدة المطلوبة التي ستستغرقها الحركة وفي حالتنا نحدد ثانية واحدة‬

:‫السطر الثاني وهو مهم جدا والذي يعرف مكان بدء وانتهاء الحركة وكذلك طريقة دخولها للشاشة‬
flipAnimation=Tween(begin: 0.0,end:
1.0).animate(CurvedAnimation(parent: animationController, curve:
Curves.fastOutSlowIn));

‫ ويمكنك الاطلاع على الأنواع‬fastOutSlowIn ‫ ودخول نوع‬2.1 ‫ إلى النقطة إلى‬1.1 ‫بدأنا في النقطة‬
.‫الأخرى بالاعتماد على الجدول‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪114‬‬
‫التعليمة الأخيرة هي‪:‬‬
‫;)(‪AnimationController.Forward‬‬

‫والتي تقوم ببدء الحركة (أي تنفيذها)‪.‬‬


‫ويجب أن نعلم أن أي عنصر سيقوم بالحركة سيتم وضعه ضمن ‪ Animation Builder‬الأمثلة‬
‫التالية ستوضح الحركة بشكل أكثر وضوحا‪.‬‬

‫مثال تطبيقي‪:‬‬
‫ليكن لدينا الواجهة التالية ونريد عند فتح التطبيق دخول الصفحة بالشكل التالي‪:‬‬

‫أي أن تنزل من الأعلى إلى الأسفل‬


‫هنا سنبني الصنف ‪ Flip‬و ‪ _Flip‬والذين سيقومان بتحقيق الواجهة المطلوبة‪.‬‬
‫الكود التالي لكود كامل الواجهة‪:‬‬
‫;'‪import 'package:flutter/material.dart‬‬
‫;'‪import 'package:flutter/animation.dart‬‬
‫‪class Flip extends StatefulWidget‬‬
‫{‬
‫‪@override‬‬
‫{ )(‪State<StatefulWidget> createState‬‬
‫‪// TODO: implement createState‬‬
‫;)(‪return new _Flip‬‬
‫}‬

‫}‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
115
class _Flip extends State<Flip> with SingleTickerProviderStateMixin{
Animation flipAnimation;
AnimationController animationController;
@override
void initState(){
super.initState();
initAnimation();
}
void initAnimation(){
animationController=AnimationController(duration: Duration(seconds:
1),vsync: this);
flipAnimation=Tween(begin: 0.0,end:
1.0).animate(CurvedAnimation(parent: animationController, curve:
Curves.fastOutSlowIn));
animationController.forward();
}
@override
Widget build(BuildContext context) {
// TODO: implement build

return AnimatedBuilder(
animation: animationController,
builder: (BuildContext context,Widget chile){
return UiWithoutAnimation();
},
);
}

Widget UiWithoutAnimation()
{
final double pi=3.14;
final double width=MediaQuery.of(context).size.width;
return new Scaffold(
body: Transform(
transform:
Matrix4.identity()..rotateX(2*pi*flipAnimation.value),
child: new Center(

child: new Card(


child: new Column(
children: <Widget>[
new Padding(padding: EdgeInsets.only(bottom: 100)),
new Text("LogIn",style: TextStyle(fontSize: 50),),
new Padding(padding: EdgeInsets.only(bottom: 75)),
new Text("Username",style: TextStyle(fontSize:
20,color: Colors.green),),
new TextField(style: TextStyle(color: Colors.black)),
new Padding(padding: EdgeInsets.only(bottom: 5)),
new Text("Password",style: TextStyle(fontSize:
20,color: Colors.green),),
new TextField(style: TextStyle(color: Colors.black)),

],
),
)
),
),
);
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪116‬‬
‫نلاحظ أننا بنينا الواجهة في تابع منفصل هو ‪ UiWithoutAnimation‬كما يجب أن نذكر أن‬
‫الحركة تمت عن طريق وضع التحويل التالي‪:‬‬
‫)‪transform: Matrix4.identity()..rotateX(2*pi*flipAnimation.value‬‬

‫بتغير المحاور للدوران مثل ‪ rotate y, rotate z‬سنحصل على حركات جديدة‪.‬‬
‫وتم استدعاء الواجهة بالكود الرئيسي ‪:main‬‬
‫;'‪import 'package:flutter/material.dart‬‬
‫;'‪import 'package:my_project/EnterSlideAnimation.dart‬‬
‫;'‪import 'package:my_project/FlipAnimation.dart‬‬
‫;'‪import 'HideShowAnimation.dart‬‬
‫;))‪void main() => runApp(MaterialApp(home: Flip(),‬‬

‫سنستعرض اآلن أمثلة وعلى عجالة مع العلم أن كل واجهة يمكن استدعاءها بالصنف ‪ main‬كما شاهدنا‬
‫لذلك لن نكرر كود التابع ‪ main‬كل مرة‪.‬‬

‫مثال تطبيقي‪:‬‬
‫اكتب الكود اللازم لبناء الواجهة التالية‪:‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪117‬‬
‫مع إمكانية تحريكها دخولا للشاشة كما يلي‪:‬‬

‫ببساطة سوف نستخدم التابع ‪ translate‬والذي يقوم بتحريك الواجهة كما في الكود‪:‬‬
‫‪transform: Matrix4.translationValues(animation.value*width, 0.0, 0.0),‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪118‬‬
‫مثال تطبيقي‪:‬‬
‫اكتب الكود اللازم لبناء الواجهة التالية بحيث عندما نضغط ضغطة واحدة على الصورة سوف تنزل‬
‫كما في الشكل‪.‬‬

‫وعندما نضغط ضغطتين عليها مرة أخرى ستعود كما كانت‪.‬‬


‫وهنا سنستخدم الأحداث ونتحكم بالحركة عن طريق ‪Animation Controller‬‬
‫يجب الذكر أن عملية إضافة الصور في ‪ Flutter‬تتم بالشكل التالي‪:‬‬
‫النقر بالزر الأيمن على مجلد المشروع واختيار ‪.show in explorer‬‬ ‫‪‬‬
‫الدخول لمجلد المشروع وإنشاء مجلد اسمه ‪ assets‬ومن ثم ننشئ مجلد اخر اسمه ‪Images‬‬ ‫‪‬‬
‫بداخله ونضع الصور فيه‪.‬‬
‫نذهب إلى الملف ‪ Pubspec.Yaml‬في ملفات المشروع‪.‬‬ ‫‪‬‬
‫نضيف الصورة المطلوبة في الملف تحت كلمة ‪ Flutter:‬كما في الشكل مع مراعاة‬ ‫‪‬‬
‫المسافات هنا‪:‬‬

‫والآن نقوم بإنشاء كود الواجهة المطلوبة ويكون بالشكل التالي‪:‬‬


‫;'‪import 'package:flutter/material.dart‬‬
‫;'‪import 'package:flutter/animation.dart‬‬
‫‪class HideShow extends StatefulWidget‬‬
‫{‬
‫‪@override‬‬
‫{ )(‪State<StatefulWidget> createState‬‬
‫‪// TODO: implement createState‬‬
‫;)(‪return new _HideShow‬‬
‫}‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
119
}
class _HideShow extends State<HideShow> with
SingleTickerProviderStateMixin{
Animation hideShowAnimation;
AnimationController animationController;
@override
void initState(){
super.initState();
animationController=AnimationController(duration: Duration(seconds:
1),vsync: this);
hideShowAnimation=Tween(begin: 0.0,end: -
0.15).animate(CurvedAnimation(parent: animationController, curve:
Curves.fastOutSlowIn));
animationController.forward();

}
@override
Widget build(BuildContext context) {
// TODO: implement build

return AnimatedBuilder(
animation: animationController,
builder: (BuildContext context,Widget chile){
return UiWithoutAnimation();
},
);
}

Widget UiWithoutAnimation()
{
final double width=MediaQuery.of(context).size.width;
return new Scaffold(
body: new Center(
child: new Stack(
children: <Widget>[
new Center(
child: Container(
padding: EdgeInsets.all(10.0),
width: 350.0,
height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0)
),
child: new Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new RaisedButton(
child: Text("Hello"),
elevation: 7.0,
color: Colors.lightBlue,
textColor: Colors.white,
onPressed: (){},
),
new RaisedButton(
child: Text("World"),
elevation: 7.0,
color: Colors.lightBlue,
textColor: Colors.white,
onPressed: (){},
)
],
),
),

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
120
),
new Center(
child: GestureDetector(
child: Container(
alignment: Alignment.bottomCenter,
width: 350,height: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15.0),
image: DecorationImage(image:
AssetImage('assets/images/apple.jpg'),fit: BoxFit.cover)

),
transform: Matrix4.translationValues(0.0,
hideShowAnimation.value*width, 0.0),
),
onTap: (){animationController.forward();},
onDoubleTap:(){animationController.reverse();} ,
),
)
],
),
),
);
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪121‬‬
‫رسائل التنبيه السريعة ‪Toast‬‬
‫تعد الرسائل السريعة ‪Toast‬من ابسط الرسائل التي تقدمها التطبيقات للمستخدم وتكون غالباً على‬
‫شكل تنبيه يظهر على الشاشة مدة زمنية ويختفي‪.‬‬
‫فلاتر تقدم لنا في احدى مكتباتها طرق التعامل مع هذا النوع من الرسائل‪.‬‬
‫لكي نستطيع التعامل مع رسائل الـ‪ Toast‬علينا أولا استدعاء المكتبة الخارجية‬
‫‪fluttertoast: ^3.0.1‬‬
‫الى الملف ‪ Pubsec.yaml‬ومن ثم ‪ packages get‬بالإضافة الى استدعاء المكتبة الى صنف العمل‬
‫الذي يريد استخدامه كما يلي‪:‬‬
‫;'‪import 'package:fluttertoast/fluttertoast.dart‬‬

‫يعد استخدام هذا النوع من الرسائل من ابسط الأشياء حيث يكفي فقط استدعاء الطريقة‬
‫‪ showToast‬لإظهار الرسالة كما يمكن ضبط اعدادات خاصة لتلك الرسائل‪:‬‬
‫(‪Fluttertoast.showToast‬‬
‫‪msg: "This is Center Short Toast",‬‬
‫‪toastLength: Toast.LENGTH_SHORT,‬‬
‫‪gravity: ToastGravity.CENTER,‬‬
‫‪timeInSecForIos: 1,‬‬
‫‪backgroundColor: Colors.red,‬‬
‫‪textColor: Colors.white,‬‬
‫‪fontSize: 16.0‬‬
‫;)‬

‫الجدول التالي يظهر اهم الخصائص المستخدمة لإظهار رسائل التنبيه السريعة‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪122‬‬
‫ولإلغاء كافة الرسائل الظاهرة يكون بالتعليمة‪:‬‬
‫)(‪Fluttertoast.cancel‬‬

‫سنستعرض الان بعض رسائل تنبيه سريعة مع خصائص مختلفة‪:‬‬

‫‪gravity:ToastGravity.CENTER‬‬ ‫‪bgColor:Colors.red‬‬

‫‪gravity:ToastGravity.TOP‬‬ ‫‪gravity:ToastGravity.BOTTOM‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪123‬‬
‫قواعد البيانات ‪Firebase‬‬

‫فاير بيز ‪ :Firebase‬هي خدمة قدمتها ‪ Google‬منذ فترة وقد كانت تقتصر فقط على تخزين‬
‫البيانات وبعض الأشياء البسيطة‪ ,‬ولكن في ‪ Google I/O 16‬تم الإعلان عن الكثير من المميزات‬
‫الجديدة والرائعة وأصبحت حديث الكثير من المطورين‬

‫مميزاتها‪:‬‬
‫التحقق ‪ : Authentication‬وهي عملية تسجيل الدخول سواء عن طريق حساب ‪Facebook‬‬ ‫‪‬‬
‫‪ ,Google, Twitter, Email‬وفي نفس الوقت حماية البيانات الموجودة في ‪Database‬‬
‫(بمعنى أنه يمكنك منع أي شخص من استخدام التطبيق دون عملية تسجيل الدخول وهو‬
‫الوضع الافتراضي(‬
‫قواعد بيانات متزامنة ‪ : Real-time Database‬وهي تفيد في تخزين البيانات على السيرفر‬ ‫‪‬‬
‫وأكثر شيء يميزها هي أنها ‪ Real-time‬بمعنى أنه أي تغيير يحصل على الداتا بيز سيتغير‬
‫فوراً في التطبيق كما سنرى في هذا الشرح‪.‬‬
‫التخزين ‪ :Storage‬تخزين الملفات والصور‪.‬‬ ‫‪‬‬
‫الاستضافة ‪ :Hosting‬لاستضافة موقعك على‪. Firebase‬‬ ‫‪‬‬
‫الإشعارات ‪:Notifications‬إرسال إشعارات‪.‬‬ ‫‪‬‬

‫والعديد من المميزات يمكنك تفقدها عند الدخول الى حسابك في ‪ Firebase‬من الجدير بالذكر أن‬
‫هذه الخدمات مجانية (ولكن ببعض الحدود‪ ,‬يمكنك رؤية ماهي الحدود عبر هذا الرابط‬
‫‪https://firebase.google.com‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪124‬‬
‫سنبدأ في هذا الدرس شرح عن ‪ Firebase Database‬وسنحاول مستقبلاً بإذن الله شرح باقي الأمور‬
‫بدايةً يجب أن يكون لديك حساب ‪ Google‬أي حساب ‪.Gmail‬‬
‫نذهب الى موقع ‪ Firebase‬نضغط ‪ Get Start‬ثم نختار ‪Add project‬‬

‫ثم نضع اسم المشروع الذي نريد ونضع الدولة والموافقة على الشروط ثم انشاء‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪125‬‬
‫بعد ذلك سيتم إنشاء المشروع ‪ ,‬نضغط على ‪Add Firebase to Your Android App‬‬

‫بعد ذلك سيطلب منا اسم ال ‪ package‬الخاص بتطبيق الاندرويد‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪126‬‬
‫نقوم بإنشاء تطبيق اندرويد جديد ثم نتوجه الى ‪ AndroidManifest.xml‬لالتقاط اسم الحزمة ونضعها‬
‫كما يظهر في الصورة ثم نضغط ‪:Register App‬‬

‫بعد ذلك نضغط على ‪ Download google-services.json‬وهو ملف إعدادات الذي يربط مشروعنا‬
‫على ‪ Firebase‬بمشروعنا في‪Android Studio‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪127‬‬
‫سيتم تحميل هذا الملف قم بنسخه ثم نتوجه الى ‪ Android Studio‬ونضغط بالزر الأيمن على‬
‫مجلد ‪ app‬ونختار‪Show in Explorer‬‬

‫سيتم فتح مجلد المشروع في حاسوبك نقوم بلصق هذا الملف الذي حملناه في مجلد‪app‬‬

‫الآن يجب علينا إضافة مكتبات ‪ Firebase‬الى مشروعنا في‪Android Studio‬‬

‫ثم نضغط ‪packages get‬‬

‫وفي ملف الصنف الذي سنبني به واجه التخاطب مع ‪ Firebase‬نضيف المكتبات كما يلي‪:‬‬
‫;'‪import 'package:firebase_database/firebase_database.dart‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪128‬‬
‫الان نعود الى واجهة السيرفر ‪ Firebase‬ونضغط على زر ‪ Database‬لإنشاء قاعدة جديدة‬

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

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪129‬‬
‫كما يمكننا ان نرى البيانات التي نملكها في قاعدة بياناتنا من التبويب ‪ Database‬في واجهة‬
‫السيرفر ‪Firebase‬‬

‫الان يجب ان نبدأ برمجياً بعمليات الاتصال بالــ فايربيز الخاصة بنا‪.‬‬
‫ما سنقوم به بالبداية هو تعريف كائن مهمته الاتصال مع قاعدة البيانات الخاصة بنا وذلك كما‬
‫يلي‪:‬‬
‫‪final‬‬
‫;)"‪studentReference=FirebaseDatabase.instance.reference().child("student‬‬

‫هذا التابع اتصل مع قاعدة البيانات الخاصة بنا وانشئ حقل هو ‪ student‬يمثل جدول الطلاب‪.‬‬

‫الخطوة التالية هي ان ننشئ احداث وتوابع تستجيب لها لكي يكون الاتصال مع القاعدة متزامناً‬
‫بحيث أي تغير يحصل في القاعدة سوف ينعكس اثره على تطبيقنا مباشرة‪.‬‬
‫;‪StreamSubscription<Event> _onStudentAddedSubscription‬‬
‫;‪StreamSubscription<Event> _onStudentChangedSubscription‬‬

‫=‪_onStudentAddedSubscription‬‬
‫;)‪studentReference.onchildAdded.listen(_onStudentAdded‬‬

‫=‪_onStudentChangedSubscription‬‬
‫;)‪studentReference.onchildChanged.listen(_onStudentUpdated‬‬

‫في الكود السابق قمنا بإنشاء مراقبين لحدثين لمراقبة الإضافة والتعديل وعند حدوث أي تغيير بهما‬
‫سوف يستدعى التابعين الخاصين بالإضافة والتعديل كما يلي على الترتيب‪:‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
130
void _onStudentAdded(Event event){
setState(() {
itmes.add(new Student.fromSnapShot(event.snapshot));
});
}

void _onStudentChanged(Event event){


var
oldStudentValue=itmes.singleWhere((student)=>student.id==event.snapshot
.key);
setState(() {
items[items.indexOf(oldStudentValue)]=new
Student.fromSnapShot(event.snapshot);
});
}

:‫اما بخصوص تابع الحذف فيكون بالشكل التالي‬


void _deleteStudent(BuildContext context,Student student,int
position) async{
await studentReference.child(student.id).remove().than((_){
setState(() {
items.removeAt(position);
});
});
}

:‫ويكون تابع الواجهة الكلي لاظهار والتعامل مع البيانات كما يلي‬


import 'dart:async';
import 'package:flutter/material.dart';
import 'package:firebase_demo/model/Student.dart';
import 'package:firebase_demo/ui/Student_Screen.dart';
class ListViewStudent extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _ListViewStudent();
}

}
final
studentReference=FirebaseDatabase.instance.reference().child("student")
;
class _ListViewStudent extends State<ListViewStudent>{

List<Student> itmes;
StreamSubscription<Event> _onStudentAddedSubscription;
StreamSubscription<Event> _onStudentChangedSubscription;
@override
initState(){
super.initState();
items=new List();

_onStudentAddedSubscription=studentReference.onchildAdded.listen(_onStu
dentAdded);

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
131
_onStudentChangedSubscription=studentReference.onchildChanged.listen(_o
nStudentUpdated);
}
@override
void dispose(){
super.dispose();
_onStudentAddedSubscription.cancel();
_onStudentChangedSubscription.cancel();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(title: new Text("Firebase"),
centerTitle: true,
backgroundColor: Colors.greenAccent
),
body: Center(
child: ListView.builder(itemCount: itmes.length,
padding: EdgeInsets.only(top: 12.0),
itemBuilder: (context,position){
return new Column(
children: <Widget>[
Divider(height: 6.5,),
ListTile(title: new Text("{$itmes[position].name}",
style: TextStyle(color: Colors.red,fontSize: 25.0),
),
subtitle:new Text("{$itmes[position].name}",
style: TextStyle(color: Colors.red,fontSize: 11.0),
),
leading: Column(
children: <Widget>[
CircleAvatar(backgroundColor: Colors.redAccent,
radius: 16.0,
child: new Text("{$itmes[position].name}"),)
IconButton(icon: Icon(Icons.delete),color:
Colors.lightBlueAccent,onPressed:

()=>_deleteStudent(context,items[position],position),),
IconButton(icon: Icon(Icons.delete),color:
Colors.lightBlueAccent,onPressed:
()=>_onStudentChanged())
],
),
onTap:
()=>_NavigateToStudent(context,items[position],position),
)
],
);
}
),
),
floatingActionButton: FloatingActionButton(onPressed:
(){_createNewStudent();},
child: new Icon(Icons.add),),
);
}
void _onStudentAdded(Event event){
setState(() {
itmes.add(new Student.fromSnapShot(event.snapshot));
});
}
void _onStudentChanged(Event event){
var

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
132
oldStudentValue=itmes.singleWhere((student)=>student.id==event.snapshot
.key);
setState(() {
items[items.indexOf(oldStudentValue)]=new
Student.fromSnapShot(event.snapshot);
});
}
void _deleteStudent(BuildContext context,Student student,int
position) async{
await studentReference.child(student.id).remove().than((_){
setState(() {
items.removeAt(position);
});
});
}
void _NavigateToStudent(BuildContext context,Student student,int
position){
await Navigator.push(context,MaterialPageRoute(builder: (context)
=>StudentScreen(student))
);
}
void _createNewStudent(BuildContext context){
await Navigator.push(context,MaterialPageRoute(builder: (context)
=>StudentScreen(new Student(null, "", "", "", "", "")))
);
}
}

‫_ مهمته هي الانتقال للشاشة التالية والتي تقوم بعرض مكان حقول‬createNewStudent ‫التابع‬
.‫ادخال مستخدم جديد او العديل عليه‬
void _createNewStudent(BuildContext context){
await Navigator.push(context,MaterialPageRoute(builder: (context)
=>StudentScreen(new Student(null, "", "", "", "", "")))
);
}

:‫اما الشاشة التالية فهي بتصميم بسيط لإظهار حقول الادخال ويكون كما يلي‬
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:firebase_demo/model/Student.dart';

class StudentScreen extends StatefulWidget{


final Student student;
StudentScreen(this.student);
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _StudentScreen(student);
}

}
final
studentReference=FirebaseDatabase.instance.reference().child("student")
;
class _StudentScreen extends State<StudentScreen>{
TextEditingController _nameController;

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
133
TextEditingController _ageController;
TextEditingController _departmentController;
TextEditingController _descriptionController;
TextEditingController _cityController;
final Student student;
_StudentScreen(this.student);
@override
void initState(){
_nameController=new TextEditingController(text:
widget.student.name);
_ageController=new TextEditingController(text: widget.student.age);
_departmentController=new TextEditingController(text:
widget.student.department);
_descriptionController=new TextEditingController(text:
widget.student.description);
_cityController=new TextEditingController(text:
widget.student.city);

@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: AppBar(title: Text("Student"),
),
body: new Container(
margin: EdgeInsets.only(top: 12.0),
alignment: Alignment.center,
child: new Column(
children: <Widget>[
TextField(
controller: _nameController,
decoration: InputDecoration(icon:
Icon(Icons.person),labelText: "name"),
),
Padding(padding: EdgeInsets.only(top: 7.0)),
TextField(
controller: _cityController,
decoration: InputDecoration(icon:
Icon(Icons.location_city),labelText: "name"),
),
Padding(padding: EdgeInsets.only(top: 7.0)),
TextField(
controller: _departmentController,
decoration: InputDecoration(icon:
Icon(Icons.departure_board),labelText: "name"),
),
Padding(padding: EdgeInsets.only(top: 7.0)),
TextField(
controller: _descriptionController,
decoration: InputDecoration(icon:
Icon(Icons.description),labelText: "name"),
),
Padding(padding: EdgeInsets.only(top: 7.0)),
TextField(
controller: _ageController,
decoration: InputDecoration(icon:
Icon(Icons.data_usage),labelText: "name"),
),
Padding(padding: EdgeInsets.only(top: 7.0)),
FlatButton(
onPressed: (){
if(widget.student.id!=0){
studentReference.child(widget.student.id).set({

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
134
'name':_nameController,
'age':_ageController,
'city':_cityController,
'department':_departmentController,
'description':_descriptionController,
}).than((_){
Navigator.pop(context);
});
}
else
{
if(widget.student.id!=0){
studentReference.push.set({
'name':_nameController,
'age':_ageController,
'city':_cityController,
'department':_departmentController,
'description':_descriptionController,
}).than((_){
Navigator.pop(context);
});
}
},
child:
(widget.student.id!=null)?Text("Update"):Text("Add") ,
)
],
),
),
);
}
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪135‬‬
‫كذلك يوجد الكثير من الأشياء التي يمكن ان نقوم بها باستخدام قواعد البيانات فايربيز مثل‬
‫الدخول باستخدام الايميل او حساب الفيس بوك او حتى التعامل مع الصور ضمن قواعد البيانات‬
‫والصوتيات ولكن سنكتفي في كتابنا الى هذا الحد ولعل باقي الشروحات ستكون على القناة على‬
‫اليوتيوب‪.‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
5

136
‫النشر والربح من التطبيقات‬
Publish Applications

We can only see a short distance‫برمجيات‬ ahead‫مهندس‬,but we ‫تأليف‬


| ‫فيصل الأسود‬ can
see plenty there
Youtube ‫الـ‬that needs
‫الكتاب على قناة‬ to
‫محتويات‬ be‫شرح‬
‫لكامل‬ done
‫يمكنك الحصول على‬

StackOverFlow Arabi Alan Turing


‫‪137‬‬
‫إضافة إعلانات ‪Admob‬‬
‫إعلانات ادموب التي تقدمها جوجل تتيح لك عرض إعلانات على تطبيقك والحصول على مبالغ‬
‫مقابل تلك العروض‪.‬‬
‫بالبداية يجب ان تملك حساب ‪ Ad mob‬لكي تستطيع بناء وحدتك الاعلانية ومن ثم الحصول على‬
‫كود تلك الوحدة لكي تعرضها على تطبيقك‪.‬‬

‫المكتبة الأساسية التي سنستخدمها في هذا القسم هي مكتبة ‪ firebase_admob‬والتي يجب‬


‫اضافتها كما فعلنا مع المكتبات او الحزم السابقة ‪ ,‬كذلك يجب استدعاء المكتبات الأساسية‬
‫للـ‪.firebase‬‬

‫لنبدأ بشرح إضافة أنواع الإعلانات المختلفة لتطبيق فلاتر ‪ ,‬بالبداية علينا تحديد معلومات التطبيق‬
‫من نوع الجهاز الهدف ‪,‬الكلمات المفتاحية المتعلقة بفكرة التطبيق‪ ,‬تاريخ الانشاء ‪ ,‬وإمكانية‬
‫الاستخدام من قبل الأطفال والعديد‪....‬‬
‫نعرف كائن معلومات تطبيق كما يلي‪:‬‬
‫‪static final MobileAdTargetingInfo targeInfo‬‬
‫(‪=new MobileAdTargetingInfo‬‬
‫‪textDevices:<String>[],‬‬
‫‪keywords:<String>['music','video','picture'],‬‬
‫;)(‪birthday:new DateTime.now‬‬
‫‪childDirected:true,‬‬
‫;)‬

‫الان سننشئ وحدتين اعلانيتين مختلفتين ‪ ,‬الأولى من نوع ‪ BannarAd‬والتي تظهر كما في الشكل‬
‫التالي‪:‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
138
:‫يمكن تعريفها بالشكل‬
BannerAd createBannarAd(){
return new BannarAd(
adUnitId:BannerAd.testAdUnitId,
size:AdSize.bannar,
targetingInfo:targeInfo,
listener:(MobileAdEvent event)
{
print("Bannar event :$event");
}
);
}
anchor ‫كما يمكن تغيير موضعها عن طريق الخاصة‬

:‫ والتي تملأ الشاشة كما في الشكل التالي‬InterstitialAd ‫الثانية من نوع‬

:‫يمكن تعريفها بالشكل‬


InterstitialAd createInterstitialAd(){
return new InterstitialAd(
adUnitId:InterstitialAd.testAdUnitId,

targetingInfo:targeInfo,
listener:(MobileAdEvent event)
{
print("Interstitial event :$event");
}
);
}

‫ بحيث نكتب‬initState() ‫الوحدة الاعلانية الأولى يمكن استدعائها عن طريق الطريقة الموروثة‬
:‫ضمنها‬
_bannerAd=createBannarAd()..show();

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
139
:‫ كما يلي‬dispose ‫ونغلقها في الطريقة‬
_bannerAd.dispose();

:‫اما الوحدة الاعلانية الثانية يمكن استدعائها عن طريق أحد الازرار لتظهر كما يلي‬
child: InkWell(
onTap: (){
createInterstitialAd()
..load()
..show();
},

:‫ كما يلي‬dispose ‫كذلك لا ننسى ان نضعها في الطريقة‬


_bannerAd.dispose();

:‫والان يكون الكود الكامل لكلا الوحدتين كما يلي‬


import 'package:flutter/material.dart';

void main() => runApp(new MaterialApp(

));

class MyAppScreen extends StatefulWidget


{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new _MyAppScreen();
}

}
class _MyAppScreen extends State<MyAppScreen>
{
static final MobileAdTargetingInfo targeInfo=new
MobileAdTargetingInfo(
textDevices:<String>[],
keywords:<String>['music','video','picture'],
birthday:new DateTime.now();
childDirected:true,
);
@override

BannerAd _bannerAd;
InterstitialAd _interstitialAd;

BannerAd createBannarAd(){
return new BannarAd(
adUnitId:BannerAd.testAdUnitId,
size:AdSize.bannar,
targetingInfo:targeInfo,
‫تأليف فيصل الأسود | مهندس برمجيات‬
Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
140
listener:(MobileAdEvent event)
{
print("Bannar event :$event");
}
);
}

InterstitialAd createInterstitialAd(){
return new InterstitialAd(
adUnitId:InterstitialAd.testAdUnitId,

targetingInfo:targeInfo,
listener:(MobileAdEvent event)
{
print("Interstitial event :$event");
}
);
}

void initState(){
_bannerAd=createBannarAd()..show();
}
@override
void dispose()
{
_bannerAd.dispose();
_interstitialAd.dispose();
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(body: new Container(
child: InkWell(
onTap: (){
createInterstitialAd()
..load()
..show();
},
),
)

) ;
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪141‬‬
‫ولكن يجب ان نعلم ان كل ما بنيناه هو لوحدات إعلانية تجريبية فيجب ان نضع رقم وحدتنا‬
‫الصحيح التي انشأناها في حسابنا في ‪ Admob‬وتوضع كما يلي‪:‬‬

‫‪adUnitId:"ca-app-pub-4245565566455613/641455455455",‬‬

‫الى هنا نكون قد اضفنا الإعلانات المطلوبة تبقى لدينا ان ننشر التطبيق في احد المتاجر‪.‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪142‬‬
‫نشر تطبيقات اندرويد‬

‫متجر جوجل)‪(Google Play Store‬‬


‫متجر جوجل )‪ (Google Play Store‬يمثل الآلية الرسمية لنشر تطبيقات الاندرويد ‪.‬إن نشر‬
‫تطبيقك على متجر جوجل يجعل من التطبيق قابل للتحميل والاستخدام من قبل المستخدمين‬
‫في جميع أنحاء العالم ‪.‬يمكن أيضا للمستخدمين إضافة تعليقات أو إعطاء تقييم للتطبيق بما‬
‫يمكن من اكتشاف الأخطاء أو تحسين التطبيق لاحق ًا ‪.‬كما يوفر متجر جوجل بعض الإحصائيات‬
‫التي يمكن الاستفادة منها لقياس نجاح أي تطبيق‪.‬‬

‫في هذه القسم سيتم شرح خطوات نشر التطبيق على متجر جوجل )‪(Google Play Store‬‬
‫يمكن تلخيص خطوات نشر التطبيق على متجر جوجل بما يلي‪:‬‬
‫تصدير التطبيق )‪ (Export‬كملف من نوع )‪. APK (Android Package‬‬ ‫‪‬‬
‫عمل توقيع رقمي )‪ (Digital Signature‬للتطبيق باستخدام شهادة )‪(Certificate‬‬ ‫‪‬‬
‫توقيع التطبيق رقمياً‪.‬‬
‫يساعد نظام اندرويد في تحديد هوية مالك التطبيق‬
‫رفع التطبيق على المتجر‪.‬‬ ‫‪‬‬
‫استخدام سوق اندرويد )‪ (Android Market‬لاستضافة وبيع التطبيق‪.‬‬ ‫‪‬‬
‫في ما يلي سنقوم بشرح خطوات إعداد التطبيق للتوقيع ومن ثم سنقوم بشرح طريقة نشر‬
‫التطبيق‪:‬‬
‫مراجعة ملف ال‪:Manifest.xml‬‬ ‫‪‬‬
‫راجع ملف بيان التطبيقات الافتراضي ‪AndroidManifest.xml‬الموجود في‬
‫‪<app dir> / android / app / src / main‬‬
‫وتحقق من صحة القيم ‪ ,‬وخصوصًا‪:‬‬
‫‪ )a‬في الوسم ‪ application‬عدل الخاصة ‪ label‬لتعكس الاسم النهائي للتطبيق‪.‬‬
‫‪ )b‬إزالة إذن ‪ android.permission.INTERNET‬إذا كان التطبيق الخاص بك لا‬
‫يحتاج إلى الوصول إلى الإنترنت ‪.‬يتضمن القالب القياسي هذه العلامة لتمكين‬
‫الاتصال بين أدوات ‪ flutter‬والتطبيق الجاري تشغيله‪.‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪143‬‬
‫مراجعة ملف التكوين ‪:Build‬‬ ‫‪‬‬
‫راجع ملف إنشاء ملف ‪ Gradle‬الافتراضي الموجود في‬
‫‪<app dir> / android / app‬‬
‫وتحقق من صحة القيم‪ ,‬وخصوصًا‪:‬‬
‫في القسم ‪ defaultConfig‬حدد معرف التطبيق ‪ ApplicationId‬واسم وكود النسخة‬
‫‪ version code & version name‬كذلك حدد اصدار النظام الأدنى المقبول او اصدار‬
‫جهاز الهدف للتطبيق ‪.minSdkVersion & targetSdkVersion‬‬

‫أضف ايقونة خاصة لتطبيقك‪:‬‬ ‫‪‬‬


‫أي تطبيق جديد له ايقونة افتراضية وهي رمز فلاتر يمكنك تغيرها بأيقونتك الخاصة كما‬
‫يلي‪:‬‬
‫بعد اعداد ايقونتك بما تتناسب مع احجام الايقونات الخاصة بالاندرويد افتح المجلد‬
‫‪<app dir>/android/app/src/main/res/mipmap-‬‬

‫ثم ضع ايقونتك الخاصة‪.‬‬


‫في الملف ‪ Manifest.xml‬حدث القيمة حسب اسم ملف الايقونة الخاص بك كما يلي‪:‬‬
‫"‪<application android:icon="@mipmap/my_icon‬‬

‫اعد تشغيل تطبيقك لتتحقق من حقيقة استخدام ايقونتك الخاصة عوضاً عن تلك‬
‫الافتراضية‪.‬‬
‫انشاء المفتاح الخاص بتطبيقك ‪:Keystore‬‬ ‫‪‬‬
‫في حال عدم تملكك لمفتاح خاص بتطبيقك عليك ان تولد واحداً بالتعليمة التالية في‬
‫الكونسول الخاص بـ ‪.flutter‬‬
‫‪keytool -genkey -v -keystore ~/key.jks -keyalg RSA -‬‬
‫‪keysize 2048 -validity 10000 -alias key‬‬

‫ربط الـمفتاح من التطبيق‪:‬‬ ‫‪‬‬


‫أنشئ ملفاً اسمه ‪ Key.properties‬في المسار التالي‪:‬‬
‫‪<app dir>/android/key.properties‬‬

‫>‪storePassword=<password from previous step‬‬


‫>‪keyPassword=<password from previous step‬‬
‫‪keyAlias=key‬‬
‫‪storeFile=<location of the key store file, e.g. /Users/<user‬‬
‫>‪name>/key.jks‬‬
‫تأليف فيصل الأسود | مهندس برمجيات‬
‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
144
.‫مع العلم ان هذا الملف خاص بك وحدك ولا يجب ان يتطلع عليه الاخرون‬

:‫عمليات ضبط التسجيل‬ 


build.gradle ‫ لنفتح الملف‬, ‫سنقوم الان في تعديلات شاملة في بعض الخصائص‬
:‫الموجود في المجلد‬
<app dir>/android/app/build.gradle

:‫بحيث نستبدل بعض التعليمات بتعليمات أخرى كما يلي‬

:‫األساس‬
android {
:‫البديل‬
def keystoreProperties = new Properties()
def keystorePropertiesFile =
rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new
FileInputStream(keystorePropertiesFile))
}

android {

:‫األساس‬
buildTypes {
release {
// TODO: Add your own signing config for the
release build.
// Signing with the debug keys for now, so
`flutter run --release` works.
signingConfig signingConfigs.debug
}
}

:‫البديل‬
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
145
:Proguard ‫تفعيل الـ‬ 
:‫ في المسار التالي‬proguard-rules.pro ‫بالبداية ننشئ ملفاً باسم‬
/android/app/proguard-rules.pro
:‫نضع فيه ما يلي‬
#Flutter Wrapper
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }

‫الاعدادات السابقة هي اعدادات أساسية لحماية مكتبات فلاتر الأساسية وأي مكتبة خارجية‬
.ً‫مضافة عليك إضافة قاعدتها الخاصة ضمن الملف ايضا‬

‫ بحيث نضيف ونعدل بعض‬build.gradle ‫ في ملف ال‬Proguard ‫ثانياً علينا تفعيل الـ‬
:‫الاعدادات‬
android {

...

buildTypes {

release {

signingConfig signingConfigs.release

minifyEnabled true
useProguard true

proguardFiles
getDefaultProguardFile('proguard-android.txt'), 'proguard-
rules.pro'

}
}
}

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
146
:‫تحرير التطبيق للنشر‬ 
:‫ لكتابة التعليمات التالية‬flutter‫استخدم سطر الأوامر كونسول الخاص بـ‬

1. cd <app dir> (replace <app dir> with your application’s directory).


2. Run flutter build apk (flutter build defaults to --release).

:‫التطبيق سيكون جاهزا في المسار‬


<app dir>/build/app/outputs/apk/release/app-release.apk

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪147‬‬
‫رفع التطبيق‪:‬‬
‫لكي تتمكن من رفع تطبيقك على متجر ‪ Google Store‬يجب ان تملك حساب مطور وان يكون‬
‫لديك جاهزاً ما يلي‪:‬‬
‫ملف ‪ APK‬الخاص بالتطبيق والذي تم توقيعه رقمياً‪.‬‬ ‫‪‬‬
‫صور )‪ (screenshots‬توضح واجهات التطبيق ) صورتان على الأقل)‬ ‫‪‬‬
‫وصف مختصر للتطبيق ووظائفه‪.‬‬ ‫‪‬‬

‫من الصفحة الرئيسية للمطور قم بالنقر على الرابط ‪ Upload Application‬حيث ستظهر‬
‫الصفحة في شكل والتي من خلالها يتم رفع ملف ‪ APK‬وكذلك ملفات الصور ‪.‬يجب ايض ًا إدخال‬
‫بعض البيانات الخاصة بالتطبيق مثل عنوان التطبيق‪ ,‬وصف له‪ ,‬آخر التعديلات على التطبيق نوع‬
‫التطبيق وتصنيفه‪.‬‬

‫بعد نشر التطبيق على متجر جوجل‪ ,‬يمكن تتبع أي تعليقات يرسلها المستخدمون وكذلك أي‬
‫أخطاء محتملة في التطبيق وعدد مرات تنزيله‪.‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
148
‫المراجع‬

Eric Windmill , 2018. "Flutter In Action" USA

Macro L. Napoli, 2018. "Beginning Flutter A Hands On Guide To App


Development"

Katby Walratb & Setb Ladd , 2009. "What is Dart"

Moises Belchin & Patricia Juberias 2014 ."Web Programming With Dart"

‫تأليف فيصل الأسود | مهندس برمجيات‬


Youtube ‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ‬

StackOverFlow Arabi
‫‪149‬‬
‫النهاية‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬
‫‪150‬‬

‫تأليف فيصل الأسود | مهندس برمجيات‬


‫يمكنك الحصول على شرح لكامل محتويات الكتاب على قناة الـ ‪Youtube‬‬

‫‪StackOverFlow Arabi‬‬

You might also like