Flutter Splash Screen متحركة باستخدام Lottie بدون Flutter Package
هل تريد إنشاء Splash Screen احترافية في Flutter تحتوي على Animation باستخدام Lottie، ولكن بدون إضافة package خاصة بـ Flutter إلى المشروع؟
بدلًا من الاعتماد على مكتبات Flutter التقليدية، يمكنك تنفيذ شاشة البداية بشكل Native باستخدام كود Android الأصلي بلغة Kotlin وكود iOS الأصلي بلغة Swift، بحيث يتم تشغيل الأنيميشن قبل تحميل واجهة Flutter.
هذه الفكرة هي جوهر مشروع Native Lottie Splash Screen الموجود على GitHub، والذي يوضح كيفية إنشاء شاشة Splash أصلية تعمل على Android وiOS وتنتقل تلقائيًا إلى Flutter بعد انتهاء الأنيميشن.
الميزة الأساسية هنا أن شاشة البداية لا تعتمد على Widget داخل Flutter، بل تبدأ من طبقة النظام الأصلية للتطبيق، ثم يتم تشغيل Flutter بعد ذلك.
ما هي فكرة Native Splash Screen في Flutter؟
عند إنشاء Splash Screen بطريقة Flutter التقليدية، تحتاج أولًا إلى تشغيل Flutter Engine ثم بناء الـ Widget الخاص بالشاشة، وهذا يعني أن شاشة البداية نفسها تصبح جزءًا من واجهة Flutter.
أما في الطريقة الحالية، فيتم إنشاء شاشة البداية داخل Native Layer:
- Android يستخدم Activity مكتوبة بلغة Kotlin.
- iOS يستخدم ViewController مكتوبًا بلغة Swift.
- ملف Lottie يتم تحميله داخل الطبقة الأصلية.
- بعد انتهاء الأنيميشن يتم الانتقال إلى Flutter.
بمعنى آخر، Flutter لا يكون مسؤولًا عن رسم شاشة البداية نفسها، وإنما يبدأ بعد انتهاء مرحلة الـ Splash الأصلية.
هل الطريقة تعمل بدون أي Package؟
هنا توجد نقطة مهمة يجب توضيحها.
المشروع لا يستخدم Flutter Lottie Package داخل pubspec.yaml لتشغيل الأنيميشن، وإنما يعتمد على مكتبات Lottie الأصلية لكل منصة.
على Android يتم استخدام مكتبة Lottie الخاصة بـ Airbnb من خلال طبقة Android الأصلية، وعلى iOS يتم استخدام lottie-ios من خلال CocoaPods. ويعرض المستودع إصدارات Lottie المستخدمة ضمن الـ Tech Stack.
لذلك التعبير الأدق هو: Native Lottie Splash بدون Flutter Package.
كيف تعمل الشاشة قبل Flutter؟
الفكرة تعتمد على أن نظام التشغيل يبدأ من نقطة Native قبل الدخول إلى شاشة Flutter الرئيسية.
على Android، يتم تعريف SplashActivity كـ Launcher Activity داخل AndroidManifest.xml، وبعد تشغيلها يتم عرض Lottie ثم فتح MainActivity.
أما على iOS، فيقوم AppDelegate بتعيين SplashViewController كواجهة البداية، وبعد انتهاء الأنيميشن يتم إنشاء FlutterViewController وعرضه.
بنية المشروع
المستودع مقسم بطريقة توضح مكان كل جزء من الحل:
native_lottie_without_any_package/
├── android/app/src/main/
│ ├── kotlin/com/example/native_lottie_without_any_package/
│ │ ├── MainActivity.kt
│ │ └── SplashActivity.kt
│ ├── res/
│ │ ├── layout/
│ │ │ └── activity_splash.xml
│ │ └── raw/
│ │ └── splash_logo.json
│ └── AndroidManifest.xml
│
├── ios/Runner/
│ ├── AppDelegate.swift
│ ├── SplashViewController.swift
│ └── splash_logo.json
│
├── lib/
│ └── main.dart
│
└── README.mdهذا التنظيم يفصل بشكل واضح بين شاشة البداية الأصلية وبين تطبيق Flutter نفسه.
تنفيذ Splash Screen على Android باستخدام Kotlin
نبدأ من Android بإنشاء SplashActivity تكون مسؤولة عن تشغيل Animation ثم الانتقال إلى MainActivity.
package com.example.native_lottie_without_any_package
import android.os.Bundle
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import com.airbnb.lottie.LottieAnimationView
class SplashActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
val lottieAnimation =
findViewById<LottieAnimationView>(R.id.lottieAnimation)
lottieAnimation.playAnimation()
lottieAnimation.postDelayed({
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
finish()
}, 3000)
}
}الكود يقوم بعدة خطوات بسيطة:
- تحميل Layout الخاص بالـ Splash.
- الحصول على
LottieAnimationView. - تشغيل الأنيميشن باستخدام
playAnimation(). - بعد 3000 ملي ثانية يتم الانتقال إلى
MainActivity. - استخدام
finish()يمنع الرجوع إلى Splash Activity.
هذا السلوك موجود مباشرة في المشروع، حيث يتم تشغيل الأنيميشن ثم فتح شاشة Flutter بعد ثلاث ثوانٍ وإنهاء الـ Activity الحالية.
إنشاء واجهة Lottie في Android
واجهة Splash نفسها موجودة داخل ملف XML، ويتم وضع عنصر LottieAnimationView في منتصف الشاشة.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF">
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottieAnimation"
android:layout_width="300dp"
android:layout_height="300dp"
android:layout_centerInParent="true"
app:lottie_rawRes="@raw/splash_logo"
app:lottie_loop="false"
app:lottie_autoPlay="false" />
</RelativeLayout> الأنيميشن هنا يتم تحميله من ملف splash_logo.json الموجود داخل مجلد res/raw، وحجم عنصر Lottie في المشروع هو 300dp × 300dp مع وضعه في منتصف الشاشة.
إضافة SplashActivity إلى AndroidManifest.xml
حتى يعمل النظام على تشغيل شاشة Splash قبل Flutter، يجب تعريف SplashActivity على أنها نقطة الدخول للتطبيق.
<activity
android:name=".SplashActivity"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity> وجود MAIN وLAUNCHER يجعل Android يبدأ من SplashActivity بدلًا من تشغيل شاشة Flutter مباشرة.
تنفيذ Splash Screen على iOS باستخدام Swift
في iOS يتم تنفيذ نفس الفكرة ولكن باستخدام UIViewController مخصص لشاشة البداية.
import UIKit
import Lottie
class SplashViewController: UIViewController {
private var animationView: LottieAnimationView?
override func viewDidLoad() {
super.viewDidLoad()
setupLottieAnimation()
}
private func setupLottieAnimation() {
animationView =
LottieAnimationView(name: "splash_logo")
animationView?.frame =
CGRect(x: 0, y: 0, width: 300, height: 300)
animationView?.center = view.center
animationView?.contentMode = .scaleAspectFit
animationView?.loopMode = .playOnce
view.addSubview(animationView!)
animationView?.play { [weak self] _ in
self?.navigateToFlutter()
}
}
private func navigateToFlutter() {
let flutterViewController =
FlutterViewController()
flutterViewController.modalPresentationStyle =
.fullScreen
self.present(
flutterViewController,
animated: true,
completion: nil
)
}
} هنا يتم إنشاء LottieAnimationView باستخدام اسم ملف الأنيميشن، ثم وضعه في مركز الشاشة وتشغيله مرة واحدة فقط.
وعند انتهاء الأنيميشن يتم استدعاء navigateToFlutter() لإنشاء FlutterViewController وعرض التطبيق.
ربط SplashViewController مع AppDelegate
حتى يبدأ التطبيق من شاشة الـ Splash الأصلية، يقوم AppDelegate بتعيين SplashViewController كـ Root View Controller.
import UIKit
import Flutter
@main
@objc class AppDelegate:
FlutterAppDelegate,
FlutterImplicitEngineDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let splashVC = SplashViewController()
window?.rootViewController = splashVC
window?.makeKeyAndVisible()
GeneratedPluginRegistrant.register(with: self)
return super.application(
application,
didFinishLaunchingWithOptions: launchOptions
)
}
}بهذه الطريقة تصبح شاشة Splash هي أول View يتم عرضها، ثم تنتقل إلى Flutter بعد اكتمال Lottie.
إضافة ملف Lottie Animation
كل ما تحتاجه هو ملف Lottie بصيغة JSON، ويمكنك الحصول عليه من مصادر مثل LottieFiles أو تصديره من التصميم الخاص بك.
في Android يوضع الملف داخل:
android/app/src/main/res/raw/splash_logo.jsonأما في iOS فيوضع داخل:
ios/Runner/splash_logo.jsonوعند تغيير ملف الأنيميشن يمكنك استخدام ملف JSON آخر بنفس الاسم، دون الحاجة إلى تعديل منطق التطبيق الأساسي.
هل يمكن تغيير مدة الـ Splash Screen؟
نعم، ويمكن تعديل ذلك بسهولة على Android.
lottieAnimation.postDelayed({
startActivity(
Intent(this, MainActivity::class.java)
)
finish()
}, 3000) القيمة 3000 تمثل 3000 ملي ثانية، أي حوالي 3 ثوانٍ.
يمكنك تغييرها حسب مدة الأنيميشن، لكن الأفضل عمليًا أن تكون مدة Splash مناسبة للأنيميشن نفسه حتى لا يبقى التطبيق على شاشة البداية بعد انتهاء الحركة أو ينتقل منها قبل اكتمالها.
تشغيل Lottie مرة واحدة أو بشكل متكرر
في المثال الموجود في المشروع يتم تشغيل الأنيميشن مرة واحدة، وبعد ذلك يتم الانتقال إلى Flutter.
في Android تم تعطيل التكرار في XML:
app:lottie_loop="false"بينما في iOS تم تحديد:
animationView?.loopMode = .playOnceوهذا مناسب جدًا لشاشات البداية، لأن الهدف منها عرض حركة قصيرة ثم الانتقال مباشرة إلى التطبيق.
تغيير لون خلفية Splash Screen
يمكنك تخصيص الخلفية بما يتوافق مع هوية التطبيق.
في Android يمكن تعديل:
android:background="#FFFFFF"أما في iOS فيمكنك تعديل:
view.backgroundColor = .whiteوبذلك تستطيع جعل الخلفية متوافقة مع ألوان الشعار أو ملف Lottie بدل استخدام اللون الأبيض الافتراضي.
ما الفرق بين Flutter Splash وNative Splash؟
| الطريقة | مكان التنفيذ | الأنيميشن | وقت الظهور |
|---|---|---|---|
| Flutter Splash | داخل Flutter | متاح | بعد بدء Flutter |
| Native Lottie Splash | Native Android/iOS | متاح | قبل شاشة Flutter |
| Native Static Splash | Native Android/iOS | لا يوجد | قبل شاشة Flutter |
الفكرة الأساسية في الحل الموجود بالمستودع هي الجمع بين ميزات الـ Native Splash المبكر وبين إمكانية تشغيل Animation باستخدام Lottie قبل عرض تطبيق Flutter.
لماذا قد تحتاج إلى Native Lottie Splash؟
هناك عدة أسباب تجعل هذا الأسلوب مناسبًا لبعض التطبيقات التي تريد بداية أكثر سلاسة:
- تشغيل الأنيميشن في الطبقة الأصلية.
- عدم الحاجة إلى Flutter Widget لعرض Splash.
- تقليل ظهور الشاشة البيضاء قبل الأنيميشن.
- إمكانية التحكم في مدة الأنيميشن وسلوك الانتقال.
- توفير تجربة متقاربة بين Android وiOS.
المستودع نفسه يوضح أن الهدف هو تجنب ظهور الوميض الأبيض وربط نهاية الأنيميشن بالانتقال إلى Flutter.
هل يوجد أي Package في Flutter؟
لا تحتاج إلى إضافة package مثل lottie داخل قسم dependencies في Flutter لاستخدام هذا الأسلوب.
لكن هذا لا يعني أن المشروع خالٍ تمامًا من مكتبات خارجية؛ فـ Lottie نفسها تعمل من خلال المكتبات الأصلية للمنصتين.
المشروع يستخدم Lottie Android بإصدار 6.2.0 وlottie-ios بإصدار يقارب 4.3.0 بحسب الـ Tech Stack وملف Podfile في المستودع.
متطلبات تشغيل المشروع
بحسب المستودع، تحتاج إلى مشروع Flutter حديث نسبيًا مع Flutter SDK من إصدار 3.x أو أحدث، بالإضافة إلى Android Studio أو Xcode وملف Lottie بصيغة JSON.
وبعد تحميل المشروع يمكنك تشغيل:
git clone https://github.com/0Ahmad0/native_lottie_without_any_package.git
cd native_lottie_without_any_package
flutter pub get
flutter runوعلى iOS يتم أيضًا تنفيذ:
cd ios
pod install
cd ..ثم تشغيل التطبيق بالطريقة المعتادة.
متى يكون هذا الأسلوب مناسبًا؟
يمكن استخدام Native Lottie Splash في التطبيقات التي تريد أن تبدأ بهوية بصرية قوية، مثل تطبيقات الشركات والمتاجر والتطبيقات الاجتماعية والتطبيقات التي تحتوي على Logo Animation.
خصوصًا عندما تكون مدة الأنيميشن قصيرة وتريد أن تكون شاشة البداية جزءًا من تجربة فتح التطبيق، وليس مجرد شاشة ثابتة تحتوي على الشعار.
ملاحظات مهمة قبل استخدام Native Lottie Splash
- يجب أن تتطابق مدة الانتقال مع مدة الأنيميشن حتى لا تظهر حركة انتهت بينما التطبيق ما زال في Splash.
- يفضل استخدام Animation خفيف وسريع حتى لا يشعر المستخدم أن التطبيق يتأخر في الفتح.
- اختبر Android وiOS بشكل منفصل لأن دورة تشغيل Native تختلف بين النظامين.
- تأكد من إضافة ملفات Lottie إلى المسارات الصحيحة داخل المشروع.
- تذكر أن Native Lottie لا يلغي الحاجة إلى مكتبة Lottie الأصلية نفسها؛ ما يتم الاستغناء عنه هو Flutter package الخاصة بتشغيل Lottie.
⚖️ المميزات والعيوب
✔️ المميزات:
- تشغيل Splash خارج واجهة Flutter.
- دعم Lottie Animation على Android وiOS.
- لا تحتاج إلى Flutter Lottie Widget.
- تحكم كامل في كود Android وiOS الأصلي.
- إمكانية تخصيص مدة الأنيميشن والخلفية.
- الانتقال تلقائيًا إلى Flutter بعد انتهاء الحركة.
العيوب:
- ستتعامل مع Kotlin وSwift بدل Dart فقط.
- تحتاج إلى تعديل ملفات Android وiOS داخل المشروع.
- أي تغيير كبير في Splash قد يتطلب تعديل الكود الأصلي لكل منصة.
- تظل بحاجة إلى مكتبات Lottie الأصلية لتشغيل ملفات Lottie.
الخلاصة
إذا كنت تبحث عن طريقة لإنشاء Splash Screen في Flutter باستخدام Lottie بدون إضافة Flutter package، فإن استخدام Native Android وNative iOS يمثل أسلوبًا عمليًا ومختلفًا عن بناء Splash بالكامل داخل Flutter.
على Android يمكنك استخدام SplashActivity مع LottieAnimationView، بينما على iOS يمكنك استخدام SplashViewController مع LottieAnimationView، ثم الانتقال إلى واجهة Flutter بعد انتهاء الأنيميشن.
الميزة الأهم هي أنك تحصل على شاشة بداية Native ذات Animation، مع إمكانية التحكم الكامل في الانتقال والتصميم وسلوك Lottie، دون الحاجة إلى الاعتماد على Widget من Flutter لعرض الـ Splash.
يمكنك الاطلاع على المشروع كاملًا وتطبيق الفكرة مباشرة من خلال المستودع:
Native Lottie Splash Screen بدون Flutter Package على GitHub
شرح مكتبة Safe Image لفلترة الصور الحساسة بالذكاء الاصطناعي
مزيد من المقالات : flutter_api_helper في Flutter: دليلك العملي لتبسيط التعامل مع REST APIs






