[بواسطة الاحواض] الرفرفة. الجزء 2. لمطوري دائرة الرقابة الداخلية

بعد استراحة طويلة ، سأواصل الحديث عن إطار Flutter الشهير في شكل سؤال وجواب. يمكنك العثور على المقالة الأولى لمطوري Android هنا ، واليوم ستكون هناك مواد مفيدة لمطوري نظام التشغيل iOS.

إذا كان لديك القليل من الوقت للدراسة المستقلة والمتعمقة للوثائق ، لكنك تريد أن تفهم ما هو رفرفة جيدة وكيفية استخدامها ، نلقي نظرة تحت القط.



رفرفة. الجزء 1. لمطوري أندرويد
رفرفة. الجزء 2. لمطوري دائرة الرقابة الداخلية
رفرفة. الجزء 3. للمطورين رد فعل الأصلية
رفرفة. الجزء 4. لمطوري الويب
رفرفة. الجزء 5. ل Xamarin.Forms المطورين

المحتويات:


  1. المشاهدات

  2. ملاحة

  3. خيوط وعدم التزامن

  4. هيكل المشروع والموارد

  5. ViewControllers

  6. تخطيطات

  7. الإيماءات والتعامل مع الحدث اللمس

  8. تصميم التطبيق

  9. شكل الإدخال

  10. الإضافات الرفرفة

  11. قواعد البيانات والتخزين المحلي

  12. إشعار



المشاهدات


السؤال:


ما هو التناظرية UIView في رفرفة؟

الجواب هو:


القطعة

الاختلافات:


UIView هو في الواقع ما سيكون على الشاشة. يتم استدعاء SetNeedsDisplay () لعرض التغييرات.

القطعة - وصف لما سيكون على الشاشة. من أجل التغيير يتم إنشاؤه من جديد.

معلومات اضافية:


يتضمن Flutter مكتبة Cupertino Widgets . أنه يحتوي على عناصر واجهة تعامل تطبيق إرشادات Apple Design .

السؤال:


كيفية تحديث عرض الحاجيات؟

الجواب هو:


باستخدام StatefulWidget وحالتها . يحتوي Flutter على نوعين من الحاجيات: StatelessWidget و StatefulWidget . أنها تعمل بنفس الطريقة ، والفرق الوحيد هو في حالة التقديم.

الاختلافات:


StatelessWidget لديه حالة غير قابلة للتغيير. مناسب لعرض النص والشعار ، إلخ. أي إذا كان يجب عدم تغيير العنصر على الشاشة خلال وقت العرض بالكامل ، فهذا يناسبك. كما يمكن استخدامه كحاوية لعناصر واجهة المستخدم.

يحتوي StatefulWidget على حالة الولاية ، التي تخزّن معلومات حول الحالة الحالية. إذا كنت ترغب في تغيير عنصر على الشاشة عند إجراء بعض الإجراءات (جاءت استجابة من الخادم ، فقد نقر المستخدم على زر ، وما إلى ذلك) - هذا هو خيارك.

مثال:


1) StatelessWidget - النص

Text( 'I like Flutter!', style: TextStyle(fontWeight: FontWeight.bold), ); 

2) StatefulWidget - عند النقر فوق الزر (FloatingActionButton) ، يتغير النص الموجود في عنصر واجهة تعامل النص من I Like Flutter إلى Flutter!

 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { //     . @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { //   String textToShow = "  Flutter"; void _updateText() { setState(() { //   textToShow = "Flutter !"; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center(child: Text(textToShow)), floatingActionButton: FloatingActionButton( onPressed: _updateText, tooltip: ' ', child: Icon(Icons.update), ), ); } } 

السؤال:


كيفية تخطيط الشاشة مع الحاجيات؟ أين هي لوحة العمل ؟

الجواب هو:


Flutter has no القصة المصورة . كل شيء تنضيد في شجرة القطعة مباشرة في التعليمات البرمجية.

مثال:


 @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: CupertinoButton( onPressed: () { setState(() { _pressedCount += 1; }); }, child: Text('Hello'), padding: EdgeInsets.only(left: 10.0, right: 10.0), ), ), ); } 

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

السؤال:


كيفية إضافة أو إزالة مكون في التخطيط أثناء تشغيل التطبيق؟

الجواب هو:


من خلال وظيفة من شأنها أن ترجع القطعة المطلوبة اعتمادا على الدولة.

الاختلافات:


على نظام التشغيل iOS ، يمكنك القيام بـ addSubview () أو إزالةFromSuperview (). في الرفرفة ، هذا غير ممكن ، لأن الحاجيات لم تتغير. فقط حالتهم يمكن أن تتغير.

مثال:


تغيير النص إلى زر من خلال النقر على FloatingActionButton.

 class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { // Default value for toggle bool toggle = true; void _toggle() { setState(() { toggle = !toggle; }); } _getToggleChild() { if (toggle) { return Text('Toggle One'); } else { return CupertinoButton( onPressed: () {}, child: Text('Toggle Two'), ); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: _getToggleChild(), ), floatingActionButton: FloatingActionButton( onPressed: _toggle, tooltip: 'Update Text', child: Icon(Icons.update), ), ); } } 

السؤال:


كيفية تحريك الحاجيات؟

الجواب هو:


باستخدام فئة AnimationController ، وهو سليل فئة الملخص <T> المتحركة . بالإضافة إلى بدء تشغيل الرسوم المتحركة ، يمكنه إيقافها مؤقتًا والرجوع إليها والتوقف عنها وتشغيلها في الاتجاه المعاكس. يعمل مع تكر ، الذي يبلغ عن إعادة رسم الشاشة.

الاختلافات:


على iOS ، يمكنك تحريك مشاهدة باستخدام تحريك (withDuration: animations :). في Flutter ، يجب كتابة الرسوم المتحركة في التعليمات البرمجية باستخدام AnimationController.

معلومات اضافية:


يمكنك معرفة المزيد في عناصر الرسوم المتحركة والحركة ، والبرنامج التعليمي للرسوم المتحركة ، والنظرة العامة للرسوم المتحركة .

مثال:


تتلاشى الرسوم المتحركة لشعار الرفرفة.

 class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Fade Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: MyFadeTest(title: 'Fade Demo'), ); } } class MyFadeTest extends StatefulWidget { MyFadeTest({Key key, this.title}) : super(key: key); final String title; @override _MyFadeTest createState() => _MyFadeTest(); } class _MyFadeTest extends State<MyFadeTest> with TickerProviderStateMixin { AnimationController controller; CurvedAnimation curve; @override void initState() { controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); curve = CurvedAnimation(parent: controller, curve: Curves.easeIn); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Container( child: FadeTransition( opacity: curve, child: FlutterLogo( size: 100.0, ) ) ) ), floatingActionButton: FloatingActionButton( tooltip: 'Fade', child: Icon(Icons.brush), onPressed: () { controller.forward(); }, ), ); } @override dispose() { controller.dispose(); super.dispose(); } } 

السؤال:


كيفية استخدام CoreGraphics ؟

الجواب هو:


يستخدم Flutter واجهة برمجة تطبيقات Canvas على محرك Skia ذي المستوى المنخفض بدلاً من CoreGraphics. يستخدم Android واجهة برمجة تطبيقات Canvas مماثلة.

معلومات اضافية:


يحتوي Flutter على فئتين للرسم على Canvas: CustomPaint و CustomPainter . الثاني ينفذ خوارزمية التقديم.

اقرأ المزيد هنا: StackOverflow

مثال:


 class SignaturePainter extends CustomPainter { SignaturePainter(this.points); final List<Offset> points; void paint(Canvas canvas, Size size) { var paint = Paint() ..color = Colors.black ..strokeCap = StrokeCap.round ..strokeWidth = 5.0; for (int i = 0; i < points.length - 1; i++) { if (points[i] != null && points[i + 1] != null) canvas.drawLine(points[i], points[i + 1], paint); } } bool shouldRepaint(SignaturePainter other) => other.points != points; } class Signature extends StatefulWidget { SignatureState createState() => SignatureState(); } class SignatureState extends State<Signature> { List<Offset> _points = <Offset>[]; Widget build(BuildContext context) { return GestureDetector( onPanUpdate: (DragUpdateDetails details) { setState(() { RenderBox referenceBox = context.findRenderObject(); Offset localPosition = referenceBox.globalToLocal(details.globalPosition); _points = List.from(_points)..add(localPosition); }); }, onPanEnd: (DragEndDetails details) => _points.add(null), child: CustomPaint(painter: SignaturePainter(_points), size: Size.infinite), ); } } 

