Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 14 additions & 13 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -78,26 +78,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
version: "10.0.4"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.3"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.1"
lints:
dependency: transitive
description:
Expand Down Expand Up @@ -126,10 +126,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.12.0"
path:
dependency: transitive
description:
Expand Down Expand Up @@ -187,10 +187,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev"
source: hosted
version: "0.6.1"
version: "0.7.0"
vector_math:
dependency: transitive
description:
Expand All @@ -203,9 +203,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev"
source: hosted
version: "13.0.0"
version: "14.2.1"
sdks:
dart: ">=3.2.0-0 <4.0.0"
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
95 changes: 29 additions & 66 deletions lib/circular_bottom_navigation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,22 +44,16 @@ class CircularBottomNavigation extends StatefulWidget {
this.controller,
this.allowSelectedIconCallback = false,
backgroundBoxShadow,
}) : backgroundBoxShadow = backgroundBoxShadow ??
[BoxShadow(color: Colors.grey, blurRadius: 2.0)],
barBackgroundColor =
(barBackgroundGradient == null && barBackgroundColor == null)
? Colors.white
: barBackgroundColor,
assert(barBackgroundColor == null || barBackgroundGradient == null,
"Both barBackgroundColor and barBackgroundGradient can't be not null."),
}) : backgroundBoxShadow = backgroundBoxShadow ?? [BoxShadow(color: Colors.grey, blurRadius: 2.0)],
barBackgroundColor = (barBackgroundGradient == null && barBackgroundColor == null) ? Colors.white : barBackgroundColor,
assert(barBackgroundColor == null || barBackgroundGradient == null, "Both barBackgroundColor and barBackgroundGradient can't be not null."),
assert(tabItems.length != 0, "tabItems is required");

@override
State<StatefulWidget> createState() => _CircularBottomNavigationState();
}

class _CircularBottomNavigationState extends State<CircularBottomNavigation>
with TickerProviderStateMixin {
class _CircularBottomNavigationState extends State<CircularBottomNavigation> with TickerProviderStateMixin {
Curve _animationsCurve = Cubic(0.27, 1.21, .77, 1.09);

late AnimationController itemsController;
Expand All @@ -71,33 +65,31 @@ class _CircularBottomNavigationState extends State<CircularBottomNavigation>
int? selectedPos;
int? previousSelectedPos;

CircularBottomNavigationController? _controller;
late CircularBottomNavigationController _controller;

@override
void initState() {
super.initState();
if (widget.controller != null) {
_controller = widget.controller;
previousSelectedPos = selectedPos = _controller!.value;
_controller = widget.controller!;
previousSelectedPos = selectedPos = _controller.value;
} else {
previousSelectedPos = selectedPos = widget.selectedPos;
_controller = CircularBottomNavigationController(selectedPos);
}

_controller!.addListener(_newSelectedPosNotify);
_controller.addListener(_newSelectedPosNotify);

_itemsSelectedState = List.generate(widget.tabItems.length, (index) {
return selectedPos == index ? 1.0 : 0.0;
});

itemsController =
AnimationController(vsync: this, duration: widget.animationDuration);
itemsController = AnimationController(vsync: this, duration: widget.animationDuration);
itemsController.addListener(() {
setState(() {
_itemsSelectedState.asMap().forEach((i, value) {
if (i == previousSelectedPos) {
_itemsSelectedState[previousSelectedPos!] =
1.0 - itemsAnimation.value;
_itemsSelectedState[previousSelectedPos!] = 1.0 - itemsAnimation.value;
} else if (i == selectedPos) {
_itemsSelectedState[selectedPos!] = itemsAnimation.value;
} else {
Expand All @@ -107,44 +99,35 @@ class _CircularBottomNavigationState extends State<CircularBottomNavigation>
});
});

selectedPosAnimation = makeSelectedPosAnimation(
selectedPos!.toDouble(), selectedPos!.toDouble());
selectedPosAnimation = makeSelectedPosAnimation(selectedPos!.toDouble(), selectedPos!.toDouble());

itemsAnimation = Tween(begin: 0.0, end: 1.0).animate(
CurvedAnimation(parent: itemsController, curve: _animationsCurve));
itemsAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(parent: itemsController, curve: _animationsCurve));
}

Animation<double> makeSelectedPosAnimation(double begin, double end) {
return Tween(begin: begin, end: end).animate(
CurvedAnimation(parent: itemsController, curve: _animationsCurve));
return Tween(begin: begin, end: end).animate(CurvedAnimation(parent: itemsController, curve: _animationsCurve));
}

void onSelectedPosAnimate() {
setState(() {});
}

void _newSelectedPosNotify() {
_setSelectedPos(widget.controller!.value);
_setSelectedPos(_controller.value);
}

@override
Widget build(BuildContext context) {
double maxShadowHeight = (widget.backgroundBoxShadow ?? []).isNotEmpty
? widget.backgroundBoxShadow!.map((e) => e.blurRadius).reduce(max)
: 0.0;
double maxShadowHeight = (widget.backgroundBoxShadow ?? []).isNotEmpty ? widget.backgroundBoxShadow!.map((e) => e.blurRadius).reduce(max) : 0.0;
double fullWidth = MediaQuery.of(context).size.width;
double fullHeight = widget.barHeight +
(widget.circleSize / 2) +
widget.circleStrokeWidth +
maxShadowHeight;
double fullHeight = widget.barHeight + (widget.circleSize / 2) + widget.circleStrokeWidth + maxShadowHeight;
double sectionsWidth = fullWidth / widget.tabItems.length;
final isRTL = Directionality.of(context) == TextDirection.rtl;

//Create the boxes Rect
List<Rect> boxes = [];
widget.tabItems.asMap().forEach((i, tabItem) {
double left =
isRTL ? fullWidth - (i + 1) * sectionsWidth : i * sectionsWidth;
double left = isRTL ? fullWidth - (i + 1) * sectionsWidth : i * sectionsWidth;
double top = fullHeight - widget.barHeight;
double right = left + sectionsWidth;
double bottom = fullHeight;
Expand Down Expand Up @@ -197,9 +180,7 @@ class _CircularBottomNavigationState extends State<CircularBottomNavigation>
topLeft: Radius.circular(widget.circleSize / 2),
topRight: Radius.circular(widget.circleSize / 2),
),
color:
widget.tabItems[selectedPos!].circleStrokeColor ??
widget.barBackgroundColor,
color: widget.tabItems[selectedPos!].circleStrokeColor ?? widget.barBackgroundColor,
boxShadow: widget.backgroundBoxShadow,
),
),
Expand All @@ -212,41 +193,30 @@ class _CircularBottomNavigationState extends State<CircularBottomNavigation>
bottomLeft: Radius.circular(widget.circleSize / 2),
bottomRight: Radius.circular(widget.circleSize / 2),
),
color:
widget.tabItems[selectedPos!].circleStrokeColor ??
widget.barBackgroundColor,
color: widget.tabItems[selectedPos!].circleStrokeColor ?? widget.barBackgroundColor,
),
),
),
],
),
Container(
margin: EdgeInsets.all(widget.circleStrokeWidth),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: widget.tabItems[selectedPos!].circleColor),
decoration: BoxDecoration(shape: BoxShape.circle, color: widget.tabItems[selectedPos!].circleColor),
),
],
),
),
left: isRTL
? fullWidth -
((selectedPosAnimation.value * sectionsWidth) +
(sectionsWidth / 2) +
(widget.circleSize / 2))
: (selectedPosAnimation.value * sectionsWidth) +
(sectionsWidth / 2) -
(widget.circleSize / 2),
? fullWidth - ((selectedPosAnimation.value * sectionsWidth) + (sectionsWidth / 2) + (widget.circleSize / 2))
: (selectedPosAnimation.value * sectionsWidth) + (sectionsWidth / 2) - (widget.circleSize / 2),
top: maxShadowHeight,
),
);

