قص الفيديو في Flutter باستخدام video_trimmer يتيح للمستخدم اختيار بداية المقطع ونهايته ثم تصدير الجزء المطلوب، مثل تجهيز فيديو قصير قبل نشره في تطبيق اجتماعي. يجب الفصل بين معاينة الجزء المحدد وحفظ ملف جديد؛ تحريك المقابض على الشريط الزمني وحده لا يغيّر الفيديو الأصلي ولا ينتج ملفًا جاهزًا للرفع.
تثبيت video_trimmer واختيار الملف
هذا الشرح يستهدف واجهة الإصدار 6.0.0 التي جرت مراجعتها في نوفمبر 2026. أضف الاعتماد التالي إلى pubspec.yaml ثم نفّذ flutter pub get. إذا كان مشروعك يستخدم إصدارًا قديمًا، اقرأ دليل التغييرات قبل الترقية؛ تغير اسم وسيط تحميل الفيديو وأصبحت نتيجة الحفظ تصل عبر onSave. لذلك لا يكفي تغيير رقم الإصدار في مشروع قديم.
dependencies:
video_trimmer: ^6.0.0اختيار الفيديو خطوة مستقلة تنفذها بإضافة اختيار ملفات مناسبة. عند إلغاء المستخدم نافذة الاختيار، ابقَ في الشاشة نفسها ولا تستدعِ التحميل بمسار فارغ. احصل على المسار من الملف الذي اختاره المستخدم فعليًا، ولا تفترض أن مسارًا ثابتًا على جهاز المطور سيعمل عند الآخرين. صلاحيات اختيار الصور والفيديو تتبع إضافة الاختيار والمنصة، وليست سببًا لطلب صلاحيات الكاميرا والميكروفون إن كنت لا تستخدمهما.
تحميل الفيديو وعرض أدوات القص
أنشئ Trimmer واحدًا طوال عمر شاشة التحرير، ثم انتظر loadVideo قبل إظهار أدوات تعتمد على مدة المقطع. المثال التالي يوضح الاستدعاءات الأساسية داخل State؛ المتغير path هو مسار الملف المختار مسبقًا. احتفظ بحالة تحميل ورسالة خطأ في الشاشة حتى لا يرى المستخدم مساحة فارغة عند فشل قراءة الملف.
final trimmer = Trimmer();
double startMs = 0;
double endMs = 0;
Future<void> loadSelectedVideo(String path) async {
if (path.isEmpty) return;
await trimmer.loadVideo(videoPath: path);
}
// After successful loading, inside build:
// VideoViewer(trimmer: trimmer)
// TrimViewer(
// trimmer: trimmer,
// viewerWidth: MediaQuery.sizeOf(context).width,
// maxVideoLength: const Duration(seconds: 30),
// onChangeStart: (value) => startMs = value,
// onChangeEnd: (value) => endMs = value,
// onChangePlaybackState: (value) {},
// )قيم onChangeStart وonChangeEnd مقاسة بالمللي ثانية، بينما maxVideoLength يستقبل Duration. الخلط بين الثانية والمللي ثانية قد ينتج نطاقًا أقصر بكثير مما اختاره المستخدم. اجعل الحد الأقصى مناسبًا لسياسة منتجك، واختبر ملفًا أقصر منه وملفًا أطول منه. لا تُنشئ Trimmer جديدًا في كل استدعاء build، لأن إعادة بناء الواجهة لا تعني بدء جلسة تحرير جديدة.
حفظ الجزء المحدد والتعامل مع فشل التصدير
قبل التصدير تأكد أن النهاية أكبر من البداية، ثم عطّل زر الحفظ أثناء العمل لمنع عمليات متزامنة. تعيد دالة الحفظ Future<void>؛ مسار الناتج يصل إلى onSave وقد يكون null عند الفشل. لا تعرض رسالة نجاح بناءً على اكتمال Future وحده، ولا ترفع الملف الأصلي بدل الناتج بالخطأ.
Future<String?> saveSelection() async {
if (endMs <= startMs) {
throw StateError('Choose a valid video range');
}
String? outputPath;
await trimmer.saveTrimmedVideo(
startValue: startMs,
endValue: endMs,
onSave: (path) => outputPath = path,
);
return outputPath;
}
// In State.dispose:
// trimmer.dispose();
// super.dispose();ضع استدعاء saveSelection داخل try/catch في حدث الزر، واستخدم finally لإعادة حالة الزر سواء نجح التصدير أم فشل. عند تعديل الواجهة بعد await تحقق من mounted. إذا أعاد الناتج null، اطلب من المستخدم إعادة المحاولة بعد مراجعة الملف ومساحة التخزين، بدل عرض مسار غير صالح. تخلص من Trimmer عند مغادرة الشاشة لتحرير موارد المشغّل.
اختبار الناتج قبل رفعه أو مشاركته
شغّل الفيديو الناتج للتحقق من مدته وصوته واتجاهه، ولا تعتمد على الصورة المصغرة وحدها. جرّب ملفات عمودية وأفقية ومقطعًا كبيرًا وفيديو بصيغة لا يدعمها الجهاز. على الويب يكون الناتج رابط blob وليس مسار ملف محليًا؛ لا تعالجه كمسار File ولا تخزنه على أنه رابط دائم صالح لمستخدم آخر. اختر آلية رفع مناسبة لبيانات الملف، واعرض تقدم الرفع منفصلًا عن تقدم التصدير.
قص الفيديو يختلف عن ضغطه أو تحميله مسبقًا؛ تقليل المدة لا يضمن معدل نقل محددًا. إذا كانت المشكلة في بدء التشغيل أثناء تصفح قائمة فيديوهات، راجع درس مكتبات Flutter واختر درس تشغيل الفيديو أو التحميل المسبق من الروابط المرتبطة أدناه. احتفظ بالملف الأصلي حتى نجاح الحفظ، ثم طبّق سياسة واضحة لتنظيف الملفات المؤقتة دون حذف ملفات المستخدم.
للتوسع في جانب قريب من هذا التطبيق، راجع عرض Stories في Flutter باستخدام advstory، ثم قارن طريقة الاستخدام مع متطلبات مشروعك.






