Flutter هو إطار عمل متعدد النظم لتطوير تطبيقات الهواتف المحمولة، والذي يوفر مجموعة غنية من المكونات المدمجة التي تسهل عملية إنشاء التطبيقات. في هذه المقالة، سنتعلم كيفية عمل شريط تنقل مخصص (Custom Navigation Bar) في Flutter.
الخطوة 1: إنشاء كلاس BottomNavBarItem
سنبدأ بإنشاء كلاس BottomNavBarItem
والذي سيمثل كل عنصر سيظهر في شريط التنقل. سيحتوي هذا الكلاس على خصائص العنوان والأيقونة الخاصة بكل عنصر.
class BottomNavBarItem { final String title; final IconData icon; BottomNavBarItem({ required this.title, required this.icon, }); }
“اقرأ أيضاً: شرح استخدام Image Picker في Flutter“
الخطوة 2: إنشاء كلاس BottomNavBar
سنقوم بإنشاء كلاس BottomNavBar
الذي يمتد من StatefulWidget
. ستكون مهمة هذا الكلاس هي تزيين شريط التنقل بأكمله ومعالجة العناصر الفرعية والفهرس الحالي ولون الخلفية ودالة onTap
التي سيتم تنفيذها عند الضغط على أي عنصر من عناصر Custom Navigation Bar.
class BottomNavBar extends StatefulWidget { final List<BottomNavBarItem> children; int currentIndex; final Color? backgroundColor; Function(int)? onTap; BottomNavBar({ Key? key, required this.children, required this.currentIndex, this.backgroundColor, required this.onTap, }); @override State<BottomNavBar> createState() => _BottomNavBarState(); }
“اقرأ أيضاً: شرح استخدام ويدجت AnimatedBuilder في فلاتر“
الخطوة 3: تصميم شكل Custom Navigation Bar
سنستخدم الكلاس _BottomNavBarState
لتحديد الشكل والرسوم المتحركة لشريط التنقل Custom Navigation Bar. سيحتوي هذا الكلاس على عنصر Container
يعمل كحاوية لشريط التنقل المخصص، ويتميز بحواف مستديرة ولون خلفية قابل للتخصيص.
class _BottomNavBarState extends State<BottomNavBar> { @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: widget.backgroundColor ?? Theme.of(context).colorScheme.primary, ), width: double.infinity, margin: const EdgeInsets.only(bottom: 10, left: 5, right: 5), height: 56, child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate( widget.children.length, (index) => NavBarItem( index: index, item: widget.children[index], selected: widget.curentIndex == index, onTap: () { setState(() { widget.curentIndex = index; widget.onTap!(widget.curentIndex); }); }, ), ), ), ); } }
“اقرأ أيضاً: شرح استخدام ويدجت TooltipTheme في فلاتر“
الخطوة 4: انشاء كلاس NavBarItem
الخاص بكل عنصر
كل عنصر تنقل سيتم تجسيده في كلاس NavBarItem
. سيتحكم هذا الكلاس أيضًا في الشكل والرسوم المتحركة والتفاعل لكل عنصر.
class NavBarItem extends StatefulWidget { final BottomNavBarItem item; final int index; bool selected; final Function onTap; final Color? backgroundColor; NavBarItem({ Key? key, required this.item, this.selected = false, required this.onTap, this.backgroundColor, required this.index, }); @override State<NavBarItem> createState() => _NavBarItemState(); }
“اقرأ أيضاً: شرح استخدام SingleChildScrollView في فلاتر“
الخطوة 5: دمج كل العناصر معًا
سنقوم بتنسيق الشكل لكل عنصر تنقل في الكلاس _NavBarItemState
. سنستخدم GestureDetector
و AnimatedContainer
لضمان انتقال سلس وجذاب بصريًا.
class _NavBarItemState extends State<NavBarItem> { @override Widget build(BuildContext context) { return GestureDetector( onTap: () { widget.onTap(); }, child: AnimatedContainer( margin: const EdgeInsets.all(8), duration: const Duration(milliseconds: 300), constraints: BoxConstraints(minWidth: widget.selected ? 100 : 56), height: 56, decoration: BoxDecoration( color: widget.selected ? widget.backgroundColor ?? Colors.white : Colors.transparent, borderRadius: BorderRadius.circular(16), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( widget.item.icon, color: widget.selected ? Theme.of(context).colorScheme.inversePrimary : Colors.black, ), Offstage( offstage: !widget.selected, child: Text(widget.item.title)), ], ), ), ); } }
“اقرأ أيضاً: شرح استخدام ويدجت TextButton في فلاتر“
مثال كامل على Custom Navigation Bar
هنا الكود كامل لعرض الـ Custom Navigation Bar يمكنك نسخه وتجربته بنفسك:
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override State<MyApp> createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { @override int index = 0; PageController controller = PageController( initialPage: 0, keepPage: true, ); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, home: Scaffold( extendBody: true, appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: const Text("Custom Bottom Navigation Bar"), ), body: PageView( controller: controller, onPageChanged: (value) => setState(() => index = value), children: <Widget>[ Container( color: Color(0xFFdddeee), child:Center(child:Text("Page 1")) ), Container( color: Color(0xFFdddeee), child:Center(child:Text("Page 2")) ), Container( color: Color(0xFFdddeee), child:Center(child:Text("Page 3")) ), Container( color: Color(0xFFdddeee), child:Center(child:Text("Page 4")) ), ], ), bottomNavigationBar: BottomNavBar( curentIndex: index, backgroundColor: Theme.of(context).colorScheme.inversePrimary, onTap: (value) => setState(() { index = value; controller.animateToPage( index, duration: const Duration(milliseconds: 300), curve: Curves.ease, ); }), children: [ BottomNavBarItem( title: "Home", icon: Icons.home_outlined, ), BottomNavBarItem( title: "Search", icon: Icons.search_rounded, ), BottomNavBarItem( title: 'Chat', icon: Icons.chat_bubble_outline, ), BottomNavBarItem( title: "Profile", icon: Icons.person_outline, ), ], ), ) ); } } class BottomNavBarItem { final String title; final IconData icon; BottomNavBarItem({ required this.title, required this.icon, }); } class BottomNavBar extends StatefulWidget { final List<BottomNavBarItem> children; int curentIndex; final Color? backgroundColor; Function(int)? onTap; BottomNavBar( {super.key, required this.children, required this.curentIndex, this.backgroundColor, required this.onTap}); @override State<BottomNavBar> createState() => _BottomNavBarState(); } class _BottomNavBarState extends State<BottomNavBar> { @override Widget build(BuildContext context) { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: widget.backgroundColor ?? Theme.of(context).colorScheme.primary, ), width: double.infinity, margin: const EdgeInsets.only(bottom: 10, left: 5, right: 5), height: 56, child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: List.generate( widget.children.length, (index) => NavBarItem( index: index, item: widget.children[index], selected: widget.curentIndex == index, onTap: () { setState(() { widget.curentIndex = index; widget.onTap!(widget.curentIndex); }); }, ), ), ), ); } } class NavBarItem extends StatefulWidget { final BottomNavBarItem item; final int index; bool selected; final Function onTap; final Color? backgroundColor; NavBarItem({ super.key, required this.item, this.selected = false, required this.onTap, this.backgroundColor, required this.index, }); @override State<NavBarItem> createState() => _NavBarItemState(); } class _NavBarItemState extends State<NavBarItem> { @override Widget build(BuildContext context) { return GestureDetector( onTap: () { widget.onTap(); }, child: AnimatedContainer( margin: const EdgeInsets.all(8), duration: const Duration(milliseconds: 300), constraints: BoxConstraints(minWidth: widget.selected ? 100 : 56), height: 56, decoration: BoxDecoration( color: widget.selected ? widget.backgroundColor ?? Colors.white : Colors.transparent, borderRadius: BorderRadius.circular(16), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon( widget.item.icon, color: widget.selected ? Theme.of(context).colorScheme.inversePrimary : Colors.black, ), Offstage( offstage: !widget.selected, child: Text(widget.item.title)), ], ), ), ); } }
“اقرأ أيضاً: شرح استخدام FractionallySizedBox في فلاتر“
في هذا المقال، تعلمنا كيفية إنشاء شريط تنقل مخصص (Custom Navigation Bar) في Flutter. بدأنا بإنشاء كلاس BottomNavBarItem
الذي يحدد كل عنصر في شريط التنقل. ثم قمنا ببناء كلاس BottomNavBar
الذي يمتد من StatefulWidget
ويتحكم في تصميم شريط التنقل بأكمله ومعالجة العناصر الفرعية والفهرس الحالي ولون الخلفية ودالة onTap
. بعد ذلك، قمنا بتصميم شكل شريط التنقل باستخدام كلاس _BottomNavBarState
، واستخدمنا كلاس NavBarItem
لتحسين عناصر التنقل الفردية بتحديد الشكل والرسوم المتحركة والتفاعل. أخيرًا، جمعنا كل العناصر معًا لإنشاء شريط التنقل المخصص الكامل Custom Navigation Bar.
يمكنك الآن استخدام شريط التنقل المخصص Custom Navigation Bar في تطبيقات Flutter الخاصة بك وتكييفه وفقًا لتصميم ووظائف التطبيق الفريدة من نوعها.