شرح Flutter Time Guard للتحقق من وقت الجهاز ومنع التلاعب بالوقت في Flutter
عند تطوير تطبيق Flutter يعتمد على الوقت والتاريخ، قد لا يكون الاعتماد على الوقت الموجود داخل جهاز المستخدم كافيًا في بعض الحالات.
فمثلًا، إذا كان لديك تطبيق يحتوي على اشتراك ينتهي في تاريخ معين، أو تجربة مجانية لمدة محددة، أو نظام حضور وانصراف، أو حجوزات، أو أكواد OTP، فإن المستخدم يستطيع تغيير تاريخ ووقت الجهاز يدويًا، وقد يؤدي ذلك إلى نتائج غير صحيحة إذا كان التطبيق يعتمد بشكل مباشر على DateTime.now().
هنا تأتي مكتبة flutter_time_guard التي توفر طريقة للتحقق من صحة وقت الجهاز ومقارنته بوقت موثوق من خلال خوادم NTP، بالإضافة إلى إمكانية مراقبة تغييرات التاريخ والوقت والمنطقة الزمنية على أجهزة Android وiOS.
في هذا المقال سنتعرف بالتفصيل على مكتبة Flutter Time Guard، وكيفية تثبيتها، وكيفية التحقق من وقت الجهاز، وكيفية اكتشاف تغيير الوقت يدويًا، وكيفية استخدام NTP، وكيفية التعامل مع انقطاع الإنترنت، بالإضافة إلى مثال عملي كامل يمكن استخدامه داخل تطبيق Flutter.
ما هي مكتبة Flutter Time Guard؟
Flutter Time Guard هي مكتبة Flutter مصممة للتطبيقات التي تعتمد على الوقت الحقيقي وتحتاج إلى اكتشاف التلاعب بساعة الجهاز أو التأكد من أن الوقت الموجود على الهاتف قريب من الوقت الموثوق من الشبكة.
المكتبة تستطيع مراقبة تغييرات التاريخ والوقت والمنطقة الزمنية، كما يمكنها التحقق من وقت الجهاز باستخدام خوادم NTP مع إمكانية تحديد مقدار السماح بالاختلاف بين الوقت المحلي والوقت الموثوق.
وتدعم المكتبة حاليًا:
- Android.
- iOS.
- التحقق من الوقت باستخدام NTP.
- اكتشاف تغيير التاريخ والوقت يدويًا.
- اكتشاف تغيير المنطقة الزمنية.
- تحديد مقدار السماح بالاختلاف في الوقت.
- تخزين آخر وقت موثوق لاستخدامه عند انقطاع الإنترنت.
- إضافة Callbacks عند اكتشاف تغيير الوقت.
- Logging اختياري أثناء التطوير.
الحزمة الحالية هي 1.3.1، ومرخصة تحت MIT، وتدعم Android وiOS.
لماذا نحتاج إلى التحقق من وقت الجهاز في Flutter؟
في Flutter يمكنك الحصول على الوقت الحالي بسهولة باستخدام:
final now = DateTime.now();لكن هذا الوقت يعتمد على الساعة الموجودة في جهاز المستخدم.
إذا قام المستخدم بتغيير التاريخ من إعدادات الهاتف، فإن:
DateTime.now()سيعطي وقت الجهاز الجديد.
وهذا قد يكون غير مناسب للتطبيقات التي تعتمد على الوقت كجزء من منطق أمني أو تجاري.
على سبيل المثال، لديك اشتراك ينتهي غدًا، ويمكن للمستخدم تغيير تاريخ الهاتف إلى بعد عدة أيام أو أشهر، مما قد يؤدي إلى تنفيذ منطق التطبيق بناءً على وقت غير صحيح.
لذلك نحتاج إلى مصدر خارجي يمكن استخدامه للتحقق من الوقت، وهنا تستخدم المكتبة NTP.
ما هو NTP؟
اختصار NTP يعني Network Time Protocol، وهو بروتوكول يستخدم للحصول على وقت موثوق من خوادم الشبكة.
بدل أن يعتمد التطبيق على:
DateTime.now()يمكنه مقارنة وقت الجهاز بوقت قادم من الشبكة ومعرفة ما إذا كان هناك اختلاف يتجاوز الحد المسموح.
وهذه الفكرة هي الأساس الذي تعتمد عليه مكتبة Flutter Time Guard للتحقق من وقت الجهاز.
الكلمة المفتاحية: التحقق من وقت الجهاز في Flutter
إذا كنت تبحث عن طريقة لتنفيذ التحقق من وقت الجهاز في Flutter، فإن Flutter Time Guard توفر API جاهزًا بدل بناء نظام كامل لاكتشاف تغيير الوقت ومقارنة الوقت المحلي بوقت الشبكة.
يمكنك استخدام المكتبة في التطبيقات التي تعتمد على الاشتراكات، التجارب المجانية، الحجوزات، الحضور، الألعاب، المكافآت، OTP أو أي عملية أخرى يكون للوقت فيها تأثير على منطق التطبيق.
تثبيت مكتبة Flutter Time Guard
يمكنك إضافة المكتبة إلى مشروع Flutter باستخدام الأمر التالي:
flutter pub add flutter_time_guard أو إضافتها مباشرة إلى ملف pubspec.yaml:
dependencies:
flutter:
sdk: flutter
flutter_time_guard: ^1.3.1بعد ذلك قم بتشغيل:
flutter pub get الإصدار الحالي المنشور من الحزمة هو 1.3.1.
استيراد المكتبة
بعد تثبيت المكتبة يمكنك استيرادها داخل ملف Dart:
import 'package:flutter_time_guard/flutter_time_guard.dart';التحقق من صحة وقت الجهاز
أهم API في المكتبة هو:
FlutterTimeGuard.isDateTimeValid() وهو يعيد Future<bool> يوضح ما إذا كان وقت الجهاز ضمن مقدار السماح المحدد مقارنة بالوقت الموثوق.
مثال بسيط:
final isValid = await FlutterTimeGuard.isDateTimeValid();
print('Is time valid? $isValid');إذا كانت النتيجة:
trueفهذا يعني أن الوقت يعتبر صالحًا وفق إعدادات التحقق.
أما:
falseفتعني أن وقت الجهاز تجاوز مقدار السماح المحدد مقارنة بالوقت المرجعي.
تحديد مقدار السماح بالاختلاف
يمكنك تحديد مقدار السماح بالاختلاف من خلال:
toleranceInSecondsمثلًا:
final isValid = await FlutterTimeGuard.isDateTimeValid(
toleranceInSeconds: 60,
);
print(isValid);في هذا المثال نسمح باختلاف يصل إلى دقيقة واحدة.
ويمكن أيضًا استخدام:
final isValid = await FlutterTimeGuard.isDateTimeValid(
toleranceInSeconds: 300,
);أي السماح باختلاف يصل إلى 5 دقائق.
القيمة الافتراضية التي توضحها وثائق المكتبة هي 86400 ثانية، أي 24 ساعة.
اختيار tolerance المناسب
لا يوجد رقم واحد مناسب لجميع التطبيقات.
على سبيل المثال، تطبيق تسجيل حضور قد يسمح بفارق معين، بينما نظام يعتمد على Credential محدود المدة قد يحتاج إلى سياسة أكثر صرامة.
لذلك تستطيع تحديد:
toleranceInSeconds: 60أو:
toleranceInSeconds: 300بحسب طبيعة الـ Business Logic داخل التطبيق.
اكتشاف تغيير التاريخ والوقت
بالإضافة إلى التحقق من الوقت، توفر المكتبة Listener لمراقبة تغييرات التاريخ والوقت والمنطقة الزمنية.
يمكنك استخدام:
FlutterTimeGuard.listenToDateTimeChange(
onTimeChanged: () {
print('System time has changed');
},
);عند اكتشاف تغيير، سيتم تنفيذ Callback الموجود داخل:
onTimeChangedوبالتالي يمكنك عرض رسالة للمستخدم أو إعادة التحقق من الوقت أو تسجيل الحدث في Analytics.
إيقاف Listener بعد أول تغيير
تحتوي المكتبة على خيار:
stopListeingAfterFirstChange لاحظ أن اسم الـ parameter في API هو stopListeingAfterFirstChange بهذا الشكل.
يمكن استخدامه كالتالي:
FlutterTimeGuard.listenToDateTimeChange(
stopListeingAfterFirstChange: true,
onTimeChanged: () {
print('Time changed');
},
);وعند تفعيل الخيار يمكن إيقاف الاستماع بعد اكتشاف أول تغيير، وهو مفيد عندما يكون المطلوب تنفيذ الإجراء مرة واحدة بدل استقبال إشعارات متعددة.
إعادة التحقق بعد اكتشاف تغيير الوقت
من المهم معرفة أن Listener وحده لا يعني بالضرورة أن المستخدم قام بتغيير الوقت يدويًا.
قد يقوم نظام التشغيل نفسه بتعديل الساعة بسبب مزامنة تلقائية أو تغيير المنطقة الزمنية أو أسباب أخرى.
لذلك توفر المكتبة مثالًا يقوم بإعادة التحقق من الوقت داخل Callback قبل إظهار التحذير للمستخدم.
FlutterTimeGuard.listenToDateTimeChange(
stopListeingAfterFirstChange: true,
onTimeChanged: () async {
final isValidTime =
await FlutterTimeGuard.isDateTimeValid(
toleranceInSeconds: 86400,
);
if (isValidTime) {
return;
}
print('Invalid device time');
},
);بهذه الطريقة يمكن تقليل الحالات التي يتم فيها اعتبار المزامنة التلقائية للنظام تلاعبًا بالوقت.
عرض Dialog عند تغيير الوقت
يمكن استخدام Listener لعرض رسالة للمستخدم.
FlutterTimeGuard.listenToDateTimeChange(
stopListeingAfterFirstChange: true,
onTimeChanged: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text(
'Invalid Device Time',
),
content: const Text(
'Please make sure that your device date and time are correct.',
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'),
),
],
);
},
);
},
);استخدام Global Navigator Key
إذا كنت تريد إظهار Dialog من مكان لا يتوفر فيه BuildContext بسهولة، يمكنك استخدام GlobalKey<NavigatorState>.
final navigatorKey = GlobalKey<NavigatorState>();ثم إضافته إلى:
MaterialApp(
navigatorKey: navigatorKey,
home: const HomePage(),
);وبعد ذلك يمكنك الوصول إلى Context:
final context = navigatorKey.currentContext;وهذه هي الطريقة المستخدمة أيضًا في المثال المتقدم الموجود في توثيق المكتبة.
ماذا يحدث عند انقطاع الإنترنت؟
هذه نقطة مهمة جدًا في المكتبة.
إذا كان التطبيق Offline، فإن isDateTimeValid يمكنه استخدام آخر وقت موثوق تم الحصول عليه من NTP، ثم تطوير هذا المرجع اعتمادًا على الوقت المنقضي باستخدام ساعة النظام monotonic المتاحة على المنصة.
هذا يعني أن التطبيق لا يصبح معتمدًا بشكل كامل على وجود اتصال بالإنترنت في كل مرة تريد فيها التحقق من الوقت.
ماذا يحدث عند أول تشغيل للتطبيق بدون Internet؟
إذا لم يكن هناك وقت موثوق محفوظ مسبقًا، مثل حالة:
- تثبيت التطبيق لأول مرة.
- مسح بيانات التطبيق.
- إعادة تشغيل الجهاز بدون توفر NTP.
- عدم توفر مصدر monotonic صالح.
فإن المكتبة تعيد true حتى لا تمنع التطبيق من العمل بشكل غير متوقع. وتوضح الوثائق أنه إذا كان تطبيقك يحتاج إلى سياسة أكثر صرامة، فيجب عليك إضافة منطق fail closed الخاص بك.
ما أهمية هذه النقطة في التطبيقات الأمنية؟
لنفترض أنك تبني تطبيقًا يحتوي على اشتراك أو صلاحية زمنية.
إذا لم يكن لديك وقت موثوق بعد، فإن إجبار التطبيق على اعتبار الجهاز غير صالح قد يمنع المستخدم من الدخول إلى التطبيق حتى في الحالات التي لا يوجد فيها تلاعب.
لذلك تتعامل المكتبة مع هذه الحالة بطريقة تسمح للتطبيق بالاستمرار، بينما يمكنك أنت إضافة السياسة التي تناسب التطبيق.
التخزين المؤقت للوقت الموثوق
تستخدم المكتبة تخزينًا محليًا للحفاظ على مرجع الوقت الموثوق.
وتستخدم ضمن اعتماداتها مكتبة:
flutter_secure_storageلتخزين البيانات المحلية المرتبطة بهذا النظام.
وفي الإصدار 1.3.1 تم تعديل آلية التخزين المؤقت لمعالجة حالات قديمة مرتبطة بقيم الوقت المخزنة، وأصبح النظام يعتمد على الوقت المنقضي باستخدام ساعة monotonic لتحديث المرجع أثناء العمل Offline.
تفعيل Logging
يمكنك تفعيل الـ Logging أثناء تطوير التطبيق من خلال:
FlutterTimeGuard.configureLogging(
enableLogs: true,
);وبعد ذلك يمكن للمكتبة طباعة معلومات تساعدك في فهم عمليات التحقق والتغييرات التي تحدث أثناء الاختبار.
ويمكن تعطيله:
FlutterTimeGuard.configureLogging(
enableLogs: false,
);وتوصي وثائق المكتبة بتفعيل Logging أثناء الدمج والتطوير ثم تعطيله في الإنتاج إلا إذا كنت تحتاج إلى معلومات تشخيصية.
استخدام FlutterTimeGuard.log
يمكنك أيضًا استخدام Logger الموجود داخل المكتبة:
FlutterTimeGuard.log(
'Time changed',
);ويمكن استخدامه لتسجيل الأحداث المتعلقة بعمليات التحقق أو اكتشاف تغيير الوقت.
مثال على تطبيق اشتراكات
لنفترض أن لديك تطبيقًا يقدم تجربة مجانية لمدة 7 أيام.
بدل الاعتماد فقط على:
DateTime.now()يمكنك أولًا التحقق من أن وقت الجهاز صالح:
final isValid = await FlutterTimeGuard.isDateTimeValid(
toleranceInSeconds: 300,
);
if (!isValid) {
print('Invalid device time');
return;
}وبعد ذلك تنفذ منطق الاشتراك أو التجربة.
مثال على تطبيق الحضور والانصراف
تطبيقات الحضور تعتمد بشكل مباشر على الوقت، لذلك يمكن استخدام المكتبة للتحقق من الساعة قبل تنفيذ عملية تسجيل الحضور.
Future<void> checkIn() async {
final isValid = await FlutterTimeGuard.isDateTimeValid(
toleranceInSeconds: 300,
);
if (!isValid) {
print('Please correct your device time.');
return;
}
// Continue check-in process
}يمكن بعد ذلك إرسال عملية تسجيل الحضور إلى Backend مع وقت الخادم أيضًا للحصول على طبقة إضافية من التحقق.
مثال على تطبيق حجوزات
إذا كان لديك تطبيق يسمح للمستخدم بحجز موعد في وقت معين، يمكنك التحقق من وقت الجهاز قبل تنفيذ العملية:
Future<void> createBooking() async {
final isValid = await FlutterTimeGuard.isDateTimeValid(
toleranceInSeconds: 300,
);
if (!isValid) {
print(
'Your device time is not valid.',
);
return;
}
// Create booking
}مثال على حماية OTP أو Token محدود المدة
يمكن أيضًا استخدام المكتبة كإشارة إضافية في التطبيقات التي تتعامل مع OTP أو Tokens محدودة المدة.
final isValid = await FlutterTimeGuard.isDateTimeValid(
toleranceInSeconds: 60,
);
if (!isValid) {
// Do not continue with time-sensitive operation.
return;
}
// Continue OTP / token flowلكن في العمليات الحساسة، يجب ألا يكون وقت الجهاز المحلي هو المصدر الوحيد للحكم على صلاحية العملية، ويمكن استخدام وقت الخادم كمرجع أساسي.
مثال على تطبيق Rewards
إذا كان لديك تطبيق يقدم نقاطًا أو مكافآت مرتبطة بفترات زمنية، فقد يحاول المستخدم تغيير الساعة للوصول إلى مكافأة قبل موعدها.
يمكن استخدام Flutter Time Guard كإشارة على مستوى التطبيق:
final validTime = await FlutterTimeGuard.isDateTimeValid(
toleranceInSeconds: 120,
);
if (!validTime) {
print(
'Device time manipulation detected.',
);
return;
}
await claimReward();مراقبة تغيير المنطقة الزمنية
المكتبة لا تركز فقط على تغيير التاريخ والساعة، وإنما تراقب أيضًا تغييرات المنطقة الزمنية للجهاز.
وهذا مهم في التطبيقات التي تتعامل مع مواعيد أو حجوزات أو تسجيل حضور أو عمليات تعتمد على Time Zone.
مشكلة False Positive
قد يحدث أن يتغير وقت الجهاز دون أن يكون المستخدم قد قام بالتلاعب به.
على سبيل المثال، قد يقوم نظام التشغيل بمزامنة الساعة تلقائيًا مع الشبكة.
لهذا السبب لا يفضل الاعتماد على:
onTimeChangedبشكل منفرد لاتخاذ قرار أمني نهائي.
يمكن إعادة التحقق باستخدام:
FlutterTimeGuard.isDateTimeValid()قبل عرض رسالة للمستخدم أو اتخاذ الإجراء المطلوب. وهذا هو الأسلوب الذي توضحه وثائق المكتبة لتقليل الإنذارات الخاطئة.
مثال كامل مع Flutter
يمكن إنشاء صفحة بسيطة تحتوي على زر للتحقق من الوقت، بالإضافة إلى Listener يراقب تغييرات الوقت.
import 'package:flutter/material.dart';
import 'package:flutter_time_guard/flutter_time_guard.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
FlutterTimeGuard.configureLogging(
enableLogs: false,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Time Guard',
home: const TimeGuardPage(),
);
}
}
class TimeGuardPage extends StatefulWidget {
const TimeGuardPage({super.key});
@override
State<TimeGuardPage> createState() =>
_TimeGuardPageState();
}
class _TimeGuardPageState
extends State<TimeGuardPage> {
bool? isValid;
bool isLoading = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
FlutterTimeGuard.listenToDateTimeChange(
stopListeingAfterFirstChange: true,
onTimeChanged: () async {
FlutterTimeGuard.log(
'System time changed',
);
final valid =
await FlutterTimeGuard.isDateTimeValid(
toleranceInSeconds: 86400,
);
if (!mounted || valid) {
return;
}
showDialog(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
title: const Text(
'Invalid Device Time',
),
content: const Text(
'Please correct your device date and time.',
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'),
),
],
);
},
);
},
);
});
}
Future<void> checkTime() async {
setState(() {
isLoading = true;
});
final result =
await FlutterTimeGuard.isDateTimeValid(
toleranceInSeconds: 300,
);
if (!mounted) {
return;
}
setState(() {
isValid = result;
isLoading = false;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text(
'Flutter Time Guard',
),
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Text(
isValid == null
? 'Check your device time'
: isValid!
? 'Device time is valid'
: 'Device time is invalid',
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 20,
),
),
const SizedBox(height: 24),
isLoading
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: checkTime,
child: const Text(
'Check Time',
),
),
],
),
),
),
);
}
}استخدام المكتبة مع Backend
يمكن استخدام Flutter Time Guard كطبقة Client-Side للتحقق من الساعة، ولكن إذا كانت العملية حساسة مثل الدفع أو الاشتراكات أو الصلاحيات الزمنية أو العمليات المالية، فمن الأفضل أن يكون Backend جزءًا من عملية التحقق.
يمكن أن يكون السيناريو:
Flutter App
↓
Flutter Time Guard
↓
Check Device Time
↓
Backend API
↓
Server Time
↓
Final Decisionوبذلك لا تعتمد على جهاز المستخدم وحده عند اتخاذ القرارات المهمة.
هل Flutter Time Guard تحتاج إلى Backend؟
لا تحتاج المكتبة إلى Backend خاص بك لكي تعمل.
هي تستطيع التحقق من وقت الجهاز باستخدام NTP وتخزين آخر وقت موثوق محليًا.
لكن وجود Backend يمكن أن يكون مهمًا عندما تكون العملية حساسة وتحتاج إلى مصدر مركزي للوقت واتخاذ القرار.
دعم Android وiOS
الحزمة الحالية تدعم:
- Android.
- iOS.
كما تشير وثائق الحزمة إلى دعم Swift Package Manager في iOS مع Flutter 3.41+، مع استمرار دعم CocoaPods للمشاريع التي لم تنتقل إلى Swift Package Manager.
اعتمادات المكتبة
من الاعتمادات الأساسية الموجودة في الإصدار الحالي:
flutterflutter_secure_storagentpplugin_platform_interface
وتوضح صفحة الحزمة هذه الاعتمادات ضمن بيانات المشروع.
ما الجديد في الإصدار 1.3.1؟
يحتوي الإصدار 1.3.1 على تحسينات مرتبطة بالتخزين المؤقت للوقت، خصوصًا لمنع النتائج الخاطئة الناتجة عن بيانات وقت قديمة عندما يبقى التطبيق مفتوحًا أو مغلقًا لفترة طويلة.
كما أصبح النظام يستخدم الوقت المنقضي من ساعة المنصة monotonic لتطوير الوقت الموثوق المخزن أثناء العمل بدون اتصال، وتم تغيير صيغة التخزين القديمة التي كانت تعتمد فقط على وقت الحائط.
وهذا مهم لأن الاعتماد على قيمة وقت مخزنة منذ فترة طويلة قد يؤدي إلى نتائج غير دقيقة عند استخدام التطبيق Offline.
متى يمكن استخدام Flutter Time Guard؟
- تطبيقات الاشتراكات.
- التجارب المجانية.
- تطبيقات الحضور والانصراف.
- أنظمة الحجز.
- تطبيقات الألعاب.
- أنظمة المكافآت والنقاط.
- تطبيقات التوصيل.
- تطبيقات FinTech.
- تطبيقات تحتوي على OTP أو Tokens محدودة المدة.
- التطبيقات التي تحتاج إلى اكتشاف تغيير وقت الجهاز.
وتذكر صفحة الحزمة نفسها هذه السيناريوهات ضمن حالات الاستخدام المقترحة للمكتبة.
الخلاصة
تقدم مكتبة Flutter Time Guard طريقة بسيطة لإضافة طبقة من التحقق من وقت الجهاز داخل تطبيقات Flutter، خصوصًا عندما يكون للوقت تأثير مباشر على سلوك التطبيق.
بدل الاعتماد على:
DateTime.now()في كل الحالات، يمكنك استخدام المكتبة لمقارنة وقت الجهاز بوقت موثوق من NTP، وتحديد مقدار السماح بالاختلاف، بالإضافة إلى مراقبة تغييرات التاريخ والوقت والمنطقة الزمنية.
ومن أهم النقاط أن المكتبة لا تتوقف بالكامل عند انقطاع الإنترنت، حيث يمكنها استخدام آخر وقت موثوق محفوظ وتحديثه اعتمادًا على الوقت المنقضي في الحالات التي تسمح بها البيئة. أما عند عدم وجود مرجع موثوق، فإنها تعيد true افتراضيًا حتى لا تمنع التطبيق من العمل، ويمكن للمطور إضافة سياسة أكثر صرامة حسب متطلبات المشروع.
إذا كنت تعمل على تطبيق يعتمد على الاشتراكات أو الحجوزات أو الحضور أو المكافآت أو أي Flow حساس للوقت، فإن Flutter Time Guard يمكن أن تكون إضافة مفيدة ضمن طبقة التعامل مع الوقت، مع إمكانية دمجها مع Backend عندما تحتاج إلى تحقق مركزي من الوقت.
مكتبة Flutter Time Guard على Pub.dev
التوثيق الرسمي للمكتبة
المزيد من مقالات Flutter على Geecoders
الكود كامل
dependencies:
flutter:
sdk: flutter
flutter_time_guard: ^1.3.1flutter pub getimport 'package:flutter/material.dart';
import 'package:flutter_time_guard/flutter_time_guard.dart';
final navigatorKey = GlobalKey<NavigatorState>();
void main() {
WidgetsFlutterBinding.ensureInitialized();
FlutterTimeGuard.configureLogging(
enableLogs: false,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
navigatorKey: navigatorKey,
title: 'Flutter Time Guard',
home: const TimeGuardPage(),
);
}
}
class TimeGuardPage extends StatefulWidget {
const TimeGuardPage({super.key});
@override
State<TimeGuardPage> createState() =>
_TimeGuardPageState();
}
class _TimeGuardPageState
extends State<TimeGuardPage> {
bool? isValid;
bool isLoading = false;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
FlutterTimeGuard.listenToDateTimeChange(
stopListeingAfterFirstChange: true,
onTimeChanged: () async {
FlutterTimeGuard.log(
'System time changed',
);
final valid =
await FlutterTimeGuard.isDateTimeValid(
toleranceInSeconds: 86400,
);
if (valid) {
return;
}
final context =
navigatorKey.currentContext;
if (context == null) {
return;
}
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (context) {
return AlertDialog(
title: const Text(
'Invalid Device Time',
),
content: const Text(
'Your device clock looks incorrect. '
'Please adjust the date and time '
'to continue.',
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'),
),
],
);
},
);
},
);
});
}
Future<void> checkTimeValidity() async {
setState(() {
isLoading = true;
});
final result =
await FlutterTimeGuard.isDateTimeValid(
toleranceInSeconds: 300,
);
if (!mounted) {
return;
}
setState(() {
isValid = result;
isLoading = false;
});
}
@override
Widget build(BuildContext context) {
String message;
if (isValid == null) {
message = 'Press the button to check time validity';
} else if (isValid!) {
message = 'Device time is valid';
} else {
message = 'Device time is invalid';
}
return Scaffold(
appBar: AppBar(
title: const Text(
'Flutter Time Guard',
),
centerTitle: true,
),
body: Center(
child: Padding(
padding: const EdgeInsets.all(24),
child: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 24),
if (isLoading)
const CircularProgressIndicator()
else
ElevatedButton(
onPressed: checkTimeValidity,
child: const Text(
'Check Time',
),
),
],
),
),
),
);
}
}مزيد من المقالات : راقب كل ضغطة داخل تطبيقك! طريقة احترافية لدمج Flutter Clarity






