يا هلا بيك يا بشمهندس! لو بتطور تطبيق موبايل بـ فلاتر (Flutter)، أكيد في لحظة معينة جالِك أكلينت أو صاحب فكرة وقال لك: "بقولك إيه، أنا عايز المستخدم يقدر يشوف آخر التحديثات من غير ما يفتح التطبيق، يعني عايزين نعمل ويدجت للشاشة الرئيسية (Home Screen Widgets)". هنا بقى بيجي الوجع الحقيقي؛ لأن Flutter لوحدها مش بتعرف ترسم على الشاشة الرئيسية (Home Screen) بشكل مباشر، لأن الـ Widgets بتاعة فلاتر عايشة جوة الـ Flutter Engine، وشاشة الموبايل الرئيسية ليها قوانين تانية خالص ولغة نيتف (Native) صريحة.
في المقال ده، هنتكلم بالتفصيل عن إزاي نبني ويدجت الشاشة الرئيسية (Home Screen Widgets) لكل من أنظمة التشغيل أندرويد (Android) وآي أو إس (iOS) باستخدام فلاتر، وازاي نربط الدنيا ببعضها من غير ما نوجع دماغنا بكود نيتف كتير.
Table of contents [Show]
المستخدم العادي بيفتح الموبايل عشرات المرات في اليوم. لو تطبيقك ظاهر قدامه على شاشة الموبايل الرئيسية (Home Screen) بيدي له معلومة سريعة ومفيدة (زي الطقس، أسعار العملات، أو آخر مهام مطلوبة منه)، نسبة إن التطبيق يمسحه أو ينساه بتـقل جداً. التفاعل (Engagement) بيزيد والسشنز (Sessions) بتاعت التطبيق بتطير للسما.
المشكلة فين بقى؟ الفلاتر (Flutter) عبارة عن إسكرينة واحدة كبيرة مرسومة بـ Skia أو Impeller، لكن الويدجت النيتف (Native Widgets) زي الـ AppWidgets في أندرويد و WidgetKit في آي أو إس، هي عبارة عن عناصر منفصلة تماماً بتشتغل لوحدها في الخلفية (Background) وبتحتاج كود نيتف خفيف عشان تظهر على الشاشة.
الحل السحري للموضوع ده هو استخدام مكتبات جاهزة (Packages) بتقوم بدور الوسيط، زي مكتبة home_widget المشهورة. المكتبة دي بتسمح لنا نكتب جزء من البيانات من فلاتر، ونبعتها للناحية النيتف (Native Side) عشان تعرضها في الويدجت.
الفكرة باختصار:
- بتصمم شكل الويدجت بلغة نيتف خفيفة جداً (SwiftUI للـ iOS و XML أو Jetpack Compose للـ Android).
- بتستخدم فلاتر عشان تحدث البيانات (Data) وتبعتها للـ Native باستخدام Shared Preferences أو App Groups.
- بتعمل تحديث (Update) للويدجت عشان تعرض البيانات الجديدة.
عشان نعمل ويدجت للآيفون، لازم نفتحه من خلال برنامج إكس كود (Xcode). الخطوات كالتالي:
- افتح مشروع فلاتر بتاعك على إكس كود (فتح ملف Runner.xcworkspace).
- من قائمة File اختار New ثم Target.
- اختار Widget Extension وسمّيها مثلاً
MyWidget. - تأكد من تفعيل خيار Include Live Activity لو احتجتها، واضغط Finish.
دلوقت إكس كود هيعمل ملفات الـ SwiftUI الخاصة بالويدجت. الكود بيكون بالشكل ده تقريباً:
import WidgetKit
import SwiftUI
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> SimpleEntry {
SimpleEntry(date: Date(), message: "أهلاً بك في تطبيقنا")
}
func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) {
let entry = SimpleEntry(date: Date(), message: "آخر تحديث")
completion(entry)
}
func timeline(in context: Context, completion: @escaping (Timeline) -> ()) {
// هنا بنجيب البيانات اللي باعتها الفلاتر
let userDefaults = UserDefaults(suiteName: "group.com.example.app")
let message = userDefaults?.string(forKey: "widget_message") ?? "مافيش بيانات"
let entry = SimpleEntry(date: Date(), message: message)
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
}
struct SimpleEntry: TimelineEntry {
let date: Date
let message: String
}
struct MyWidgetEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack {
Text("تحديث جديد:")
Text(entry.message)
.font(.headline)
}
}
}
في الأندرويد الموضوع بيعتمد على إحنا بنحتاج نعمل تصميم XML للويدجت، وكلاس بيورث من AppWidgetProvider. الكود بيبقى جوه مجلد android/app/src/main.
بنعمل ملف تخطيط (Layout) وليكن اسمه widget_layout.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#1E1E1E"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/widget_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="جاري التحميل..."
android:textColor="#FFFFFF"
android:textSize="16sp" />
</LinearLayout>
وبعدين بنربط الكلام ده بكود الفلاتر عن طريق مكتبة home_widget بالشكل ده:
import 'package:home_widget/home_widget.dart';
// دالة لتحديث البيانات وإرسالها للنيتف
void updateAppWidget(String message) async {
await HomeWidget.saveWidgetData('widget_message', message);
await HomeWidget.updateWidget(
name: 'MyWidgetProvider',
androidName: 'MyWidgetProvider',
iOSName: 'MyWidget',
);
}
نصيحة من أخ لتطوير مهاراتك
موضوع الـ Home Screen Widgets في الأول ممكن تحس إنه مكلكع عشان فيه شغل نيتف (Native)، بس السر كله في الممارسة. متخافش تفتح إكس كود أو أندرويد استوديو وتعدل في الكود النيتف الخفيف ده. ابدأ بتطبيق صغير بيعرض نص بسيط (Text)، وبعدين طوّره بحيث يعرض صور (Images) أو بيانات بتتحدث من واجهة برمجية (API). العب بالمكتبات واقرأ الدوكومنتشن (Documentation) كويس، وصدقني الخطوة دي هتنقل السيرة الذاتية بتاعتك لمستوى تاني خالص قدام الشركات الكبيرة.