//Here are the Icons and texts of items
boxes.asMap().forEach((int pos, Rect r) {
// Icon
Color iconColor = pos == selectedPos
? widget.selectedIconColor
: widget.normalIconColor;
Color iconColor = pos == selectedPos ? widget.selectedIconColor : widget.normalIconColor;
double scaleFactor = pos == selectedPos ? 1.2 : 1.0;
children.add(
Positioned(
Expand All @@ -259,10 +229,7 @@ class _CircularBottomNavigationState extends State<CircularBottomNavigation>
),
),
left: r.center.dx - (widget.iconsSize / 2),
top: r.center.dy -
(widget.iconsSize / 2) -
(_itemsSelectedState[pos] *
((widget.barHeight / 2) + widget.circleStrokeWidth)),
top: r.center.dy - (widget.iconsSize / 2) - (_itemsSelectedState[pos] * ((widget.barHeight / 2) + widget.circleStrokeWidth)),
),
);

Expand Down Expand Up @@ -290,18 +257,15 @@ class _CircularBottomNavigationState extends State<CircularBottomNavigation>
),
),
left: r.left,
top: r.top +
(widget.circleSize / 2) -
(widget.circleStrokeWidth * 2) +
((1.0 - _itemsSelectedState[pos]) * textHeight),
top: r.top + (widget.circleSize / 2) - (widget.circleStrokeWidth * 2) + ((1.0 - _itemsSelectedState[pos]) * textHeight),
));

if (pos != selectedPos) {
children.add(
Positioned.fromRect(
child: GestureDetector(
onTap: () {
_controller!.value = pos;
_controller.value = pos;
},
),
rect: r,
Expand Down Expand Up @@ -335,8 +299,7 @@ class _CircularBottomNavigationState extends State<CircularBottomNavigation>

itemsController.forward(from: 0.0);

selectedPosAnimation = makeSelectedPosAnimation(
previousSelectedPos!.toDouble(), selectedPos!.toDouble());
selectedPosAnimation = makeSelectedPosAnimation(previousSelectedPos!.toDouble(), selectedPos!.toDouble());
selectedPosAnimation.addListener(onSelectedPosAnimate);

_selectedCallback();
Expand All @@ -350,9 +313,9 @@ class _CircularBottomNavigationState extends State<CircularBottomNavigation>

@override
void dispose() {
super.dispose();
itemsController.dispose();
_controller!.removeListener(_newSelectedPosNotify);
_controller.removeListener(_newSelectedPosNotify);
super.dispose();
}
}

Expand Down
27 changes: 14 additions & 13 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -71,26 +71,26 @@ packages:
dependency: transitive
description:
name: leak_tracker
sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa"
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev"
source: hosted
version: "10.0.0"
version: "10.0.4"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.3"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
version: "3.0.1"
lints:
dependency: transitive
description:
Expand Down Expand Up @@ -119,10 +119,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev"
source: hosted
version: "1.11.0"
version: "1.12.0"
path:
dependency: transitive
description:
Expand Down Expand Up @@ -180,10 +180,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b"
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev"
source: hosted
version: "0.6.1"
version: "0.7.0"
vector_math:
dependency: transitive
description:
Expand All @@ -196,9 +196,10 @@ packages:
dependency: transitive
description:
name: vm_service
sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev"
source: hosted
version: "13.0.0"
version: "14.2.1"
sdks:
dart: ">=3.2.0-0 <4.0.0"
dart: ">=3.3.0 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"