You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
359 lines
12 KiB
Dart
359 lines
12 KiB
Dart
import 'package:flutter/animation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_slider_drawer/src/app_bar.dart';
|
|
import 'package:flutter_slider_drawer/src/helper/slider_app_bar.dart';
|
|
import 'package:flutter_slider_drawer/src/helper/slider_shadow.dart';
|
|
import 'package:flutter_slider_drawer/src/slider_bar.dart';
|
|
import 'package:flutter_slider_drawer/src/helper/utils.dart';
|
|
import 'package:flutter_slider_drawer/src/slider_direction.dart';
|
|
|
|
/// SliderDrawer which have two [child] and [slider] parameter
|
|
///
|
|
///For Example :
|
|
///
|
|
/// Scaffold(
|
|
/// body: SliderDrawer(
|
|
/// appBar: SliderAppBar(
|
|
/// appBarColor: Colors.white,
|
|
/// title: Text(title,
|
|
/// style: const TextStyle(
|
|
/// fontSize: 22, fontWeight: FontWeight.w700))),
|
|
/// key: _key,
|
|
/// sliderOpenSize: 200,
|
|
/// slider: SliderView(
|
|
/// onItemClick: (title) {
|
|
/// _key.currentState!.closeSlider();
|
|
/// setState(() {
|
|
/// this.title = title;
|
|
/// });
|
|
/// },
|
|
/// ),
|
|
/// child: AuthorList()),
|
|
/// )
|
|
///
|
|
///
|
|
class SliderDrawer extends StatefulWidget {
|
|
/// [Widget] which display when user open drawer
|
|
///
|
|
final Widget slider;
|
|
|
|
/// [Widget] main screen widget
|
|
///
|
|
final Widget child;
|
|
|
|
/// [int] you can changes sliderDrawer open/close animation times with this [animationDuration]
|
|
/// parameter
|
|
///
|
|
final int animationDuration;
|
|
|
|
/// [double] you can change open drawer size by this parameter [sliderOpenSize]
|
|
///
|
|
final double sliderOpenSize;
|
|
|
|
///[double] you can change close drawer size by this parameter [sliderCloseSize]
|
|
/// by Default it is 0. if you set 30 then drawer will not close full it will display 30 size of width always
|
|
///
|
|
final double sliderCloseSize;
|
|
|
|
///[bool] if you set [false] then swipe to open feature disable.
|
|
///By Default it's true
|
|
///
|
|
final bool isDraggable;
|
|
|
|
///[appBar] if you set [null] then it will not display app bar
|
|
///
|
|
final SliderAppBar? appBar;
|
|
|
|
/// The primary color of the button when the drawer button is in the down (pressed) state.
|
|
/// The splash is represented as a circular overlay that appears above the
|
|
/// [highlightColor] overlay. The splash overlay has a center point that matches
|
|
/// the hit point of the user touch event. The splash overlay will expand to
|
|
/// fill the button area if the touch is held for long enough time. If the splash
|
|
/// color has transparency then the highlight and drawer button color will show through.
|
|
///
|
|
/// Defaults to the Theme's splash color, [ThemeData.splashColor].
|
|
///
|
|
final Color splashColor;
|
|
|
|
///[SliderShadow] you can enable shadow of [child] Widget by this parameter
|
|
final SliderShadow? sliderShadow;
|
|
|
|
///[slideDirection] you can change slide direction by this parameter [slideDirection]
|
|
///There are three type of [SlideDirection]
|
|
///[SlideDirection.RIGHT_TO_LEFT]
|
|
///[SlideDirection.LEFT_TO_RIGHT]
|
|
///[SlideDirection.TOP_TO_BOTTOM]
|
|
///
|
|
/// By default it's [SlideDirection.LEFT_TO_RIGHT]
|
|
///
|
|
final SlideDirection slideDirection;
|
|
|
|
const SliderDrawer({
|
|
Key? key,
|
|
required this.slider,
|
|
required this.child,
|
|
this.isDraggable = true,
|
|
this.animationDuration = 400,
|
|
this.sliderOpenSize = 265,
|
|
this.splashColor = Colors.transparent,
|
|
this.sliderCloseSize = 0,
|
|
this.slideDirection = SlideDirection.LEFT_TO_RIGHT,
|
|
this.sliderShadow,
|
|
this.appBar = const SliderAppBar(),
|
|
}) : super(key: key);
|
|
|
|
@override
|
|
SliderDrawerState createState() => SliderDrawerState();
|
|
}
|
|
|
|
class SliderDrawerState extends State<SliderDrawer>
|
|
with TickerProviderStateMixin {
|
|
static const double WIDTH_GESTURE = 50.0;
|
|
static const double HEIGHT_GESTURE = 30.0;
|
|
static const double BLUR_SHADOW = 20.0;
|
|
double _percent = 0.0;
|
|
|
|
AnimationController? _animationDrawerController;
|
|
late Animation _animation;
|
|
|
|
bool _isDragging = false;
|
|
|
|
/// check whether drawer is open
|
|
bool get isDrawerOpen => _animationDrawerController!.isCompleted;
|
|
|
|
/// it's provide [animationController] for handle and lister drawer animation
|
|
AnimationController? get animationController => _animationDrawerController;
|
|
|
|
/// Toggle drawer
|
|
void toggle() => _animationDrawerController!.isCompleted
|
|
? _animationDrawerController!.reverse()
|
|
: _animationDrawerController!.forward();
|
|
|
|
/// Open slider
|
|
void openSlider() => _animationDrawerController!.forward();
|
|
|
|
/// Close slider
|
|
void closeSlider() => _animationDrawerController!.reverse();
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
_animationDrawerController = AnimationController(
|
|
vsync: this,
|
|
duration: Duration(milliseconds: widget.animationDuration));
|
|
|
|
_animation =
|
|
Tween<double>(begin: widget.sliderCloseSize, end: widget.sliderOpenSize)
|
|
.animate(CurvedAnimation(
|
|
parent: _animationDrawerController!,
|
|
curve: Curves.decelerate,
|
|
reverseCurve: Curves.decelerate));
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return LayoutBuilder(builder: (context, constrain) {
|
|
return Container(
|
|
child: Stack(children: <Widget>[
|
|
/// Menu
|
|
SliderBar(
|
|
slideDirection: widget.slideDirection,
|
|
sliderMenu: widget.slider,
|
|
sliderMenuOpenSize: widget.sliderOpenSize,
|
|
),
|
|
|
|
/// Displaying the shadow
|
|
if (widget.sliderShadow != null) ...[
|
|
_Shadow(
|
|
animationDrawerController: _animationDrawerController,
|
|
slideDirection: widget.slideDirection,
|
|
sliderOpenSize: widget.sliderOpenSize,
|
|
animation: _animation,
|
|
sliderShadow: widget.sliderShadow!,
|
|
),
|
|
],
|
|
|
|
//Child
|
|
AnimatedBuilder(
|
|
animation: _animationDrawerController!,
|
|
builder: (_, child) {
|
|
return Transform.translate(
|
|
offset: Utils.getOffsetValues(
|
|
widget.slideDirection, _animation.value),
|
|
child: child,
|
|
);
|
|
},
|
|
child: GestureDetector(
|
|
behavior: HitTestBehavior.deferToChild,
|
|
onHorizontalDragStart: _onHorizontalDragStart,
|
|
onHorizontalDragEnd: _onHorizontalDragEnd,
|
|
onHorizontalDragUpdate: (detail) =>
|
|
_onHorizontalDragUpdate(detail, constrain),
|
|
child: Container(
|
|
width: double.infinity,
|
|
height: double.infinity,
|
|
color: widget.appBar?.appBarColor ?? Colors.transparent,
|
|
child: Column(
|
|
children: <Widget>[
|
|
if (widget.appBar != null)
|
|
SAppBar(
|
|
slideDirection: widget.slideDirection,
|
|
onTap: () => toggle(),
|
|
animationController: _animationDrawerController!,
|
|
drawerIcon: widget.appBar!.drawerIcon,
|
|
drawerIconColor: widget.appBar!.drawerIconColor,
|
|
drawerIconSize: widget.appBar!.drawerIconSize,
|
|
splashColor: widget.splashColor,
|
|
sliderAppBar: widget.appBar!,
|
|
),
|
|
Expanded(child: widget.child),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
]));
|
|
});
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
super.dispose();
|
|
_animationDrawerController!.dispose();
|
|
}
|
|
|
|
void _onHorizontalDragStart(DragStartDetails detail) {
|
|
if (!widget.isDraggable) return;
|
|
|
|
//Check use start dragging from left edge / right edge then enable dragging
|
|
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(() {
|
|
_isDragging = 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(() {
|
|
_isDragging = true;
|
|
});
|
|
}
|
|
}
|
|
|
|
void _onHorizontalDragEnd(DragEndDetails detail) {
|
|
if (!widget.isDraggable) return;
|
|
if (_isDragging) {
|
|
openOrClose();
|
|
setState(() {
|
|
_isDragging = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
void _onHorizontalDragUpdate(
|
|
DragUpdateDetails detail,
|
|
BoxConstraints constraints,
|
|
) {
|
|
if (!widget.isDraggable) return;
|
|
// open drawer for left/right type drawer
|
|
if (_isDragging && widget.slideDirection == SlideDirection.LEFT_TO_RIGHT ||
|
|
widget.slideDirection == SlideDirection.RIGHT_TO_LEFT) {
|
|
var globalPosition = detail.globalPosition.dx;
|
|
globalPosition = globalPosition < 0 ? 0 : globalPosition;
|
|
double position = globalPosition / constraints.maxWidth;
|
|
var realPosition = widget.slideDirection == SlideDirection.LEFT_TO_RIGHT
|
|
? position
|
|
: (1 - position);
|
|
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);
|
|
}*/
|
|
|
|
// close drawer for left/right type drawer
|
|
if (isDrawerOpen &&
|
|
(widget.slideDirection == SlideDirection.LEFT_TO_RIGHT ||
|
|
widget.slideDirection == SlideDirection.RIGHT_TO_LEFT) &&
|
|
detail.delta.dx < 15) {
|
|
closeSlider();
|
|
}
|
|
}
|
|
|
|
move(double percent) {
|
|
_percent = percent;
|
|
_animationDrawerController!.value = percent;
|
|
_animationDrawerController!.notifyListeners();
|
|
}
|
|
|
|
openOrClose() {
|
|
if (_percent > 0.3) {
|
|
openSlider();
|
|
} else {
|
|
closeSlider();
|
|
}
|
|
}
|
|
}
|
|
|
|
class _Shadow extends StatelessWidget {
|
|
const _Shadow({
|
|
Key? key,
|
|
required AnimationController? animationDrawerController,
|
|
required this.animation,
|
|
required this.sliderShadow,
|
|
required this.slideDirection,
|
|
required this.sliderOpenSize,
|
|
}) : _animationDrawerController = animationDrawerController,
|
|
super(key: key);
|
|
|
|
final AnimationController? _animationDrawerController;
|
|
final Animation animation;
|
|
final SliderShadow sliderShadow;
|
|
final SlideDirection slideDirection;
|
|
final double sliderOpenSize;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AnimatedBuilder(
|
|
animation: _animationDrawerController!,
|
|
builder: (_, child) {
|
|
return Transform.translate(
|
|
offset: Utils.getOffsetValueForShadow(
|
|
slideDirection, animation.value, sliderOpenSize),
|
|
child: child,
|
|
);
|
|
},
|
|
child: Container(
|
|
width: double.infinity,
|
|
height: double.infinity,
|
|
decoration: BoxDecoration(shape: BoxShape.rectangle, boxShadow: [
|
|
BoxShadow(
|
|
color: sliderShadow.shadowColor,
|
|
blurRadius: sliderShadow.shadowBlurRadius,
|
|
// soften the shadow
|
|
spreadRadius: sliderShadow.shadowSpreadRadius,
|
|
//extend the shadow
|
|
offset: Offset(
|
|
15.0, // Move to right 15 horizontally
|
|
15.0, // Move to bottom 15 Vertically
|
|
),
|
|
)
|
|
]),
|
|
),
|
|
);
|
|
}
|
|
}
|