السؤال:


كيفية تغيير شفافية الحاجيات؟

الجواب هو:


التفاف في القطعة العتامة .

الاختلافات:


على iOS ، كل المشاهدات لها .opacity أو .alpha. في Flutter ، يستبدل هذا الخيار عنصر واجهة المجمع.

السؤال:


كيفية إنشاء الحاجيات المخصصة؟

الجواب هو:


إنشاء الحاجيات داخل واحد (بدلا من الميراث).

الاختلافات:


في نظام التشغيل iOS ، يمكنك أن ترث من طريقة العرض التي نهتم بها ونضيف منطقك الخاص. في Flutter ، يتم توارث عنصر واجهة المستخدم دائمًا من StatelessWidget أو StatefulWidget. أي تحتاج إلى إنشاء عنصر واجهة مستخدم جديد واستخدامه في مجموعة عناصر واجهة المستخدم التي تحتاج إليها كمعلمات أو حقول.

مثال:


 class CustomButton extends StatelessWidget { final String label; CustomButton(this.label); @override Widget build(BuildContext context) { return RaisedButton(onPressed: () {}, child: Text(label)); } } @override Widget build(BuildContext context) { return Center( child: CustomButton("Hello"), ); } 

ملاحة


السؤال:


كيفية تنفيذ التنقل بين الشاشات في الرفرفة؟

الجواب هو:


للتنقل بين الشاشات ، يتم استخدام فئتي Navigator و Route .

الاختلافات:


لا يوجد لدى Flutter مفاهيم مثل UIViewController و UINavigationController. هناك المستكشف (المستكشف) والطرق (الطرق). يشبه Navigator أداة التحكم UINavigationController من حيث المبدأ. يمكنه القيام بالدفع () أو pop () إلى المسار الذي تحدده. Route هو نوع من UIViewController ، لكن في Flutter من المعتاد مقارنة ذلك بشاشة أو صفحة.

لدى Flutter طريقتان للملاحة:

  • وصف الخريطة مع أسماء الطريق
  • انتقل مباشرة إلى الطريق.

