Screenshot :
Program :
main.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/cupertino.dart';
import 'bubble_tab_indicator.dart';
void main() {
runApp(new MaterialApp(
debugShowCheckedModeBanner: false,
home: new TabDemo(),
));
}
class TabDemo extends StatefulWidget {
@override
_TabDemoState createState() => new _TabDemoState();
}
class _TabDemoState extends State with SingleTickerProviderStateMixin {
TabController tabController;
TextStyle tabStyle = TextStyle(fontSize: 16);
@override
void initState() {
super.initState();
tabController = TabController(length: 3, vsync: this, initialIndex: 0);
}
@override
Widget build(BuildContext context) {
return tabCreate();
}
tabCreate() => DefaultTabController(
length: 3,
child: new Scaffold(
appBar: new AppBar(
title: new Text("Flutter Tab"),
bottom: TabBar(
/* indicator: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(50.0)
),*/
indicator: new BubbleTabIndicator(
indicatorHeight: 40.0,
indicatorColor: Colors.orange.shade50,
tabBarIndicatorSize: TabBarIndicatorSize.tab,
),
//indicatorColor: Colors.black54,
labelColor: Colors.orangeAccent,
unselectedLabelColor: Colors.white,
controller: tabController,
isScrollable: false,
tabs: [
//TODO TAB NAME PICKUP, DISPATCH, POSTPONE
tabName('Flutter'),
tabName('Android'),
tabName('Kotlin'),
],
),
),
body: TabBarView(controller: tabController, children: [
//TODO TAB SLIDING
new Container(child: Text('Flutter')),
new Container(child: Text('Android')),
new Container(child: Text('Kotlin')),
])));
tabCreate() => DefaultTabController(
length: 3,
child: new Scaffold(
appBar: new AppBar(
title: new Text("Flutter Tab"),
bottom: TabBar(
/* indicator: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(50.0)
),*/
indicator: new BubbleTabIndicator(
indicatorHeight: 40.0,
indicatorColor: Colors.orange.shade50,
tabBarIndicatorSize: TabBarIndicatorSize.tab,
),
//indicatorColor: Colors.black54,
labelColor: Colors.orangeAccent,
unselectedLabelColor: Colors.white,
controller: tabController,
isScrollable: false,
tabs: [
//TODO TAB NAME PICKUP, DISPATCH, POSTPONE
tabName('Flutter'),
tabName('Android'),
tabName('Kotlin'),
],
),
),
body: TabBarView(controller: tabController, children: [
//TODO TAB SLIDING
new Container(child: Text('Flutter')),
new Container(child: Text('Android')),
new Container(child: Text('Kotlin')),
])));
@override
void dispose() {
tabController.dispose();
super.dispose();
}
tabName(String name) => Tab(
child: Text(
name,
style: tabStyle,
),
);
}
bubble_tab_indicator.dart
library flutter_bubble_tab_indicator;
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
class BubbleTabIndicator extends Decoration {
final double indicatorHeight;
final Color indicatorColor;
final double indicatorRadius;
final EdgeInsetsGeometry padding;
final EdgeInsetsGeometry insets;
final TabBarIndicatorSize tabBarIndicatorSize;
const BubbleTabIndicator({
this.indicatorHeight: 20.0,
this.indicatorColor: Colors.greenAccent,
this.indicatorRadius: 100.0,
this.tabBarIndicatorSize = TabBarIndicatorSize.label,
this.padding: const EdgeInsets.symmetric(vertical: 2.0, horizontal: 8.0),
this.insets: const EdgeInsets.symmetric(horizontal: 5.0),
}) : assert(indicatorHeight != null),
assert(indicatorColor != null),
assert(indicatorRadius != null),
assert(padding != null),
assert(insets != null);
@override
Decoration lerpFrom(Decoration a, double t) {
if (a is BubbleTabIndicator) {
return new BubbleTabIndicator(
padding: EdgeInsetsGeometry.lerp(a.padding, padding, t),
insets: EdgeInsetsGeometry.lerp(a.insets, insets, t),
);
}
return super.lerpFrom(a, t);
}
@override
Decoration lerpTo(Decoration b, double t) {
if (b is BubbleTabIndicator) {
return new BubbleTabIndicator(
padding: EdgeInsetsGeometry.lerp(padding, b.padding, t),
insets: EdgeInsetsGeometry.lerp(insets, b.insets, t),
);
}
return super.lerpTo(b, t);
}
@override
Decoration lerpTo(Decoration b, double t) {
if (b is BubbleTabIndicator) {
return new BubbleTabIndicator(
padding: EdgeInsetsGeometry.lerp(padding, b.padding, t),
insets: EdgeInsetsGeometry.lerp(insets, b.insets, t),
);
}
return super.lerpTo(b, t);
}
class _BubblePainter extends BoxPainter {
_BubblePainter(this.decoration, VoidCallback onChanged)
: assert(decoration != null),
super(onChanged);
final BubbleTabIndicator decoration;
double get indicatorHeight => decoration.indicatorHeight;
Color get indicatorColor => decoration.indicatorColor;
double get indicatorRadius => decoration.indicatorRadius;
EdgeInsetsGeometry get padding => decoration.padding;
EdgeInsetsGeometry get insets => decoration.insets;
TabBarIndicatorSize get tabBarIndicatorSize => decoration.tabBarIndicatorSize;
Rect _indicatorRectFor(Rect rect, TextDirection textDirection) {
assert(rect != null);
assert(textDirection != null);
Rect indicator = padding.resolve(textDirection).inflateRect(rect);
if (tabBarIndicatorSize == TabBarIndicatorSize.tab) {
indicator = insets.resolve(textDirection).deflateRect(rect);
}
return new Rect.fromLTWH(
indicator.left,
indicator.top,
indicator.width,
indicator.height,
);
}
@override
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
assert(configuration != null);
assert(configuration.size != null);
final Rect rect = Offset(
offset.dx, (configuration.size.height / 2) - indicatorHeight / 2) &
Size(configuration.size.width, indicatorHeight);
final TextDirection textDirection = configuration.textDirection;
final Rect indicator = _indicatorRectFor(rect, textDirection);
final Paint paint = Paint();
paint.color = indicatorColor;
paint.style = PaintingStyle.fill;
canvas.drawRRect(
RRect.fromRectAndRadius(indicator, Radius.circular(indicatorRadius)),
paint);
}
}