Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
rafalbednarczuk committed Mar 7, 2019
0 parents commit 4b92e24
Show file tree
Hide file tree
Showing 12 changed files with 484 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
@@ -0,0 +1,10 @@
.DS_Store
.dart_tool/

.packages
.pub/

build/
ios/.generated/
ios/Flutter/Generated.xcconfig
ios/Runner/GeneratedPluginRegistrant.*
3 changes: 3 additions & 0 deletions CHANGELOG.md
@@ -0,0 +1,3 @@
## [0.0.1] - TODO: Add release date.

* TODO: Describe initial release.
1 change: 1 addition & 0 deletions LICENSE
@@ -0,0 +1 @@
TODO: Add your license here.
14 changes: 14 additions & 0 deletions README.md
@@ -0,0 +1,14 @@
# curved_navigation_bar

A new Flutter package.

## Getting Started

This project is a starting point for a Dart
[package](https://flutter.io/developing-packages/),
a library module containing code that can be shared easily across
multiple Flutter or Dart projects.

For help getting started with Flutter, view our
[online documentation](https://flutter.io/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
@@ -0,0 +1,23 @@
package io.flutter.plugins;

import io.flutter.plugin.common.PluginRegistry;

/**
* Generated file. Do not edit.
*/
public final class GeneratedPluginRegistrant {
public static void registerWith(PluginRegistry registry) {
if (alreadyRegisteredWith(registry)) {
return;
}
}

private static boolean alreadyRegisteredWith(PluginRegistry registry) {
final String key = GeneratedPluginRegistrant.class.getCanonicalName();
if (registry.hasPlugin(key)) {
return true;
}
registry.registrarFor(key);
return false;
}
}
2 changes: 2 additions & 0 deletions android/local.properties
@@ -0,0 +1,2 @@
sdk.dir=/home/rafal/Android/Sdk
flutter.sdk=/home/rafal/flutter
19 changes: 19 additions & 0 deletions curved_navigation_bar.iml
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
</content>
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart Packages" level="project" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
</component>
</module>
136 changes: 136 additions & 0 deletions lib/curved_navigation_bar.dart
@@ -0,0 +1,136 @@
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'src/nav_button.dart';
import 'src/nav_custom_painter.dart';

class CurvedNavigationBar extends StatefulWidget {
final List<Widget> items;
final int index;
final Color color;
final Color backgroundColor;
final ValueChanged<int> onTap;
final Curve animationCurve;
final Duration animationDuration;

CurvedNavigationBar(
{Key key,
@required this.items,
this.index = 0,
this.color = Colors.white,
this.backgroundColor = Colors.blueAccent,
this.onTap,
this.animationCurve = Curves.easeOutCubic,
this.animationDuration = const Duration(milliseconds: 600)})
: assert(items != null),
assert(items.length >= 2),
assert(0 <= index && index < items.length),
super(key: key);

@override
_CurvedNavigationBarState createState() => _CurvedNavigationBarState();
}

class _CurvedNavigationBarState extends State<CurvedNavigationBar>
with SingleTickerProviderStateMixin {
double _startingPos = 0;
int _endingIndex = 0;
double _pos = 0;
double _buttonHide = 0;
Widget _icon;
AnimationController _animationController;
int _length;

@override
void initState() {
super.initState();
_icon = widget.items[0];
_length = widget.items.length;
_animationController = AnimationController(vsync: this);
_animationController.addListener(() {
setState(() {
_pos = _animationController.value;
final endingPos = _endingIndex / widget.items.length;
final middle = (endingPos + _startingPos) / 2;
if ((endingPos - _pos).abs() < (_startingPos - _pos).abs()) {
_icon = widget.items[_endingIndex];
}
_buttonHide =
(1 - ((middle - _pos) / (_startingPos - middle)).abs()).abs();
});
});
}

@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Container(
color: widget.backgroundColor,
height: 75.0,
child: Stack(
overflow: Overflow.visible,
alignment: Alignment.bottomCenter,
children: <Widget>[
Positioned(
bottom: -40,
left: _pos * size.width,
width: size.width / _length,
child: Center(
child: Transform.translate(
offset: Offset(
0,
-(1 - _buttonHide) * 80,
),
child: Material(
color: widget.color,
type: MaterialType.circle,
elevation: 3.0,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: _icon,
),
),
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: CustomPaint(
painter: NavCustomPainter(_pos, _length, widget.color),
child: Container(
height: 75.0,
),
),
),
Align(
alignment: Alignment.bottomCenter,
child: SizedBox(
height: 100.0,
child: Row(
children: widget.items.map((item) {
return NavButton(
onTap: _buttonTap,
position: _pos,
length: _length,
index: widget.items.indexOf(item),
child: item,
);
}).toList())),
),
],
),
);
}

void _buttonTap(int index) {
if (widget.onTap != null) {
widget.onTap(index);
}
final newPosition = index / _length;
setState(() {
_startingPos = _pos;
_endingIndex = index;
_animationController.animateTo(newPosition,
duration: widget.animationDuration, curve: widget.animationCurve);
});
}
}
33 changes: 33 additions & 0 deletions lib/src/nav_button.dart
@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';

class NavButton extends StatelessWidget {
final double position;
final int length;
final int index;
final ValueChanged<int> onTap;
final Widget child;

NavButton({this.onTap, this.position, this.length, this.index, this.child});

@override
Widget build(BuildContext context) {
final desiredPosition = 1.0 / length * index;
final difference = (position - desiredPosition).abs();
final verticalAlignment = 1 - length * difference;
final opacity = length * difference;
return Expanded(
child: InkWell(
onTap: () {
onTap(index);
},
child: Transform.translate(
offset:
Offset(0, difference < 1.0 / length ? verticalAlignment * 40 : 0),
child: Opacity(
opacity: difference < 1.0 / length * 0.99 ? opacity : 1.0,
child: child),
),
),
);
}
}
50 changes: 50 additions & 0 deletions lib/src/nav_custom_painter.dart
@@ -0,0 +1,50 @@
import 'package:flutter/material.dart';

class NavCustomPainter extends CustomPainter {
double loc;
double s;
Color color;

NavCustomPainter(double startingLoc, int itemsLength, this.color) {
final span = 1.0 / itemsLength;
s = 0.2;
loc = startingLoc + (span - s) / 2;
}

@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.fill;

final path = Path()
..moveTo(0, 0)
..lineTo((loc - 0.1) * size.width, 0)
..cubicTo(
(loc + s * 0.20) * size.width,
size.height * 0.05,
loc * size.width,
size.height * 0.60,
(loc + s * 0.50) * size.width,
size.height * 0.60,
)
..cubicTo(
(loc + s) * size.width,
size.height * 0.60,
(loc + s - s * 0.20) * size.width,
size.height * 0.05,
(loc + s + 0.1) * size.width,
0,
)
..lineTo(size.width, 0)
..lineTo(size.width, size.height)
..lineTo(0, size.height)
..close();
canvas.drawPath(path, paint);
}

@override
bool shouldRepaint(CustomPainter oldDelegate) {
return this != oldDelegate;
}
}

0 comments on commit 4b92e24

Please sign in to comment.