From 38d37fd4070b7df28b0e36075e67a8e02126e41e Mon Sep 17 00:00:00 2001 From: Sikander Saleem Date: Thu, 4 Sep 2025 14:10:28 +0300 Subject: [PATCH] custom tab bar added. --- lib/extensions/string_extensions.dart | 6 +- lib/presentation/home/landing_page.dart | 3 + lib/widgets/custom_tab_bar.dart | 121 ++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 lib/widgets/custom_tab_bar.dart diff --git a/lib/extensions/string_extensions.dart b/lib/extensions/string_extensions.dart index b9d4ba8..d118d08 100644 --- a/lib/extensions/string_extensions.dart +++ b/lib/extensions/string_extensions.dart @@ -164,6 +164,8 @@ extension EmailValidator on String { bool isCenter = false, int? maxlines, TextAlign? textAlign, + FontWeight? weight, + double? letterSpacing = -0.4, }) => Text( this, @@ -172,8 +174,8 @@ extension EmailValidator on String { style: TextStyle( color: color ?? AppColors.blackColor, fontSize: 16.fSize, - letterSpacing: -0.4, - fontWeight: isBold ? FontWeight.bold : FontWeight.normal, + letterSpacing: letterSpacing, + fontWeight: weight ?? (isBold ? FontWeight.bold : FontWeight.normal), decoration: isUnderLine ? TextDecoration.underline : null, ), ); diff --git a/lib/presentation/home/landing_page.dart b/lib/presentation/home/landing_page.dart index 7435df9..da3249a 100644 --- a/lib/presentation/home/landing_page.dart +++ b/lib/presentation/home/landing_page.dart @@ -19,6 +19,7 @@ import 'package:hmg_patient_app_new/presentation/medical_file/medical_file_page. import 'package:hmg_patient_app_new/theme/colors.dart'; import 'package:hmg_patient_app_new/widgets/bottom_navigation/bottom_navigation.dart'; import 'package:hmg_patient_app_new/widgets/buttons/custom_button.dart'; +import 'package:hmg_patient_app_new/widgets/custom_tab_bar.dart' show CustomTabBar; import 'package:hmg_patient_app_new/widgets/transitions/fade_page.dart'; import 'package:provider/provider.dart'; @@ -116,6 +117,8 @@ class _LandingPageState extends State { ), ), SizedBox(height: 12.h), + CustomTabBar(), + SizedBox(height: 12.h), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/lib/widgets/custom_tab_bar.dart b/lib/widgets/custom_tab_bar.dart new file mode 100644 index 0000000..ae0e6f6 --- /dev/null +++ b/lib/widgets/custom_tab_bar.dart @@ -0,0 +1,121 @@ +import 'package:flutter/material.dart'; +import 'package:hmg_patient_app_new/core/app_assets.dart'; +import 'package:hmg_patient_app_new/core/app_export.dart'; +import 'package:hmg_patient_app_new/core/utils/utils.dart'; +import 'package:hmg_patient_app_new/extensions/int_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/string_extensions.dart'; +import 'package:hmg_patient_app_new/extensions/widget_extensions.dart'; + +class CustomTabBarModel { + String? image; + String title; + + CustomTabBarModel(this.image, this.title); +} + +class CustomTabBar extends StatefulWidget { + final int initialIndex = 0; + final List tabs; + final Color activeTextColor; + final Color activeBackgroundColor; + final Color? inActiveTextColor; + final Color inActiveBackgroundColor; + final Function(int)? onTabChange; + + CustomTabBar( + {Key? key, + this.tabs = const [], + this.activeTextColor = const Color(0xff2E3039), + this.inActiveTextColor = const Color(0xff898A8D), + this.activeBackgroundColor = const Color(0x142E3039), + this.inActiveBackgroundColor = Colors.white, + this.onTabChange}) + : super(key: key); + + @override + _CustomTabBarState createState() { + return _CustomTabBarState(); + } +} + +class _CustomTabBarState extends State { + List tabs = []; + int selectedIndex = 0; + + @override + void initState() { + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + void callBackCurrentIndex() { + if (widget.onTabChange != null) widget.onTabChange!(selectedIndex); + } + + @override + Widget build(BuildContext context) { + tabs = [ + CustomTabBarModel(null, "By Visit"), + CustomTabBarModel(null, "By Test"), + CustomTabBarModel(null, "By Value"), + CustomTabBarModel(AppAssets.myFilesBottom, "By Value"), + CustomTabBarModel(null, "By Value"), + ]; + + late Widget parentWidget; + + if (tabs.length > 3) { + parentWidget = ListView.separated( + scrollDirection: Axis.horizontal, + padding: EdgeInsets.zero, + physics: const BouncingScrollPhysics(), + itemBuilder: (cxt, index) => myTab(tabs[index], index), + separatorBuilder: (cxt, index) => 4.width, + itemCount: tabs.length, + ); + } else { + parentWidget = Row( + spacing: 4, + children: [for (int i = 0; i < tabs.length; i++) myTab(tabs[i], i).expanded], + ); + } + + return Container( + height: 62.h, + padding: EdgeInsets.all(4), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(11), + ), + child: parentWidget); + } + + Widget myTab(CustomTabBarModel tabBar, int currentIndex) { + bool isSelected = selectedIndex == currentIndex; + return Container( + height: 54.h, + padding: EdgeInsets.only(top: 4, bottom: 4, left: 16, right: 16), + alignment: Alignment.center, + decoration: BoxDecoration( + color: isSelected ? widget.activeBackgroundColor : widget.inActiveBackgroundColor, + borderRadius: BorderRadius.circular(7), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + spacing: 4, + children: [ + if (tabBar.image != null) Utils.buildSvgWithAssets(icon: tabBar.image!, height: 18, width: 18, iconColor: isSelected ? Color(0xff2E3039) : Color(0xff898A8D)), + tabBar.title.toText16(weight: isSelected ? FontWeight.w600 : FontWeight.w500, color: isSelected ? Color(0xff2E3039) : Color(0xff898A8D), letterSpacing: isSelected ? -0.3 : -0.1), + ], + )).onPress(() { + setState(() { + selectedIndex = currentIndex; + }); + callBackCurrentIndex(); + }); + } +}