تصميم لصفحة الوضع الليلي والنهاري مع انميشن في فلاتر
في هذا المقال سوف نشارك معكم design جديد ومختلف لصفحة الdark mode حيث يعد الوضع الليلي في التطبيق شيئ اساسي حاليا ونلاحظ ان التطبيقات التي لا تتضمن وضع ليلي تكون اقل استخداما من التطبيقات التي تحتوي عليه ولكن في هذه المقالة سوف نقدم لكم تصميم جديد ومختلف لهذه الصفحة يمكنك استخدام التصميم كما تريد والتعديل عليه والتصميم عباره عن صفحة بيها خاصية للتغيير بين الوضع الداكن والفاتح بإستخدام switch كما هو موضح بالصورة .
دون إجراء أي مقارنات مع الأنظمة الأساسية الأخرى ، إليك بعض الميزات والسمات التي يمكن أن تغريك بتجربة Flutter ، اولا الإنتاجية عالية. يمكنك استخدام نفس قاعدة الشفرة لتطبيقات iOS و Android لأن Flutter متعدد الأنظمة الأساسية. سيوفر لك هذا بلا شك الوقت والمال.
الثانية اداء ممتاز. يجمع Dart إلى التعليمات البرمجية الأصلية ، ويوفر Flutter عناصر واجهة المستخدم الخاصة به ، لذلك ليست هناك حاجة لاستخدام أدوات OEM. هذا يعني أن الاتصال بين التطبيق والنظام الأساسي سيكون أقل توسطًا. "Flutter هو أول SDK للجوال يتيح طرق عرض تفاعلية دون الحاجة إلى جسر JavaScript ،" كما تصفه الشركة. كل هذا يضيف إلى أوقات بدء التطبيق الأسرع ومخاوف أقل بشأن الأداء.
المكتبات التي تحتاجها لتصميم الصفحة
path_provider: ^2.0.9
// dataBase local
hive: ^2.1.0
// state management
provider: ^6.0.2
إنشاء صفحة theme تحتوي على جميع ملحقات تصميم الصفحة
تحتوي هذه الصفحه على جميع الاشكال والthemes التي يحتاجها تطبيقك والتصميم الذي تعمل عليه قم فقط بنسخ جميع الاكواد منها ووضعها لديك في البرنامج لو تلاحظ فهي عباره فقط عن تصميم للوضع الليلي والنهاري ونقوم بحفظ قيمة التغيير .
themeProvider.dart
class ThemeProvider with ChangeNotifier {
ThemeProvider({this.isLightTheme});
bool? isLightTheme;
// mange status bar color when the theme change.
getCurrentStatusNavigationBarColor() {
if (isLightTheme!) {
SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarBrightness: Brightness.light,
statusBarIconBrightness: Brightness.dark,
systemNavigationBarColor: Color(0xFFFFFFFF),
systemNavigationBarIconBrightness: Brightness.dark,
));
} else {
SystemChrome.setSystemUIOverlayStyle( const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarBrightness: Brightness.dark,
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor: const Color(0xFF26242e),
systemNavigationBarIconBrightness: Brightness.light,
));
}
}
// use to toggle the theme
toggleThemeData() async {
final setting = await Hive.openBox('settings');
setting.put('isLightTheme', !isLightTheme!);
isLightTheme = !isLightTheme!;
getCurrentStatusNavigationBarColor();
notifyListeners();
}
// Global theme data we are always if the light theme is enable #isLightTheme.
ThemeData themeData() {
return ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
primarySwatch: isLightTheme! ? Colors.grey : Colors.grey,
primaryColor: isLightTheme! ? Colors.white : const Color(0xff1e1f28),
brightness: isLightTheme! ? Brightness.light : Brightness.dark,
backgroundColor:
isLightTheme! ? const Color(0xFFFFFFFF) : const Color(0xff26242e),
scaffoldBackgroundColor:
isLightTheme! ? const Color(0xFFFFFFFF) : const Color(0xff26242e),
);
}
ThemeColor themeMode() {
return ThemeColor(
gradient: [
if (isLightTheme!) ...[const Color(0xDD0090FF),const Color(0xDD00CCFF)],
if (!isLightTheme!) ...[const Color(0xFFCEF783) , const Color(0xFFF7B583)]
],
textColor: isLightTheme! ? const Color(0xff000000) : const Color(0xffffffff),
toggleBackgroundColor: isLightTheme! ? const Color(0xffe7e7e8) : const Color(0xff222029),
toggleButtonColor: isLightTheme! ? const Color(0xffffffff) : const Color(0xff34323d),
shadow: [
if (isLightTheme!)
const BoxShadow(
color: Color(0xFFd8d7da),
spreadRadius: 5,
blurRadius: 10,
offset: Offset(0, 5)),
if (!isLightTheme!)
const BoxShadow(
color: Color(0x66000000),
spreadRadius: 5,
blurRadius: 10,
offset: Offset(0, 5))
],
);
}
}
class ThemeColor {
List<Color>? gradient;
List<BoxShadow>? shadow;
Color? backgroundColor;
Color? toggleButtonColor;
Color? toggleBackgroundColor;
Color? textColor;
ThemeColor({
this.gradient,
this.backgroundColor,
this.toggleButtonColor,
this.toggleBackgroundColor,
this.textColor,
this.shadow,
});
}
تصميم صفحة تحتوي على الAnimation الموجوده داخل الصفحة
هذه الصفحة تحتوي على الanimation للتغير بين الصفحات بكل ارياحيه دون مشاكل مع وجود انميشن خلال عملية الانتقال بشكل بسيط ويمكنك التعديل على الانميشن ليتناسب مع الشكل الذي تريده في تطبيقك .
ZAnimatedToggle.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ZAnimatedToggle extends StatefulWidget {
final List<String> values;
final ValueChanged onToggleCallback;
ZAnimatedToggle({
Key? key,
required this.values,
required this.onToggleCallback,
}) : super(key: key);
@override
_ZAnimatedToggleState createState() => _ZAnimatedToggleState();
}
class _ZAnimatedToggleState extends State<ZAnimatedToggle> {
@override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
final themeProvider = Provider.of<ThemeProvider>(context);
return SizedBox(
width: width * .7,
height: width * .13,
child: Stack(
children: [
GestureDetector(
onTap: () {
widget.onToggleCallback(1);
},
child: Container(
width: width * .7,
height: width * .13,
decoration: ShapeDecoration(
color: themeProvider.themeMode().toggleBackgroundColor,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(width * .1))),
child: Row(
children: List.generate(
widget.values.length,
(index) => Padding(
padding: EdgeInsets.symmetric(horizontal: width * .1),
child: Text(
widget.values[index],
style: TextStyle(
fontSize: width * .05,
fontWeight: FontWeight.bold,
color: const Color(0xFF918f95)),
),
),
),
),
),
),
AnimatedAlign(
alignment: themeProvider.isLightTheme!
? Alignment.centerLeft
: Alignment.centerRight,
duration: const Duration(milliseconds: 350),
curve: Curves.ease,
child: Container(
alignment: Alignment.center,
width: width * .35,
height: width * .13,
decoration: ShapeDecoration(
color: themeProvider.themeMode().toggleButtonColor,
shadows: themeProvider.themeMode().shadow,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(width * .1))),
child: Text(
themeProvider.isLightTheme!
? widget.values[0]
: widget.values[1],
style: TextStyle(
fontSize: width * .045, fontWeight: FontWeight.bold),
),
),
)
],
),
);
}
}
تصميم صفحة للDarkMode و LightModel
هذه الصفحة تعد التصميم الذي سوف نقوم بعمله لو تلاحظ سوف تجد ان التصميم بسيط جدا وعند تغيير وضع الى الاخر يتم استدعاء التصميم من الtheme وتغيره مثل لون الصفحة وايضا تغيير لشكل العنصر الذي يظهر بالاعلى بالنسبة للانميشن الذي قمنا به في الاعلى فهو عباره عن الانتقال او التبديل بين الوضع الداكن والفاتح داخل الswitch كما هو موضح بصورة الغلاف .
GeeCoders.dart
class GeeCoders extends StatefulWidget {
const GeeCoders({Key? key}) : super(key: key);
@override
State<GeeCoders> createState() => _GeeCodersState();
}
class _GeeCodersState extends State<GeeCoders>
with SingleTickerProviderStateMixin {
AnimationController? _animationController;
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
@override
void initState() {
_animationController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 800));
super.initState();
}
changeTheme(bool? theme) {
if (!theme!) {
_animationController!.forward(from: 0.0);
} else {
_animationController!.reverse(from: 1.0);
}
}
@override
Widget build(BuildContext context) {
double height = MediaQuery
.of(context)
.size
.height;
double width = MediaQuery
.of(context)
.size
.width;
final themeProvider = Provider.of<ThemeProvider>(context);
return Scaffold(
body: SafeArea(
key: _scaffoldKey,
child: Container(
margin: EdgeInsetsDirectional.only(top: height * 0.1),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Center(
child: Stack(
alignment: Alignment.topCenter,
children: [
Container(
width: width * .25,
height: width * .25,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: themeProvider
.themeMode()
.gradient!,
begin: Alignment.bottomLeft,
end: Alignment.topRight,
)),
),
Transform.translate(
offset: const Offset(40, 0),
child: ScaleTransition(
scale: _animationController!.drive(
Tween<double>(begin: 0.0, end: 1.0).chain(
CurveTween(curve: themeProvider.isLightTheme! ? Curves.easeInBack : Curves.decelerate ),
),
),
alignment: Alignment.bottomRight,
child: Container(
width: width * .26,
height: width * .26,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: themeProvider.isLightTheme!
? Colors.white
: const Color(0xFF26242e)),
),
),
)
],
),
),
SizedBox(
height: height * 0.1,
),
Text(
'Chose a style',
style: TextStyle(
fontSize: width * 0.06, fontWeight: FontWeight.bold),
),
SizedBox(
height: height * 0.03,
),
SizedBox(
width: width * .6,
child: const Text(
'Pop or subtle. Day or night . Customize your interface',
textAlign: TextAlign.center,
),
),
SizedBox(
height: height * 0.1,
),
ZAnimatedToggle(
values: const ['Light', 'Dark'],
onToggleCallback: (v) async {
await themeProvider.toggleThemeData();
setState(() {
changeTheme(themeProvider.isLightTheme);
});
},
),
],
),
),
),);
}
}
تصميم صفحة الmain لبدء تشغيل المشروع
نستخدم في هذه الصفحة الWidgetsFlutterBinding وهي لتشغيل العناصر التي يحتاجها تصميمك والتاكد ان كل شيئ يعمل بشكل صحيح قبل بدء التطبيق لكي لا يحدث مشاكل وبعدها قمنا باستخدام hive للحصول على البيانات وهيا عباره عن Local database لتخزين بيانات صغيره مثل النصوص , وبناء على القيمة يتم بدء تشغيل التطبيق عليها سواء كان يعمل على الوضع الداكن ام الفاتح .
main.dart
import 'package:path_provider/path_provider.dart' as pathProvider;
void main()async {
WidgetsFlutterBinding.ensureInitialized();
final appDocumentDirectory = await pathProvider.getApplicationDocumentsDirectory();
Hive.init(appDocumentDirectory.path);
final settings = await Hive.openBox('settings');
bool isLightTheme = settings.get('isLightTheme') ?? false;
runApp(ChangeNotifierProvider(
create: (_)=>
ThemeProvider(
isLightTheme: isLightTheme
),
child: AppStart()
,));
}
class AppStart extends StatelessWidget {
const AppStart({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
ThemeProvider themeProvider = Provider.of<ThemeProvider>(context);
return MyApp(
themeProvider: themeProvider,
);
}
}
class MyApp extends StatefulWidget {
final ThemeProvider themeProvider;
MyApp({Key? key , required this.themeProvider}) : super(key: key);
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: widget.themeProvider.themeData(),
home: GeeCoders(),
);
}
}
فيديو الشرح
لمزيد من الشروحات :
- كيفية اضافة الwebView داخل تطبيقات الFlutter بدون مشاكل وبسهوله .
- اضافة toast لتطبيقك وكيفية التعامل معه داخل ملف components
- اضافة عداد للعناصر badges الموجوده في السلة Flutter
- كيفية استخدام animations عند تغيير الصور في flutter بسهوله
- شرح مكتبة elastic drawer لعمل تداخل بين الصور اثناء التنقل في flutter