مثال:


 void main() { runApp(CupertinoApp( home: MyAppHome(), // becomes the route named '/' routes: <String, WidgetBuilder> { '/a': (BuildContext context) => MyPage(title: 'page A'), '/b': (BuildContext context) => MyPage(title: 'page B'), '/c': (BuildContext context) => MyPage(title: 'page C'), }, )); } Navigator.of(context).pushNamed('/b'); 

السؤال:


كيفية التنقل إلى تطبيق جهة خارجية؟

الجواب هو:


إما التفاعل مع طبقة iOS للتطبيق من خلال MethodChannel ، أو باستخدام المكون الإضافي لمشغل URL .

السؤال:


كيفية جعل البوب ​​العودة في دائرة الرقابة الداخلية ViewController؟

الجواب هو:


عن طريق استدعاء SystemNavigator.pop ().

معلومات اضافية:


يستدعي SystemNavigator.pop () من رمز Dart التعليمة البرمجية التالية في iOS:
 UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController; if ([viewController isKindOfClass:[UINavigationController class]]) { [((UINavigationController*)viewController) popViewControllerAnimated:NO]; } 

إذا لم يكن هذا هو ما تحتاجه ، فيمكنك القيام بالتنفيذ من خلال MethodChannel .

خيوط وعدم التزامن


السؤال:


كيف تكتب كود غير متزامن في الرفرفة؟

الجواب هو:


تقوم Dart بتنفيذ نموذج تنفيذ أحادي الترابط يتم تشغيله على Isolates . يستخدم التنفيذ غير المتزامن async / await ، والذي قد تكون على دراية به من coroutines C # أو JavaScript أو Kotlin.

مثال:


تلبية الطلب وإرجاع النتيجة لتحديث واجهة المستخدم:

 loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } 

عند تلقي استجابة الطلب ، تحتاج إلى استدعاء الأسلوب setState () لإعادة رسم شجرة القطعة باستخدام البيانات الجديدة.

مثال:


تحميل وتحديث البيانات في ListView :

 import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); loadData(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); })); } Widget getRow(int i) { return Padding( padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}") ); } loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } } 

السؤال:


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

الجواب هو:


كما ذكر أعلاه - استخدام المتزامن / انتظار والعزلة (عزل).

الاختلافات:


من خارج منطقة الجزاء على iOS ، يمكنك استخدام العملية مع إعادة تعريف محتملة للطرق. في Flutter "خارج الصندوق" ، تحتاج فقط إلى استخدام المزامنة / الانتظار ، وسيتولى دارت الاهتمام بالباقي.

مثال:


هنا يتم عزل أسلوب dataLoader (). بمعزل ، يمكنك تشغيل عمليات ثقيلة مثل تحليل JSONs الكبيرة والتشفير ومعالجة الصور ، إلخ.

 loadData() async { ReceivePort receivePort = ReceivePort(); await Isolate.spawn(dataLoader, receivePort.sendPort); // The 'echo' isolate sends its SendPort as the first message SendPort sendPort = await receivePort.first; List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts"); setState(() { widgets = msg; }); } // The entry point for the isolate static dataLoader(SendPort sendPort) async { // Open the ReceivePort for incoming messages. ReceivePort port = ReceivePort(); // Notify any other isolates what port this isolate listens to. sendPort.send(port.sendPort); await for (var msg in port) { String data = msg[0]; SendPort replyTo = msg[1]; String dataURL = data; http.Response response = await http.get(dataURL); // Lots of JSON to parse replyTo.send(json.decode(response.body)); } } Future sendReceive(SendPort port, msg) { ReceivePort response = ReceivePort(); port.send([msg, response.sendPort]); return response.first; }   : import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'dart:async'; import 'dart:isolate'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); loadData(); } showLoadingDialog() { if (widgets.length == 0) { return true; } return false; } getBody() { if (showLoadingDialog()) { return getProgressDialog(); } else { return getListView(); } } getProgressDialog() { return Center(child: CircularProgressIndicator()); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: getBody()); } ListView getListView() => ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); }); Widget getRow(int i) { return Padding(padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}")); } loadData() async { ReceivePort receivePort = ReceivePort(); await Isolate.spawn(dataLoader, receivePort.sendPort); // The 'echo' isolate sends its SendPort as the first message SendPort sendPort = await receivePort.first; List msg = await sendReceive(sendPort, "https://jsonplaceholder.typicode.com/posts"); setState(() { widgets = msg; }); } // the entry point for the isolate static dataLoader(SendPort sendPort) async { // Open the ReceivePort for incoming messages. ReceivePort port = ReceivePort(); // Notify any other isolates what port this isolate listens to. sendPort.send(port.sendPort); await for (var msg in port) { String data = msg[0]; SendPort replyTo = msg[1]; String dataURL = data; http.Response response = await http.get(dataURL); // Lots of JSON to parse replyTo.send(json.decode(response.body)); } } Future sendReceive(SendPort port, msg) { ReceivePort response = ReceivePort(); port.send([msg, response.sendPort]); return response.first; } } 

السؤال:


كيفية جعل طلبات الشبكة في رفرفة؟

الجواب هو:


لدى Flutter حزمة HTTP الخاصة بها.

مثال:


لاستخدام حزمة HTTP ، أضفها كتبعية في pubspec.yaml:

 dependencies: ... http: ^0.11.3+16 

لتنفيذ الطلب ، يرجى الانتظار في وظيفة المتزامن http.get ():

 import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; [...] loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } } 

السؤال:


كيف تظهر التقدم؟

الجواب هو:


باستخدام أداة ProgressIndicator .

مثال:


 import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); loadData(); } showLoadingDialog() { return widgets.length == 0; } getBody() { if (showLoadingDialog()) { return getProgressDialog(); } else { return getListView(); } } getProgressDialog() { return Center(child: CircularProgressIndicator()); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: getBody()); } ListView getListView() => ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); }); Widget getRow(int i) { return Padding(padding: EdgeInsets.all(10.0), child: Text("Row ${widgets[i]["title"]}")); } loadData() async { String dataURL = "https://jsonplaceholder.typicode.com/posts"; http.Response response = await http.get(dataURL); setState(() { widgets = json.decode(response.body); }); } } 

هيكل المشروع والموارد


السؤال:


حيث لتخزين الموارد من قرارات مختلفة؟

الجواب هو:


