-
-
Save RockinPaul/65d58284bb7c7c0f0d045b5fb6d27bcf to your computer and use it in GitHub Desktop.
TabBar with badge that update color on swap - Demo included (Flutter)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2021, Thea Choem, All rights reserved. | |
import 'package:badges/badges.dart'; | |
import 'package:flutter/material.dart'; | |
class CustomTabBarItem { | |
final String label; | |
final String? value; | |
CustomTabBarItem({ | |
required this.label, | |
this.value, | |
}); | |
} | |
class CustomTabBar extends StatelessWidget implements PreferredSizeWidget { | |
const CustomTabBar({ | |
Key? key, | |
this.controller, | |
required this.items, | |
this.isScrollable = true, | |
this.height = kToolbarHeight, | |
this.onTap, | |
this.padding, | |
}) : super(key: key); | |
/// if `TabController` isn't provided, make sure you have | |
/// wraped your widget with `DefaultTabController` | |
final TabController? controller; | |
final List<CustomTabBarItem> items; | |
final double height; | |
final bool isScrollable; | |
final ValueChanged<int>? onTap; | |
final EdgeInsetsGeometry? padding; | |
@override | |
Widget build(BuildContext context) { | |
final TabController? tabController = controller ?? DefaultTabController.of(context); | |
return Theme( | |
data: Theme.of(context).copyWith( | |
splashColor: Colors.transparent, | |
highlightColor: Colors.transparent, | |
), | |
child: Container( | |
width: double.infinity, | |
padding: padding, | |
child: TabBar( | |
isScrollable: isScrollable, | |
controller: tabController, | |
onTap: onTap, | |
tabs: List.generate( | |
items.length, | |
(itemIndex) { | |
var item = items[itemIndex]; | |
return GestureDetector( | |
onTap: () { | |
tabController?.animateTo(itemIndex); | |
if (onTap != null) onTap!(itemIndex); | |
}, | |
child: Row( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: [ | |
Text(item.label), | |
if (item.value != null && item.value!.isNotEmpty) | |
_buildValueContainer( | |
controller: tabController!, | |
index: itemIndex, | |
item: item, | |
), | |
], | |
), | |
); | |
}, | |
), | |
), | |
), | |
); | |
} | |
// this method is used to build the | |
Widget _buildValueContainer({ | |
required TabController controller, | |
int? index, | |
CustomTabBarItem? item, | |
}) { | |
return AnimatedBuilder( | |
animation: controller.animation!, | |
builder: (context, child) { | |
var unselectedColor = Theme.of(context).unselectedWidgetColor; | |
var selectedColor = Theme.of(context).colorScheme.primary; | |
Color? color = unselectedColor; | |
final double offset = controller.offset; | |
final bool isCurrentChild = index == controller.index; | |
final int currentIndex = controller.index; | |
bool dragToRight = offset > 0; | |
if (dragToRight) { | |
bool inScope = index! <= currentIndex + 1 && index >= currentIndex; | |
if (inScope) { | |
color = isCurrentChild | |
? Color.lerp(selectedColor, unselectedColor, offset)! | |
: Color.lerp(unselectedColor, selectedColor, offset)!; | |
} | |
} else { | |
bool inScope = index! <= currentIndex && index >= currentIndex - 1; | |
if (inScope) { | |
color = isCurrentChild | |
? Color.lerp(selectedColor, unselectedColor, -offset)! | |
: Color.lerp(unselectedColor, selectedColor, -offset)!; | |
} | |
} | |
var themeData = Theme.of(context); | |
var textStyle = themeData.textTheme.overline?.copyWith(color: themeData.backgroundColor); | |
var containerSize = 16.0; | |
return Container( | |
height: containerSize, | |
margin: const EdgeInsets.only(left: 4.0), | |
child: Badge( | |
alignment: Alignment.topRight, | |
animationType: BadgeAnimationType.scale, | |
shape: BadgeShape.square, | |
showBadge: !(item?.value == "0" || item?.value == null), | |
animationDuration: Duration(milliseconds: 200), | |
badgeColor: Colors.transparent, | |
elevation: 0.0, | |
padding: EdgeInsets.zero, | |
badgeContent: Container( | |
height: containerSize, | |
alignment: Alignment.center, | |
padding: const EdgeInsets.symmetric(horizontal: 4), | |
decoration: BoxDecoration(borderRadius: BorderRadius.circular(containerSize), color: color), | |
child: Text( | |
item?.value ?? "", | |
style: textStyle, | |
maxLines: 1, | |
overflow: TextOverflow.ellipsis, | |
), | |
), | |
), | |
); | |
}, | |
); | |
} | |
@override | |
Size get preferredSize => Size.fromHeight(height); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment