Merge pull request #8 from NikhilVadoliya/dev

Code Refactor and implement swipe feature for open/close drawer
master
NikhilVadoliya 4 years ago committed by GitHub
commit 298a46b22a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -30,25 +30,26 @@ class _MyAppState extends State<MyApp> {
return MaterialApp( return MaterialApp(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
home: Scaffold( home: Scaffold(
body: SliderMenuContainer( body: Padding(
appBarColor: Colors.white, padding: const EdgeInsets.only(top: 0),
key: _key, child: SliderMenuContainer(
appBarPadding: const EdgeInsets.only(top: 20), appBarColor: Colors.white,
sliderMenuOpenOffset: 250, key: _key,
appBarHeight: 60, sliderMenuOpenSize: 200,
title: Text( title: Text(
title, title,
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w700), style: TextStyle(fontSize: 22, fontWeight: FontWeight.w700),
), ),
sliderMenu: MenuWidget( sliderMenu: MenuWidget(
onItemClick: (title) { onItemClick: (title) {
_key.currentState.closeDrawer(); _key.currentState.closeDrawer();
setState(() { setState(() {
this.title = title; this.title = title;
}); });
}, },
), ),
sliderMain: MainWidget()), sliderMain: MainWidget()),
),
), ),
); );
} }

@ -27,8 +27,11 @@ class _MainWidgetState extends State<MainWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return Container(
margin: const EdgeInsets.all(20),
child: ListView.separated( child: ListView.separated(
padding: const EdgeInsets.symmetric(horizontal: 10), scrollDirection: Axis.vertical,
// physics: BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10),
itemBuilder: (builder, index) { itemBuilder: (builder, index) {
return LimitedBox( return LimitedBox(
maxHeight: 150, maxHeight: 150,

@ -1,4 +1,4 @@
library flutter_slider_drawer; library flutter_slider_drawer;
export 'package:flutter_slider_drawer/src/slider_open.dart'; export 'package:flutter_slider_drawer/src/slider_direction.dart';
export 'package:flutter_slider_drawer/src/slider.dart'; export 'package:flutter_slider_drawer/src/slider.dart';

@ -0,0 +1,76 @@
import 'package:flutter/material.dart';
import 'package:flutter_slider_drawer/src/slider_direction.dart';
class SliderAppBar extends StatelessWidget {
final EdgeInsets appBarPadding;
final Color appBarColor;
final Widget drawerIcon;
final Color splashColor;
final Color drawerIconColor;
final double drawerIconSize;
final double appBarHeight;
final AnimationController animationController;
final VoidCallback onTap;
final Widget title;
final bool isTitleCenter;
final Widget trailing;
final SlideDirection slideDirection;
const SliderAppBar(
{Key key,
this.appBarPadding,
this.appBarColor,
this.drawerIcon,
this.splashColor,
this.drawerIconColor,
this.drawerIconSize,
this.animationController,
this.onTap,
this.title,
this.isTitleCenter,
this.trailing,
this.slideDirection, this.appBarHeight})
: super(key: key);
@override
Widget build(BuildContext context) {
return Container(
height: appBarHeight,
padding: appBarPadding ?? const EdgeInsets.only(top: 24),
color: appBarColor,
child: Row(
children: appBar(),
),
);
}
List<Widget> appBar() {
List<Widget> list = [
drawerIcon ??
IconButton(
splashColor: splashColor ?? Colors.black,
icon: AnimatedIcon(
icon: AnimatedIcons.menu_close,
color: drawerIconColor,
size: drawerIconSize,
progress: animationController),
onPressed: () => onTap()),
Expanded(
child: isTitleCenter
? Center(
child: title,
)
: title,
),
trailing ??
SizedBox(
width: 35,
)
];
if (slideDirection == SlideDirection.RIGHT_TO_LEFT) {
return list.reversed.toList();
}
return list;
}
}

@ -0,0 +1,36 @@
import 'dart:ui';
import 'package:flutter_slider_drawer/flutter_slider_drawer.dart';
class Utils {
///
/// This method get Offset base on [sliderOpen] type
///
static Offset getOffsetValues(SlideDirection direction, double value) {
switch (direction) {
case SlideDirection.LEFT_TO_RIGHT:
return Offset(value, 0);
case SlideDirection.RIGHT_TO_LEFT:
return Offset(-value, 0);
case SlideDirection.TOP_TO_BOTTOM:
return Offset(0, value);
default:
return Offset(value, 0);
}
}
static Offset getOffsetValueForShadow(
SlideDirection direction, double value, double slideOpenWidth) {
switch (direction) {
case SlideDirection.LEFT_TO_RIGHT:
return Offset(value - (slideOpenWidth > 50 ? 20 : 10), 0);
case SlideDirection.RIGHT_TO_LEFT:
return Offset(-value - 5, 0);
case SlideDirection.TOP_TO_BOTTOM:
return Offset(0, value - (slideOpenWidth > 50 ? 15 : 5));
default:
return Offset(value - 30.0, 0);
}
}
}

@ -0,0 +1,34 @@
import 'package:flutter/material.dart';
import 'package:flutter_slider_drawer/src/slider_direction.dart';
///
/// Build and Align the Menu widget based on the slide open type
///
class SlideMenuBar extends StatelessWidget {
final SlideDirection slideDirection;
final double sliderMenuOpenSize;
final Widget sliderMenu;
const SlideMenuBar(
{Key key, this.slideDirection, this.sliderMenuOpenSize, this.sliderMenu})
: super(key: key);
@override
Widget build(BuildContext context) {
var container = Container(
width: sliderMenuOpenSize,
child: sliderMenu,
);
switch (slideDirection) {
case SlideDirection.LEFT_TO_RIGHT:
return container;
break;
case SlideDirection.RIGHT_TO_LEFT:
return Positioned(right: 0, top: 0, bottom: 0, child: container);
case SlideDirection.TOP_TO_BOTTOM:
return Positioned(right: 0, left: 0, top: 0, child: container);
break;
}
return Container();
}
}

@ -1,13 +1,19 @@
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_slider_drawer/flutter_slider_drawer.dart'; import 'package:flutter_slider_drawer/src/app_bar.dart';
import 'package:flutter_slider_drawer/src/menu_bar.dart';
import 'package:flutter_slider_drawer/src/helper/utils.dart';
import 'package:flutter_slider_drawer/src/slider_direction.dart';
class SliderMenuContainer extends StatefulWidget { class SliderMenuContainer extends StatefulWidget {
final Widget sliderMenu; final Widget sliderMenu;
final Widget sliderMain; final Widget sliderMain;
final int sliderAnimationTimeInMilliseconds; final int animationDuration;
final double sliderMenuOpenOffset; final double sliderMenuOpenSize;
final double sliderMenuCloseOffset; final double sliderMenuCloseSize;
final bool isDraggable;
final bool hasAppBar;
final Color drawerIconColor; final Color drawerIconColor;
final Widget drawerIcon; final Widget drawerIcon;
final double drawerIconSize; final double drawerIconSize;
@ -23,14 +29,15 @@ class SliderMenuContainer extends StatefulWidget {
final Widget trailing; final Widget trailing;
final Color appBarColor; final Color appBarColor;
final EdgeInsets appBarPadding; final EdgeInsets appBarPadding;
final SliderOpen sliderOpen; final SlideDirection slideDirection;
const SliderMenuContainer({ const SliderMenuContainer({
Key key, Key key,
this.sliderMenu, this.sliderMenu,
this.sliderMain, this.sliderMain,
this.sliderAnimationTimeInMilliseconds = 200, this.isDraggable = true,
this.sliderMenuOpenOffset = 265, this.animationDuration = 200,
this.sliderMenuOpenSize = 265,
this.drawerIconColor = Colors.black, this.drawerIconColor = Colors.black,
this.drawerIcon, this.drawerIcon,
this.splashColor, this.splashColor,
@ -40,13 +47,14 @@ class SliderMenuContainer extends StatefulWidget {
this.appBarPadding, this.appBarPadding,
this.title, this.title,
this.drawerIconSize = 27, this.drawerIconSize = 27,
this.appBarHeight, this.appBarHeight = 70,
this.sliderMenuCloseOffset = 0, this.sliderMenuCloseSize = 0,
this.sliderOpen = SliderOpen.LEFT_TO_RIGHT, this.slideDirection = SlideDirection.LEFT_TO_RIGHT,
this.isShadow = false, this.isShadow = false,
this.shadowColor = Colors.grey, this.shadowColor = Colors.grey,
this.shadowBlurRadius = 25.0, this.shadowBlurRadius = 25.0,
this.shadowSpreadRadius = 5.0, this.shadowSpreadRadius = 5.0,
this.hasAppBar = true,
}) : assert(sliderMenu != null), }) : assert(sliderMenu != null),
assert(sliderMain != null), assert(sliderMain != null),
super(key: key); super(key: key);
@ -56,236 +64,232 @@ class SliderMenuContainer extends StatefulWidget {
} }
class SliderMenuContainerState extends State<SliderMenuContainer> class SliderMenuContainerState extends State<SliderMenuContainer>
with SingleTickerProviderStateMixin { with TickerProviderStateMixin {
double _slideBarXOffset = 0; static const double WIDTH_GESTURE = 50.0;
double _slideBarYOffset = 0; static const double HEIGHT_GESTURE = 30.0;
bool _isSlideBarOpen = false; static const double BLUR_SHADOW = 20.0;
AnimationController _animationController; double slideAmount = 0.0;
double _percent = 0.0;
AnimationController _animationDrawerController;
Animation animation;
bool dragging = false;
Widget drawerIcon; Widget drawerIcon;
/// check whether drawer is open /// check whether drawer is open
bool get isDrawerOpen => _isSlideBarOpen; bool get isDrawerOpen => _animationDrawerController.isCompleted;
/// Toggle drawer /// it's provide [animationController] for handle and lister drawer animation
void toggle() { AnimationController get animationController => _animationDrawerController;
setState(() {
_isSlideBarOpen
? _animationController.reverse()
: _animationController.forward();
if (widget.sliderOpen == SliderOpen.LEFT_TO_RIGHT ||
widget.sliderOpen == SliderOpen.RIGHT_TO_LEFT) {
_slideBarXOffset = _isSlideBarOpen
? widget.sliderMenuCloseOffset
: widget.sliderMenuOpenOffset;
} else {
_slideBarYOffset = _isSlideBarOpen
? widget.sliderMenuCloseOffset
: widget.sliderMenuOpenOffset;
}
_isSlideBarOpen = !_isSlideBarOpen; /// Toggle drawer
}); void toggle() => _animationDrawerController.isCompleted
} ? _animationDrawerController.reverse()
: _animationDrawerController.forward();
/// Open drawer /// Open drawer
void openDrawer() { void openDrawer() => _animationDrawerController.forward();
setState(() {
_animationController.forward();
if (widget.sliderOpen == SliderOpen.LEFT_TO_RIGHT ||
widget.sliderOpen == SliderOpen.RIGHT_TO_LEFT) {
_slideBarXOffset = widget.sliderMenuOpenOffset;
} else {
_slideBarYOffset = widget.sliderMenuOpenOffset;
}
_isSlideBarOpen = true;
});
}
/// Close drawer /// Close drawer
void closeDrawer() { void closeDrawer() => _animationDrawerController.reverse();
setState(() {
_animationController.reverse();
if (widget.sliderOpen == SliderOpen.LEFT_TO_RIGHT ||
widget.sliderOpen == SliderOpen.RIGHT_TO_LEFT) {
_slideBarXOffset = widget.sliderMenuCloseOffset;
} else {
_slideBarYOffset = widget.sliderMenuCloseOffset;
}
_isSlideBarOpen = false;
});
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_animationController = AnimationController( _animationDrawerController = AnimationController(
vsync: this, vsync: this,
duration: duration: Duration(milliseconds: widget.animationDuration));
Duration(milliseconds: widget.sliderAnimationTimeInMilliseconds));
animation = Tween<double>(
begin: widget.sliderMenuCloseSize, end: widget.sliderMenuOpenSize)
.animate(CurvedAnimation(
parent: _animationDrawerController,
curve: Curves.easeIn,
reverseCurve: Curves.easeOut));
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Container( return LayoutBuilder(builder: (context, constrain) {
child: Stack(children: <Widget>[ return Container(
/// Display Menu child: Stack(children: <Widget>[
menuWidget(), /// Display Menu
SlideMenuBar(
slideDirection: widget.slideDirection,
sliderMenu: widget.sliderMenu,
sliderMenuOpenSize: widget.sliderMenuOpenSize,
),
/// Displaying the shadow
if (widget.isShadow) ...[
AnimatedBuilder(
animation: _animationDrawerController,
builder: (_, child) {
return Transform.translate(
offset: Utils.getOffsetValueForShadow(widget.slideDirection,
animation.value, widget.sliderMenuOpenSize),
child: child,
);
return child;
},
child: Container(
width: double.infinity,
height: double.infinity,
decoration: BoxDecoration(shape: BoxShape.rectangle, boxShadow: [
BoxShadow(
color: widget.shadowColor,
blurRadius: widget.shadowBlurRadius,
// soften the shadow
spreadRadius: widget.shadowSpreadRadius,
//extend the shadow
offset: Offset(
15.0, // Move to right 15 horizontally
15.0, // Move to bottom 15 Vertically
),
)
]),
),
),
],
/// Displaying the shadow //Display Main Screen
if (widget.isShadow) ...[ AnimatedBuilder(
AnimatedContainer( animation: _animationDrawerController,
duration: builder: (_, child) {
Duration(milliseconds: widget.sliderAnimationTimeInMilliseconds), return Transform.translate(
curve: Curves.easeIn, offset:
width: double.infinity, Utils.getOffsetValues(widget.slideDirection, animation.value),
height: double.infinity, child: child,
transform: getTranslationValuesForShadow(widget.sliderOpen), );
decoration: BoxDecoration(shape: BoxShape.rectangle, boxShadow: [ },
BoxShadow( child: GestureDetector(
color: widget.shadowColor, behavior: HitTestBehavior.deferToChild,
blurRadius: widget.shadowBlurRadius, // soften the shadow onHorizontalDragStart: _onHorizontalDragStart,
spreadRadius: widget.shadowSpreadRadius, //extend the shadow onHorizontalDragEnd: _onHorizontalDragEnd,
offset: Offset( onHorizontalDragUpdate: (detail) =>
15.0, // Move to right 10 horizontally _onHorizontalDragUpdate(detail, constrain),
15.0, // Move to bottom 10 Vertically child: Container(
width: double.infinity,
height: double.infinity,
color: widget.appBarColor,
child: Column(
children: <Widget>[
if (widget.hasAppBar)
SliderAppBar(
slideDirection: widget.slideDirection,
onTap: () => toggle(),
appBarHeight: widget.appBarHeight,
animationController: _animationDrawerController,
appBarColor: widget.appBarColor,
appBarPadding: widget.appBarPadding,
drawerIcon: widget.drawerIcon,
drawerIconColor: widget.drawerIconColor,
drawerIconSize: widget.drawerIconSize,
isTitleCenter: widget.isTitleCenter,
splashColor: widget.splashColor,
title: widget.title ?? '',
trailing: widget.trailing,
),
Expanded(child: widget.sliderMain),
],
), ),
) ),
]), ),
), ),
], ]));
});
}
/// Display Main Screen @override
AnimatedContainer( void dispose() {
duration: super.dispose();
Duration(milliseconds: widget.sliderAnimationTimeInMilliseconds), _animationDrawerController.dispose();
curve: Curves.easeIn,
width: double.infinity,
height: double.infinity,
color: widget.appBarColor,
transform: getTranslationValues(widget.sliderOpen),
child: Column(
children: <Widget>[
Container(
padding: widget.appBarPadding ?? const EdgeInsets.only(top: 24),
color: widget.appBarColor,
child: Row(
children: appBar(),
),
),
Expanded(child: widget.sliderMain),
],
)),
]));
} }
List<Widget> appBar() { void _onHorizontalDragStart(DragStartDetails detail) {
List<Widget> list = [ if (!widget.isDraggable) return;
widget.drawerIcon ??
IconButton(
splashColor: widget.splashColor ?? Colors.black,
icon: AnimatedIcon(
icon: AnimatedIcons.menu_close,
color: widget.drawerIconColor,
size: widget.drawerIconSize,
progress: _animationController),
onPressed: () {
toggle();
}),
Expanded(
child: widget.isTitleCenter
? Center(
child: widget.title,
)
: widget.title,
),
widget.trailing ??
SizedBox(
width: 35,
)
];
if (widget.sliderOpen == SliderOpen.RIGHT_TO_LEFT) { //Check use start dragging from left edge / right edge then enable dragging
return list.reversed.toList(); if ((widget.slideDirection == SlideDirection.LEFT_TO_RIGHT &&
detail.localPosition.dx <= WIDTH_GESTURE) ||
(widget.slideDirection == SlideDirection.RIGHT_TO_LEFT &&
detail.localPosition.dx >=
WIDTH_GESTURE) /*&&
detail.localPosition.dy <= widget.appBarHeight*/
) {
this.setState(() {
dragging = true;
});
}
//Check use start dragging from top edge / bottom edge then enable dragging
if (widget.slideDirection == SlideDirection.TOP_TO_BOTTOM &&
detail.localPosition.dy >= HEIGHT_GESTURE) {
this.setState(() {
dragging = true;
});
} }
return list;
} }
/// Build and Align the Menu widget based on the slide open type void _onHorizontalDragEnd(DragEndDetails detail) {
menuWidget() { if (!widget.isDraggable) return;
switch (widget.sliderOpen) { if (dragging) {
case SliderOpen.LEFT_TO_RIGHT: openOrClose();
return Container( setState(() {
width: widget.sliderMenuOpenOffset, dragging = false;
child: widget.sliderMenu, });
);
break;
case SliderOpen.RIGHT_TO_LEFT:
return Positioned(
right: 0,
top: 0,
bottom: 0,
child: Container(
width: widget.sliderMenuOpenOffset,
child: widget.sliderMenu,
),
);
case SliderOpen.TOP_TO_BOTTOM:
return Positioned(
right: 0,
left: 0,
top: 0,
child: Container(
width: widget.sliderMenuOpenOffset,
child: widget.sliderMenu,
),
);
break;
} }
} }
/// void _onHorizontalDragUpdate(
/// This method get Matrix4 data base on [sliderOpen] type DragUpdateDetails detail,
/// BoxConstraints constraints,
) {
Matrix4 getTranslationValues(SliderOpen sliderOpen) { if (!widget.isDraggable) return;
switch (sliderOpen) { // open drawer for left/right type drawer
case SliderOpen.LEFT_TO_RIGHT: if (dragging && widget.slideDirection == SlideDirection.LEFT_TO_RIGHT ||
return Matrix4.translationValues( widget.slideDirection == SlideDirection.RIGHT_TO_LEFT) {
_slideBarXOffset, _slideBarYOffset, 1.0); var globalPosition = detail.globalPosition.dx;
case SliderOpen.RIGHT_TO_LEFT: globalPosition = globalPosition < 0 ? 0 : globalPosition;
return Matrix4.translationValues( double position = globalPosition / constraints.maxWidth;
-_slideBarXOffset, _slideBarYOffset, 1.0); var realPosition = widget.slideDirection == SlideDirection.LEFT_TO_RIGHT
? position
case SliderOpen.TOP_TO_BOTTOM: : (1 - position);
return Matrix4.translationValues(0, _slideBarYOffset, 1.0); move(realPosition);
}
// open drawer for top/bottom type drawer
/*if (dragging && widget.slideDirection == SlideDirection.TOP_TO_BOTTOM) {
var globalPosition = detail.globalPosition.dx;
globalPosition = globalPosition < 0 ? 0 : globalPosition;
double position = globalPosition / constraints.maxHeight;
var realPosition = widget.slideDirection == SlideDirection.TOP_TO_BOTTOM
? position
: (1 - position);
move(realPosition);
}*/
default: // close drawer for left/right type drawer
return Matrix4.translationValues(0, 0, 1.0); if (isDrawerOpen &&
(widget.slideDirection == SlideDirection.LEFT_TO_RIGHT ||
widget.slideDirection == SlideDirection.RIGHT_TO_LEFT) &&
detail.delta.dx < 15) {
closeDrawer();
} }
} }
Matrix4 getTranslationValuesForShadow(SliderOpen sliderOpen) { move(double percent) {
switch (sliderOpen) { _percent = percent;
case SliderOpen.LEFT_TO_RIGHT: _animationDrawerController.value = percent;
return Matrix4.translationValues( _animationDrawerController.notifyListeners();
_slideBarXOffset - 30, _slideBarYOffset, 1.0);
case SliderOpen.RIGHT_TO_LEFT:
return Matrix4.translationValues(
-_slideBarXOffset - 5, _slideBarYOffset, 1.0);
case SliderOpen.TOP_TO_BOTTOM:
return Matrix4.translationValues(0, _slideBarYOffset - 20, 1.0);
default:
return Matrix4.translationValues(0, 0, 1.0);
}
} }
@override openOrClose() {
void dispose() { if (_percent > 0.3) {
super.dispose(); openDrawer();
_animationController.dispose(); } else {
closeDrawer();
}
} }
} }

@ -0,0 +1 @@
enum SlideDirection { LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM }

@ -1 +0,0 @@
enum SliderOpen { LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM }
Loading…
Cancel
Save