في الأصول.

الاختلافات:


في iOS ، تحتوي الموارد الرسومية على Images.xcasset ، والتي توجد في مجلد الأصول. رفرفة لديها الأصول فقط. يمكن تحديد موقع مجلد الموارد في أي مكان في المشروع ، والأهم من ذلك ، كتابة المسار إليه في ملف pubspec.yaml.

معلومات اضافية:


أحجام الموارد الرسومية في iOS و Flutter متطابقة وتتبع التنسيق المستند إلى الكثافة.

موقع المورد:

 images/my_icon.png // Base: 1.0x image images/2.0x/my_icon.png // 2.0x image images/3.0x/my_icon.png // 3.0x image 

المسار في ملف pubspec.yaml:

 assets: - images/my_icon.png 

باستخدام AssetImage :

 return AssetImage("images/a_dot_burr.jpeg"); 

باستخدام الأصول مباشرة:

 @override Widget build(BuildContext context) { return Image.asset("images/my_image.png"); } 

السؤال:


حيث لتخزين السلاسل؟ كيفية توطينهم؟

الجواب هو:


تخزينها في الحقول الثابتة. توطين باستخدام حزمة intl .

مثال:


 class Strings { static String welcomeMessage = "Welcome To Flutter"; } Text(Strings.welcomeMessage) 

السؤال:


ما هو نظيره ل CocoaPods؟ كيفية إضافة التبعيات؟

الجواب هو:


pubspec.yaml.

معلومات اضافية:


يقوم برنامج Flutter بتفويض الإنشاء إلى مُنشئي Android و iOS الأصليين. انظر قائمة بجميع المكتبات الشعبية لـ Flutter في Pub .

ViewControllers


السؤال:


ما هو ما يعادل ViewController في رفرفة؟

الجواب هو:


كل شيء في رفرفة هو الحاجيات. يتم لعب دور ViewController للعمل مع واجهة المستخدم بواسطة عناصر واجهة المستخدم. ودور الملاحة ، كما ذكر في الفقرة على الملاحة ، هو Navigator و Route.

السؤال:


كيفية التعامل مع أحداث دورة الحياة؟

الجواب هو:


باستخدام الأسلوب WidgetsBinding و didChangeAppLifecycleState () .

معلومات اضافية:


يستخدم Flutter FlutterAppDelegate في الكود الأصلي ، ويجعل محرك Flutter تغييرات حالة المعالجة غير واضحة قدر الإمكان. ولكن إذا كنت لا تزال بحاجة إلى القيام ببعض الأعمال حسب الحالة ، فدورة الحياة تختلف قليلاً:

  • غير نشط - التطبيق غير نشط ولا يتلقى إدخال المستخدم. هذه الحالة موجودة فقط في نظام التشغيل iOS ، ولا يوجد في نظام Android أي تناظر ؛
  • تم الإيقاف مؤقتًا - التطبيق غير مرئي حاليًا للمستخدم ، ولا يستجيب لإدخالات المستخدم ، ولكنه يعمل في الخلفية ؛
  • تم استئناف - التطبيق مرئي ويستجيب لإدخال المستخدم ؛
  • تعليق - التطبيق في عملية التوقف. هذه الحالة موجودة فقط على نظام Android ، في نظام iOS لا يوجد نظير لها.

تم توضيح ذلك بمزيد من التفاصيل في وثائق AppLifecycleStatus .

مثال:


 import 'package:flutter/widgets.dart'; class LifecycleWatcher extends StatefulWidget { @override _LifecycleWatcherState createState() => _LifecycleWatcherState(); } class _LifecycleWatcherState extends State<LifecycleWatcher> with WidgetsBindingObserver { AppLifecycleState _lastLifecycleState; @override void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); } @override void dispose() { WidgetsBinding.instance.removeObserver(this); super.dispose(); } @override void didChangeAppLifecycleState(AppLifecycleState state) { setState(() { _lastLifecycleState = state; }); } @override Widget build(BuildContext context) { if (_lastLifecycleState == null) return Text('This widget has not observed any lifecycle changes.', textDirection: TextDirection.ltr); return Text('The most recent lifecycle state this widget observed was: $_lastLifecycleState.', textDirection: TextDirection.ltr); } } void main() { runApp(Center(child: LifecycleWatcher())); } 

تخطيطات


السؤال:


ما هو ما يعادل UITableView و UICollectionView ؟

الجواب هو:


يكون ListView .

مثال:


 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView(children: _getListData()), ); } _getListData() { List<Widget> widgets = []; for (int i = 0; i < 100; i++) { widgets.add(Padding(padding: EdgeInsets.all(10.0), child: Text("Row $i"))); } return widgets; } } 

السؤال:


كيف أعرف عنصر القائمة الذي تم النقر عليه؟

الجواب هو:


يجب أن يتعامل عنصر واجهة المستخدم ، وهو عنصر في القائمة ، مع النقر فوقه.

الاختلافات:


في نظام التشغيل iOS ، يعرض الجدول المنفصل: didSelectRowAtIndexPath: الطريقة مسؤولة عن ذلك. في Flutter ، يجب لف عنصر القائمة في عنصر واجهة تعامل النقرات ، مثل GestureDetector .

السؤال:


كيفية تحديث ListView بشكل حيوي؟

الجواب هو:


قم بتحديث قائمة البيانات واستدعاء setState ().

الاختلافات:


في نظام iOS ، تحتاج إلى تحديث البيانات واستدعاء طريقة إعادة تحميل البيانات. في Flutter ، بعد setState () ، سيتم إعادة رسم القطعة مرة أخرى.

مثال:


 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); for (int i = 0; i < 100; i++) { widgets.add(getRow(i)); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView(children: widgets), ); } Widget getRow(int i) { return GestureDetector( child: Padding( padding: EdgeInsets.all(10.0), child: Text("Row $i"), ), onTap: () { setState(() { widgets = List.from(widgets); widgets.add(getRow(widgets.length + 1)); print('row $i'); }); }, ); } } 


معلومات اضافية:


لإنشاء قائمة ، يوصى باستخدام ListView.Builder .

مثال:


 import 'package:flutter/material.dart'; void main() { runApp(SampleApp()); } class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { List widgets = []; @override void initState() { super.initState(); for (int i = 0; i < 100; i++) { widgets.add(getRow(i)); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: ListView.builder( itemCount: widgets.length, itemBuilder: (BuildContext context, int position) { return getRow(position); }, ), ); } Widget getRow(int i) { return GestureDetector( child: Padding( padding: EdgeInsets.all(10.0), child: Text("Row $i"), ), onTap: () { setState(() { widgets.add(getRow(widgets.length + 1)); print('row $i'); }); }, ); } } 


السؤال:


ما هو التناظرية من UIScrollView ؟

الجواب هو:


ListView مع الحاجيات.

مثال:


 @override Widget build(BuildContext context) { return ListView( children: <Widget>[ Text('Row One'), Text('Row Two'), Text('Row Three'), Text('Row Four'), ], ); } 


معلومات اضافية:


مزيد من التفاصيل هنا .

الإيماءات والتعامل مع الحدث اللمس


السؤال:


كيفية إضافة onClick المستمع لعنصر واجهة المستخدم في رفرفة؟

الجواب هو:


إذا كانت القطعة تدعم النقرات ، فحينها onPressed (). إن لم يكن ، ثم في onTap ().

مثال:


في onPressed ():

 @override Widget build(BuildContext context) { return RaisedButton( onPressed: () { print("click"); }, child: Text("Button"), ); } 

في onTap ():

 class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( child: FlutterLogo( size: 200.0, ), onTap: () { print("tap"); }, ), ), ); } } 

السؤال:


كيفية التعامل مع الإيماءات الأخرى على الحاجيات؟

الجواب هو:


باستخدام GestureDetector . يمكنهم التعامل مع الإجراءات التالية:

صنبور



انقر نقرا مزدوجا



الضغط لفترة طويلة



السحب الرأسي



السحب الأفقي



مثال:


معالجة onDoubleTap:

 AnimationController controller; CurvedAnimation curve; @override void initState() { controller = AnimationController(duration: const Duration(milliseconds: 2000), vsync: this); curve = CurvedAnimation(parent: controller, curve: Curves.easeIn); } class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( body: Center( child: GestureDetector( child: RotationTransition( turns: curve, child: FlutterLogo( size: 200.0, )), onDoubleTap: () { if (controller.isCompleted) { controller.reverse(); } else { controller.forward(); } }, ), ), ); } } 


تصميم التطبيق


السؤال:


كيفية استخدام السمة (السمة) في التطبيق؟

الجواب هو:


باستخدام أداة MaterialApp أو WidgetApp كجذر في التطبيق.

مثال:


 class SampleApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, textSelectionColor: Colors.red ), home: SampleAppPage(), ); } } 


السؤال:


كيفية استخدام الخطوط المخصصة؟

الجواب هو:


تحتاج فقط إلى وضع ملف الخط في المجلد (فكر في الاسم بنفسك) والإشارة إلى المسار إلى pubspec.yaml.

مثال:


 fonts: - family: MyCustomFont fonts: - asset: fonts/MyCustomFont.ttf - style: italic 

 @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: Text( 'This is a custom font text', style: TextStyle(fontFamily: 'MyCustomFont'), ), ), ); } 

السؤال:


كيفية نمط الحاجيات النص؟

الجواب هو:


باستخدام المعلمات:

  • اللون.
  • الديكور.
  • decorationColor.
  • decorationStyle.
  • fontFamily.
  • حجم الخط.
  • fontStyle.
  • fontWeight.
  • شفرة التجزئة.
  • ارتفاع.
  • وراثة.
  • letterSpacing.
  • textBaseline.
  • wordSpacing.


شكل الإدخال


السؤال:


كيفية الحصول على نتيجة إدخال المستخدم؟

الجواب هو:


باستخدام TextEditingController .

مثال:

 class _MyFormState extends State<MyForm> { // Create a text controller and use it to retrieve the current value. // of the TextField! final myController = TextEditingController(); @override void dispose() { // Clean up the controller when disposing of the Widget. myController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Retrieve Text Input'), ), body: Padding( padding: const EdgeInsets.all(16.0), child: TextField( controller: myController, ), ), floatingActionButton: FloatingActionButton( // When the user presses the button, show an alert dialog with the // text the user has typed into our text field. onPressed: () { return showDialog( context: context, builder: (context) { return AlertDialog( // Retrieve the text the user has typed in using our // TextEditingController content: Text(myController.text), ); }, ); }, tooltip: 'Show me the value!', child: Icon(Icons.text_fields), ), ); } } 


: Retrieve the value of a text field .

:


hint TextInput ?

:


InputDecoration , .

مثال:


 body: Center( child: TextField( decoration: InputDecoration(hintText: "This is a hint"), ), ) 

:


?

:


InputDecoration .

مثال:


 class SampleApp extends StatelessWidget { // This widget is the root of your application. @override Widget build(BuildContext context) { return MaterialApp( title: 'Sample App', theme: ThemeData( primarySwatch: Colors.blue, ), home: SampleAppPage(), ); } } class SampleAppPage extends StatefulWidget { SampleAppPage({Key key}) : super(key: key); @override _SampleAppPageState createState() => _SampleAppPageState(); } class _SampleAppPageState extends State<SampleAppPage> { String _errorText; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Sample App"), ), body: Center( child: TextField( onSubmitted: (String text) { setState(() { if (!isEmail(text)) { _errorText = 'Error: This is not an email'; } else { _errorText = null; } }); }, decoration: InputDecoration(hintText: "This is a hint", errorText: _getErrorText()), ), ), ); } _getErrorText() { return _errorText; } bool isEmail(String emailString) { String emailRegexp = r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'; RegExp regExp = RegExp(emailRegexp); return regExp.hasMatch(emailString); } } 

Flutter


:


GPS?

:


geolocator .

:


?

:


image_picker .

:


Facebook?

:


flutter_facebook_login .

:


Firebase?

:


Firebase Flutter first party plugins :


:


() ?

:


Flutter EventBus . : developing packages and plugins .


:


UserDefault?

:


Shared_Preferences plugin ( Shared Preferences Android ).

مثال:


 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.'); prefs.setInt('counter', counter); } 

:


Core Data ?

:


SQFlite .


:


push-?

:


Firebase_Messaging .

استنتاج


. , , . « » . «-» . , ? 2016 Kotlin, - 2017. , , . , .
2016 Flutter Dart. , 2018 . . ! , , , . ( Google Fuchsia , , , Flutter ). — ! , — . . Apple Store!

Source: https://habr.com/ru/post/ar477182/


All Articles