flutter reset only custom widget state after it's optional function execution - flutter

I have a page where we have some pickup session when you select a pickup session at the bottom SwipeActionButton widget activate
now user can swipe right side and after swipe complete an async function execute which most time hit an api so if api result is success app goes to next page no problem here but if api result gave an error it shows a dialog
Press Ok and dialog pop but SwipeActionButton widget still show complete swipe how I can reset it.
code
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: NormalAppBar(
title: Text("Assign Requests"),
),
body: Consumer<PickupSessionProvider>(
builder: (context, provider, child) => Stack(
children: [
widget.requestIds.length == 0
? _requestsLoaded
? provider.unassignedRequestCount == 0
? Center(
child: Text("No requests.",
style: Theme.of(context).textTheme.headline6),
)
: _buildRequestsList(provider.unassignedRequests!)
: Center(
child: CircularProgressIndicator(),
)
: provider.pickupSessions!.length == 0
? Center(
child: Text("No active pickup session.",
style: Theme.of(context).textTheme.headline6),
)
: ListView(
padding: const EdgeInsets.only(bottom: 80),
children: [
Padding(
padding: const EdgeInsets.all(20),
child: Text(
"Select pickup session",
style: Theme.of(context).textTheme.headline4,
),
),
for (var pickupSession in provider.pickupSessions!)
_buildPickupSessionTile(pickupSession)
],
),
Positioned(
bottom: 0,
child: SwipeActionButton(
margin: EdgeInsets.symmetric(horizontal: 15, vertical: 20),
onDone: (_selectRequestList && requestIds.length == 0) ||
(!_selectRequestList &&
_selectedPickupSessionId == null)
? null
: () async {
var result = await showDialog(
context: context,
builder: (context) => _ProgressDialog(
requestIds: requestIds,
pickupSessionId: _selectedPickupSessionId),
barrierDismissible: true);
if (result == true) Navigator.of(context).pop(true);
},
doneText: "Assign request",
disabledText: "Assign request",
infoText: "Swipe to assign request",
),
)
],
),
),
);
}
Custom SwipeActionButton widget
class SwipeActionButton extends StatefulWidget {
final double height;
final Color doneColor;
final Color swiperColor;
final Color textColor;
final String doneText;
final String disabledText;
final VoidCallback? onDone;
final String? infoText;
final double? width;
final Color? backgroundColor;
final EdgeInsetsGeometry margin;
SwipeActionButton({
Key? key,
this.height = 50,
this.doneColor = const Color(0xff44b31f),
this.swiperColor = const Color(0xff44b31f),
this.textColor = const Color(0xff44b31f),
required this.doneText,
required this.disabledText,
this.onDone,
this.infoText,
this.width,
this.backgroundColor,
this.margin = EdgeInsets.zero
}) : super(key: key);
#override
_SwipeActionButtonState createState() => _SwipeActionButtonState();
}
class _SwipeActionButtonState extends State<SwipeActionButton>
with SingleTickerProviderStateMixin {
double swipePercent = 0.0;
bool swipeDone = false;
bool isDisabled = false;
late Color backgroundColor;
late AnimationController _controller;
Animation<double>? _animation;
void initState() {
super.initState();
backgroundColor = widget.backgroundColor ?? Color(0xff3344b31f);
_controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 500))
..addListener(() {
setState(() {
swipePercent = _animation?.value ??0;
});
})
..addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed && swipeDone) {
widget.onDone!();
}
});
}
void dispose() {
_controller.dispose();
super.dispose();
}
_onDragStart(DragStartDetails details) {
_controller.reset();
swipePercent = 0.0;
}
_onDragUpdate(DragUpdateDetails details) {
setState(() {
swipePercent =
details.globalPosition.dx / MediaQuery.of(context).size.width;
if (swipePercent > 0.90) swipeDone = true;
});
}
_onDragEnd(DragEndDetails details) {
if (swipePercent > 0.90 || swipeDone) {
_animation =
Tween<double>(begin: swipePercent, end: 1).animate(_controller);
} else {
_animation =
Tween<double>(end: 0, begin: swipePercent).animate(_controller);
}
_controller.forward();
}
#override
Widget build(BuildContext context) {
isDisabled = widget.onDone == null;
double screenWidth = MediaQuery.of(context).size.width;
return Container(
alignment: Alignment.center,
margin: widget.margin,
width: screenWidth - widget.margin.horizontal,
child: Stack(
clipBehavior: Clip.hardEdge,
children: <Widget>[
Container(
height: widget.height,
decoration: BoxDecoration(
color: isDisabled ? Colors.grey : Color(0xff3344b31f),
borderRadius: BorderRadius.all(Radius.circular(100.0)),
border: Border.all(
color: isDisabled ? Colors.grey : Color(0xff3344b31f),
width: 1.5,
),
),
child: Center(
child: Text(widget.infoText ?? "",
style: Theme.of(context)
.textTheme
.subtitle1!
.copyWith(color: widget.textColor))),
),
Container(
width: isDisabled
? screenWidth
: lerpDouble(widget.height, screenWidth, swipePercent),
height: widget.height,
child: Center(
child: Opacity(
opacity: isDisabled ? 1 : lerpDouble(0, 1, swipePercent)!,
child: Text(
isDisabled ? widget.disabledText : widget.doneText,
style: Theme.of(context).textTheme.subtitle1!.copyWith(
color: Colors.white,
),
textScaleFactor:
isDisabled ? 1 : lerpDouble(0, 1, swipePercent),
))),
decoration: BoxDecoration(
color: isDisabled ? Colors.grey : null,
borderRadius: BorderRadius.all(Radius.circular(100.0)),
/* border: Border.all(
color: Theme.of(context).primaryColor,
width: 1.5,
), */
gradient: isDisabled
? null
: LinearGradient(
begin: Alignment.center,
end: Alignment.centerRight,
colors: [
widget.doneColor,
swipeDone ? widget.doneColor : backgroundColor
])),
),
isDisabled
? Container()
: Positioned(
left: lerpDouble(
0, screenWidth -(15 +widget.margin.horizontal) - (widget.height * .9), swipePercent),
/* top: widget.height * .1,
bottom: widget.height * .1,
*/
child: AbsorbPointer(
absorbing: swipeDone,
child: GestureDetector(
onHorizontalDragStart: _onDragStart,
onHorizontalDragUpdate: _onDragUpdate,
onHorizontalDragEnd: _onDragEnd,
child: Opacity(
opacity: 1,
child: AnimatedContainer(
duration: Duration(milliseconds: 500),
height: widget.height,
width: widget.height,
decoration: BoxDecoration(
borderRadius:
BorderRadius.all(Radius.circular(100.0)),
border: Border.all(
color: Theme.of(context).primaryColor,
width: 1.5,
),
boxShadow: swipeDone
? null
: [
BoxShadow(
color: Colors.black45,
blurRadius: 4)
],
color: swipeDone
? backgroundColor
: widget.swiperColor),
child: swipeDone
? Icon(
Icons.check,
size: 20,
color: Colors.white,
)
: Icon(
Icons.arrow_forward,
size: 20,
color: Colors.white,
),
))))),
],
),
);
}
}

You're asking how you can make the swipeButton reset in case the request doesn't return with a valid value.
The swipeButton's state is defined by its swipeDone and swipePercent variables. To achieve what you want you need to pass swipeDone as a parameter when constructing the widget.
class SwipeActionButton extends StatefulWidget {
// Make swipeDone a class variable for the widget
bool swipeDone;
final double height;
final Color doneColor;
final Color swiperColor;
final Color textColor;
final String doneText;
final String disabledText;
final VoidCallback? onDone;
final String? infoText;
final double? width;
final Color? backgroundColor;
final EdgeInsetsGeometry margin;
SwipeActionButton({
// Add it to the constructor
required this.swipeDone,
Key? key,
this.height = 50,
this.doneColor = const Color(0xff44b31f),
this.swiperColor = const Color(0xff44b31f),
this.textColor = const Color(0xff44b31f),
required this.doneText,
required this.disabledText,
this.onDone,
this.infoText,
this.width,
this.backgroundColor,
this.margin = EdgeInsets.zero,
}) : super(key: key);
#override
_SwipeActionButtonState createState() => _SwipeActionButtonState();
}
In _SwipeActionButtonState, delete bool swipeDone = false; and replace every swipeDone by widget.swipeDone.
You also need to reset the value of swipePercent.
You can do this by adding at the beginning of the swipeButton's build method :
if (widget.swipeDone == false && swipePercent > 0.9) swipePercent = 0;
Now you can declare the variable swipeDone in the parent widget state, pass it as a parameter and modify it whenever needed. For more clarity I give you an example with a simple widget that reset the swipe button when the floating action button is pressed.
Complete code :
import 'package:flutter/material.dart';
import 'dart:ui';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool swipeDone = false;
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(),
body: SwipeActionButton(
disabledText: 'disabled',
doneText: 'doneText',
onDone: () {},
swipeDone: swipeDone,
),
floatingActionButton: FloatingActionButton(onPressed: () {
setState(() {
swipeDone = false;
});
}),
),
);
}
}
class SwipeActionButton extends StatefulWidget {
// Make swipeDone a class variable for the widget
bool swipeDone;
final double height;
final Color doneColor;
final Color swiperColor;
final Color textColor;
final String doneText;
final String disabledText;
final VoidCallback? onDone;
final String? infoText;
final double? width;
final Color? backgroundColor;
final EdgeInsetsGeometry margin;
SwipeActionButton({
// Add it to the constructor
required this.swipeDone,
Key? key,
this.height = 50,
this.doneColor = const Color(0xff44b31f),
this.swiperColor = const Color(0xff44b31f),
this.textColor = const Color(0xff44b31f),
required this.doneText,
required this.disabledText,
this.onDone,
this.infoText,
this.width,
this.backgroundColor,
this.margin = EdgeInsets.zero,
}) : super(key: key);
#override
_SwipeActionButtonState createState() => _SwipeActionButtonState();
}
class _SwipeActionButtonState extends State<SwipeActionButton> with SingleTickerProviderStateMixin {
double swipePercent = 0.0;
bool isDisabled = false;
late Color backgroundColor;
late AnimationController _controller;
Animation<double>? _animation;
#override
void initState() {
super.initState();
backgroundColor = widget.backgroundColor ?? Color(0xff3344b31f);
_controller = AnimationController(vsync: this, duration: Duration(milliseconds: 500))
..addListener(() {
setState(() {
swipePercent = _animation?.value ?? 0;
});
})
..addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed && widget.swipeDone) {
widget.onDone!();
}
});
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
_onDragStart(DragStartDetails details) {
_controller.reset();
swipePercent = 0.0;
}
_onDragUpdate(DragUpdateDetails details) {
setState(() {
swipePercent = details.globalPosition.dx / MediaQuery.of(context).size.width;
if (swipePercent > 0.90) widget.swipeDone = true;
});
}
_onDragEnd(DragEndDetails details) {
if (swipePercent > 0.90 || widget.swipeDone) {
_animation = Tween<double>(begin: swipePercent, end: 1).animate(_controller);
} else {
_animation = Tween<double>(end: 0, begin: swipePercent).animate(_controller);
}
_controller.forward();
}
#override
Widget build(BuildContext context) {
if (widget.swipeDone == false && swipePercent > 0.9) swipePercent = 0;
isDisabled = widget.onDone == null;
double screenWidth = MediaQuery.of(context).size.width;
return Container(
alignment: Alignment.center,
margin: widget.margin,
width: screenWidth - widget.margin.horizontal,
child: Stack(
clipBehavior: Clip.hardEdge,
children: <Widget>[
Container(
height: widget.height,
decoration: BoxDecoration(
color: isDisabled ? Colors.grey : Color(0xff3344b31f),
borderRadius: BorderRadius.all(Radius.circular(100.0)),
border: Border.all(
color: isDisabled ? Colors.grey : Color(0xff3344b31f),
width: 1.5,
),
),
child: Center(
child: Text(widget.infoText ?? "",
style: Theme.of(context).textTheme.subtitle1!.copyWith(color: widget.textColor))),
),
Container(
width: isDisabled ? screenWidth : lerpDouble(widget.height, screenWidth, swipePercent),
height: widget.height,
child: Center(
child: Opacity(
opacity: isDisabled ? 1 : lerpDouble(0, 1, swipePercent)!,
child: Text(
isDisabled ? widget.disabledText : widget.doneText,
style: Theme.of(context).textTheme.subtitle1!.copyWith(
color: Colors.white,
),
textScaleFactor: isDisabled ? 1 : lerpDouble(0, 1, swipePercent),
))),
decoration: BoxDecoration(
color: isDisabled ? Colors.grey : null,
borderRadius: BorderRadius.all(Radius.circular(100.0)),
/* border: Border.all(
color: Theme.of(context).primaryColor,
width: 1.5,
), */
gradient: isDisabled
? null
: LinearGradient(
begin: Alignment.center,
end: Alignment.centerRight,
colors: [widget.doneColor, widget.swipeDone ? widget.doneColor : backgroundColor])),
),
isDisabled
? Container()
: Positioned(
left:
lerpDouble(0, screenWidth - (15 + widget.margin.horizontal) - (widget.height * .9), swipePercent),
/* top: widget.height * .1,
bottom: widget.height * .1,
*/
child: AbsorbPointer(
absorbing: widget.swipeDone,
child: GestureDetector(
onHorizontalDragStart: _onDragStart,
onHorizontalDragUpdate: _onDragUpdate,
onHorizontalDragEnd: _onDragEnd,
child: Opacity(
opacity: 1,
child: AnimatedContainer(
duration: Duration(milliseconds: 500),
height: widget.height,
width: widget.height,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(100.0)),
border: Border.all(
color: Theme.of(context).primaryColor,
width: 1.5,
),
boxShadow:
widget.swipeDone ? null : [BoxShadow(color: Colors.black45, blurRadius: 4)],
color: widget.swipeDone ? backgroundColor : widget.swiperColor),
child: widget.swipeDone
? Icon(
Icons.check,
size: 20,
color: Colors.white,
)
: Icon(
Icons.arrow_forward,
size: 20,
color: Colors.white,
),
))))),
],
),
);
}
}

Related

how can i achieve these effect in design (snap effect ,change align when scrolling )

i need to design screen like this in short videovideo_link
i did this but its not very similar to the previous effect
here i create CustomScrollView with NotificationListener to achieve snap effect but is still begging
and isused TransitionAppBar to make appbar
and used SliverPersistentHeader wiche implement SliverPersistentHeaderDelegate to controll on align size and position
import 'dart:math';
import 'dart:developer' as d;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class FinalScroll extends StatefulWidget {
FinalScroll({Key? key}) : super(key: key);
#override
State<FinalScroll> createState() => _FinalScrollState();
}
class _FinalScrollState extends State<FinalScroll> {
final ScrollController _controller = ScrollController();
final double kLength = 300;
final double kLengthSnap = 225;
final Duration kDurationSnap = const Duration(milliseconds: 20);
#override
Widget build(BuildContext context) {
if (!(!_controller.hasClients || _controller.offset == 0)) {
// print('controll offset ${_controller.offset}');
}
double sizeImage = kLength;
return SafeArea(
child: Scaffold(
body: LayoutBuilder(builder: (context, s) {
return NotificationListener(
onNotification: (scrollEndNotification) {
// print('controll offset ${_controller.offset}');
if (scrollEndNotification is ScrollEndNotification) {
if (_controller.offset < 0.4 * kLength) {
print('snap from CTB');
Future.microtask(
() {
return _controller.animateTo(0.0,
duration: kDurationSnap, curve: Curves.bounceInOut);
},
);
setState(() {
sizeImage = kLength;
});
}
if (_controller.offset > kLength * 0.4 &&
_controller.offset <= kLength * .6) {
print('snap from CTT');
Future.microtask(() => _controller.animateTo(kLength * 0.6,
duration: kDurationSnap, curve: Curves.bounceInOut));
setState(() {
sizeImage = 50;
});
}
}
return false;
},
child: CustomScrollView(
controller: _controller,
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
TransitionAppBar(
extent: kLength,
sizeImage: sizeImage,
avatar: const Text("Rancho"),
title: const Text(''),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return ListTile(
title: Text("${index}a"),
);
},
childCount: 25,
),
)
],
),
);
}),
),
);
}
}
class TransitionAppBar extends StatefulWidget {
final Widget avatar;
final Widget title;
final double extent;
final double sizeImage;
const TransitionAppBar({
required this.avatar,
required this.title,
this.extent = 250,
required this.sizeImage,
Key? key,
}) : super(key: key);
#override
State<TransitionAppBar> createState() => _TransitionAppBarState();
}
class _TransitionAppBarState extends State<TransitionAppBar>
with TickerProviderStateMixin {
#override
Widget build(BuildContext context) {
return MediaQuery.removePadding(
context: context,
removeBottom: true,
child: SliverPersistentHeader(
pinned: true,
floating: true,
delegate: _TransitionAppBarDelegate(
avatar: widget.avatar,
sizeImage: widget.sizeImage,
title: widget.title,
extent: widget.extent > 200 ? widget.extent : 200,
vsync: this),
),
);
}
}
class _TransitionAppBarDelegate extends SliverPersistentHeaderDelegate {
_TransitionAppBarDelegate(
{required this.avatar,
required this.title,
required this.sizeImage,
this.extent = 250,
required this.vsync});
final double sizeImage;
final Widget avatar;
final Widget title;
final double extent;
/// =================== init ========= ////
final _avatarMarginTween =
EdgeInsetsTween(begin: EdgeInsets.zero, end: const EdgeInsets.all(16));
///from BTC
final _titleMarginTweenBTC = EdgeInsetsTween(
begin: EdgeInsets.zero, end: const EdgeInsets.only(left: 70));
final _titleAlignTweenBTC = AlignmentTween(
begin: const Alignment(-.7, .8), end: const Alignment(-1.0, 0.2));
final _avatarAlignTweenBTC =
AlignmentTween(begin: const Alignment(-1, 1), end: Alignment.centerLeft);
///from CTT
final _titleMarginTweenCTT = EdgeInsetsTween(
begin: const EdgeInsets.only(left: 70), end: EdgeInsets.zero);
final _titleAlignTweenCTT = AlignmentTween(
begin: const Alignment(-1.0, 0.2), end: const Alignment(-.2, 0));
final _avatarAlignTweenCTT = AlignmentTween(
begin: Alignment.centerLeft, end: const Alignment(-.7, 0.0));
final decorationTween = DecorationTween(
begin: const BoxDecoration(
shape: BoxShape.rectangle,
color: Colors.red,
image: DecorationImage(
fit: BoxFit.cover,
alignment: Alignment.centerLeft,
image: AssetImage('asset/dash.jpeg'))),
end: const BoxDecoration(
shape: BoxShape.circle,
color: Colors.yellow,
image: DecorationImage(
alignment: Alignment.centerLeft,
fit: BoxFit.fill,
image: AssetImage(
'asset/dash.jpeg',
),
),
));
#override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final double topPadding = MediaQuery.of(context).padding.top;
final double visibleMainHeight = maxExtent - shrinkOffset - topPadding;
final double extraToolbarHeight =
max(minExtent - extent - topPadding - (kToolbarHeight), 0.0);
final double visibleToolbarHeight =
visibleMainHeight - extent - extraToolbarHeight;
final bool isScrolledUnder =
overlapsContent || (shrinkOffset > maxExtent - minExtent);
final double toolbarOpacity =
(visibleToolbarHeight / (kToolbarHeight)).clamp(0.0, 1.0);
final progress = min(1.0, shrinkOffset / maxExtent);
//margin
final avatarMargin = _avatarMarginTween.lerp(progress);
final titleMarginBTC = _titleMarginTweenBTC.lerp(progress);
final titleMarginCTT = _titleMarginTweenCTT.lerp(shrinkOffset / 300);
//align
final avatarAlignBTC = _avatarAlignTweenBTC.lerp(progress);
final titleAlignBTC = _titleAlignTweenBTC.lerp(progress);
final avatarAlignCTT = _avatarAlignTweenCTT.lerp(shrinkOffset / extent);
final titleAlignCTT = _titleAlignTweenCTT.lerp(shrinkOffset / extent);
//decoration
final decorationT = decorationTween.lerp(progress);
// print(
// 'shrinkOffset:$shrinkOffset maxExtent: $maxExtent minExtent: $minExtent progress : $progress');
print(sizeImage);
final Widget toolbar = Stack(
fit: StackFit.passthrough,
children: <Widget>[
AnimatedContainer(
duration: const Duration(milliseconds: 100),
height: minExtent,
constraints: BoxConstraints(maxHeight: minExtent),
color: Colors.grey.shade100,
),
LayoutBuilder(builder: (context, constraint) {
var checkIfProgressThanFromValue = progress < 0.3;
var checkLessThan1 = !(progress > 0.01);
return SizedBox(
height: sizeImage,
width: sizeImage==maxExtent?MediaQuery.of(context).size.width:30,
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Flexible(
child: Padding(
padding: avatarMargin,
child: AnimatedContainer(
duration: const Duration(milliseconds: 100),
constraints: BoxConstraints(
maxHeight: maxExtent,
maxWidth: constraint.maxWidth,
minHeight: 0,
minWidth: 0),
height: checkLessThan1 ? maxExtent : 50,
width: checkLessThan1
? MediaQuery.of(context).size.width
: 30,
// decoration: decorationT,
child: OverflowBox(
maxWidth: MediaQuery.of(context).size.width,
child: SizedBox(
height: sizeImage,
width: checkLessThan1
? MediaQuery.of(context).size.width
: 30,
child: FittedBox(
fit: BoxFit.fitHeight,
alignment: shrinkOffset > minExtent
? avatarAlignCTT
: avatarAlignBTC,
child: ClipRRect(
borderRadius: checkLessThan1
? BorderRadius.zero
: BorderRadius.circular(
min(24, progress * 24)),
clipBehavior: Clip.antiAlias,
child: AnimatedSize(
duration: Duration(milliseconds: 100),
child: Image.asset(
'asset/dash.jpeg',
// alignment: shrinkOffset > minExtent
// ? avatarAlignCTT
// : avatarAlignBTC,
fit: BoxFit.fill,
height: sizeImage,
width: sizeImage == maxExtent
? MediaQuery.of(context).size.width
: 30,
// height: checkIfProgressThanFromValue
// ? maxExtent
// : 50,
// width: checkIfProgressThanFromValue
// ? MediaQuery.of(context).size.width
// : 50,
),
),
),
),
),
),
),
),
),
],
),
);
}),
_RenderTilte(
progress: progress,
titleMarginBTC: titleMarginBTC,
titleMarginCTT: titleMarginCTT,
titleAlignBTC: titleAlignBTC,
titleAlignCTT: titleAlignCTT)
],
);
final Widget appBar = FlexibleSpaceBar.createSettings(
minExtent: minExtent,
maxExtent: maxExtent,
currentExtent: max(minExtent, maxExtent - shrinkOffset),
child: toolbar,
);
return SafeArea(
child: LayoutBuilder(builder: (context, c) {
return SizedBox(
height: c.maxHeight,
width: c.maxWidth,
child: ClipRect(child: appBar));
}),
);
}
#override
double get maxExtent => extent;
#override
double get minExtent => max(kToolbarHeight, (maxExtent * .33));
#override
bool shouldRebuild(_TransitionAppBarDelegate oldDelegate) {
return true;
}
#override
OverScrollHeaderStretchConfiguration get stretchConfiguration =>
OverScrollHeaderStretchConfiguration(
stretchTriggerOffset: 150, onStretchTrigger: () async {});
#override
FloatingHeaderSnapConfiguration? get snapConfiguration =>
FloatingHeaderSnapConfiguration(
curve: Curves.bounceInOut,
duration: const Duration(milliseconds: 10));
#override
PersistentHeaderShowOnScreenConfiguration? get showOnScreenConfiguration =>
const PersistentHeaderShowOnScreenConfiguration();
#override
final TickerProvider vsync;
}
class _RenderTilte extends StatelessWidget {
const _RenderTilte({
Key? key,
required this.progress,
required this.titleMarginBTC,
required this.titleMarginCTT,
required this.titleAlignBTC,
required this.titleAlignCTT,
}) : super(key: key);
final double progress;
final EdgeInsets titleMarginBTC;
final EdgeInsets titleMarginCTT;
final Alignment titleAlignBTC;
final Alignment titleAlignCTT;
#override
Widget build(BuildContext context) {
return Padding(
padding: progress < 0.3 ? titleMarginBTC : titleMarginCTT,
child: Align(
alignment: progress < 0.3 ? titleAlignBTC : titleAlignCTT,
child: Opacity(
opacity: progress == 0 || progress == 1 ? 1 : progress * 0.01,
child: const Text(
'abd alazeez ',
),
),
),
);
}
}
here how to build the previous design and i dont know what the next step

Issues with Easy Geofencing in Flutter when i am trying to call the service more then one time

i am trying to check if the user in specific geofencing zone using the Easy geofencing package but every time i call the EasyGeofencing.startGeofenceService() and EasyGeofencing.getGeofenceStream() with new position ( pointedLatitude, pointedLongitude, radiusMeter) the service do not work and the console print "Parse value===> false" if any one know how i solve this problem please help me, i am stuck since a week.
this is my code
import 'dart:async';
import 'package:easy_geofencing/easy_geofencing.dart';
import 'package:geolocator/geolocator.dart';
import 'package:easy_geofencing/enums/geofence_status.dart';
import 'package:flutter/material.dart';
import 'package:flutter_application_1/domain/models/shop_model.dart';
import 'package:flutter_application_1/presentation/resources/assets_manager.dart';
import 'package:flutter_application_1/presentation/resources/color_manager.dart';
import 'package:flutter_application_1/presentation/resources/values_manager.dart';
import 'package:flutter_application_1/presentation/sidebars/cardWidget.dart';
import 'package:flutter_application_1/presentation/sidebars/profileSideBar.dart';
import 'package:flutter_application_1/services/shop_services.dart';
import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart';
class ShopsDownBar extends StatefulWidget with ChangeNotifier {
ShopsDownBar({Key? key}) : super(key: key);
#override
State<ShopsDownBar> createState() => _ShopsDownBarState();
}
class _ShopsDownBarState extends State<ShopsDownBar>
with SingleTickerProviderStateMixin<ShopsDownBar> {
StreamSubscription<GeofenceStatus>? geofenceStatusStream;
StreamSubscription<EasyGeofencing>? easyGeofencingStream;
Geolocator geolocator = Geolocator();
String geofenceStatus = '';
bool isReady = false;
int raduis = 25;
double? longitude;
double? latitude;
Position? position;
late StreamController<bool> isOpenStreamController;
late Stream<bool> isOpenNStream;
late StreamSink<bool> isOpenNSink;
late AnimationController _animationControler;
List<Shops> shops = [];
getCurrentPosition() async {
position = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
print("LOCATION => ${position?.toJson()}");
isReady = (position != null) ? true : false;
}
setLocation() async {
await getCurrentPosition();
print("POSITION => ${position!.toJson()}");
}
final _animationDuration = const Duration(milliseconds: 500);
setShops() {
ShopServices().fetchShops().then((value) {
setState(() {
if (value != null) {
for (int i = 0; i < value.length; i++) {
shops.add(Shops(
type: value[i].type,
shopStatus: value[i].shopStatus,
rewards: value[i].rewards,
id: value[i].id,
shopName: value[i].shopName,
shopAddress: value[i].shopAddress,
markerShop: value[i].markerShop,
createdAt: value[i].createdAt,
updatedAt: value[i].updatedAt,
));
}
}
});
});
}
void onIconPressed() {
final animationStatus = _animationControler.status;
final isAnimationDone = animationStatus == AnimationStatus.completed;
if (isAnimationDone) {
isOpenNSink.add(false);
_animationControler.reverse();
} else if (Provider.of<ProfileSideBar>(context, listen: false).isOpen ==
false) {
isOpenNSink.add(true);
_animationControler.forward();
}
}
#override
void initState() {
setShops();
getCurrentPosition();
if (isReady) {
print('jawna behi');
setState(() {
setLocation();
});
}
_animationControler =
AnimationController(vsync: this, duration: _animationDuration);
isOpenStreamController = PublishSubject<bool>();
isOpenNStream = isOpenStreamController.stream;
isOpenNSink = isOpenStreamController.sink;
super.initState();
}
#override
void dispose() {
_animationControler.dispose();
isOpenStreamController.close();
isOpenNSink.close();
EasyGeofencing.stopGeofenceService();
super.dispose();
}
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
var height = size.height;
var width = size.width;
return StreamBuilder<bool>(
initialData: false,
stream: isOpenNStream,
builder: (context, isOpenAsync) {
return AnimatedPositioned(
duration: _animationDuration,
top: isOpenAsync.data == false ? height * 0.91 : height * 0.24,
bottom: AppSize.s1_5,
right: AppSize.s1_5,
left: AppSize.s1_5,
child: Column(
children: [
Align(
child: GestureDetector(
onTap: () {
onIconPressed();
},
child: Container(
// alignment: Alignment.cen,
padding: const EdgeInsets.only(
left: AppMargin.m60,
right: AppMargin.m60,
top: AppMargin.m8,
),
child: Icon(
isOpenAsync.data == true
? Icons.close
: Icons.wallet_giftcard,
size: AppSize.s28,
color: ColorManager.primary,
),
decoration: const BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.only(
topLeft: Radius.circular(AppSize.s40),
topRight: Radius.circular(AppSize.s40)),
),
),
),
),
Expanded(
child: Container(
margin: const EdgeInsets.only(
left: AppMargin.m16, right: AppMargin.m16),
height: height / 1.4,
width: width,
decoration: BoxDecoration(
color: ColorManager.white,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(AppSize.s28),
topRight: Radius.circular(AppSize.s28)),
),
child: shops.isNotEmpty
? ListView.builder(
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(
bottom: AppPadding.p8,
top: AppPadding.p18),
child: Giftcart(
ontap: () {
// print("starting geoFencing Service");
EasyGeofencing.startGeofenceService(
pointedLatitude: shops[index]
.markerShop
.locations
.latitude
.toString(),
pointedLongitude: shops[index]
.markerShop
.locations
.longitude
.toString(),
radiusMeter: raduis.toString(),
eventPeriodInSeconds: 5);
geofenceStatusStream ??=
EasyGeofencing.getGeofenceStream()!
.listen((GeofenceStatus? status) {
print(status.toString());
setState(() {
geofenceStatus = status.toString();
});
if (status.toString() ==
'GeofenceStatus.enter') {
print("entered");
} else {
print("not entered");
}
});
},
shopName: shops[index].shopName,
details: shops[index].shopAddress,
imagePath: ImageAssets.logo1,
),
);
},
itemCount: shops.length)
: Padding(
padding: const EdgeInsets.fromLTRB(
AppPadding.p100,
AppPadding.p100,
AppPadding.p100,
AppPadding.p200),
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
ColorManager.primary),
),
)),
),
],
),
);
});
}
}
giftCart widget
import 'package:flutter/material.dart';
import 'package:flutter_application_1/presentation/resources/color_manager.dart';
import 'package:flutter_application_1/presentation/resources/values_manager.dart';
//import 'package:flutter_application_1/presentation/resources/font_manager.dart';
class Giftcart extends StatelessWidget {
final String shopName;
final String details;
final String imagePath;
final void Function() ontap;
const Giftcart({
Key? key,
required this.ontap,
required this.shopName,
required this.details,
required this.imagePath,
}) : super(key: key);
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
var height = size.height;
var width = size.width;
return Center(
child: Container(
decoration: BoxDecoration(
color: ColorManager.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: ColorManager.grey,
offset: const Offset(0, 0),
blurRadius: 10,
),
],
),
height: height * 0.1,
width: width * 0.8,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
height: height * 0.15,
width: width * 0.15,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
),
child: Image.asset(imagePath)),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: height * 0.02,
),
Text(
shopName,
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.end,
),
SizedBox(
height: height * 0.01,
),
Text(
details,
style: Theme.of(context).textTheme.bodyText1,
textAlign: TextAlign.start,
),
],
),
Padding(
padding: const EdgeInsets.only(right: 10.0),
child: SizedBox(
height: height * 0.12,
width: width * 0.12,
child: FloatingActionButton(
backgroundColor: ColorManager.primary,
onPressed: ontap,
child: Icon(
Icons.card_giftcard,
size: AppSize.s18,
color: ColorManager.white,
)),
),
),
],
),
),
);
}
}
the Screen
the button on the left is for checking the geofenceStatus
this is the package doc
Add "geofenceStatusStream = null;" after stopGeofenceService as below:
setState(() {
EasyGeofencing.stopGeofenceService();
geofenceStatusStream!.cancel();
geofenceStatusStream = null;
});

flutter web inkwell onhover causing exception

When I hover the icon button and then hover to another position always in the InkWell region, I get this exception:
Error: Assertion failed:
../…/animation/animation_controller.dart:487
_ticker != null
"AnimationController.reverse() called after AnimationController.dispose()\nAnimationController methods should not be used after calling dispose."
at Object.throw_ [as throw] (http://localhost:38805/dart_sdk.js:5063:11)
at Object.assertFailed (http://localhost:38805/dart_sdk.js:4988:15)
at animation_controller.AnimationController.new.reverse (http://localhost:38805/packages/flutter/src/animation/animation_controller.dart.lib.js:305:42)
at internalCallback (http://localhost:38805/dart_sdk.js:26215:11)
This is the code:
return Material(
child: InkWell(
onTap: widget.onTap,
onHover: (bool isHoverIn) {
print("isHoverIN: $isHoverIn iscancel: $isCancelButtonVisible");
// (isHoverIn) {
if (isHoverIn != isCancelButtonVisible)
setState(() {
isCancelButtonVisible = isHoverIn;
});
},
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Container(
height: boxSize,
width: boxSize,
decoration: BoxDecoration(
image: DecorationImage(
image: widget.imageData.image!.image,
fit: BoxFit.cover,
),
border: Border.all(
color: Colors.grey,
width: 5,
),
borderRadius: BorderRadius.circular(12),
),
child: isCancelButtonVisible
? Align(
alignment: Alignment.topRight,
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.cancel),
tooltip: 'rimuovi',
onPressed: () => widget.onRemove(widget.position),
),
)
: null,
)),
),
);
Printing the hover actions from the IconButton and out the IconButton in the InkWell region:
isHoverIN: true iscancel: false
isHoverIN: false iscancel: true
isHoverIN: true iscancel: false
This is the full widget:
class _ProductMediaViewer extends StatefulWidget {
final int position;
final ProductVariantImage imageData;
final void Function(int, int) swap;
final void Function(int) onRemove;
final void Function() onTap;
final double boxSize;
const _ProductMediaViewer({
Key? key,
required this.position,
required this.imageData,
required this.swap,
required this.onRemove,
required this.onTap,
required this.boxSize,
}) : super(key: key);
#override
_ProductMediaViewerState createState() => _ProductMediaViewerState();
}
class _ProductMediaViewerState extends State<_ProductMediaViewer> {
bool isCancelButtonVisible = false;
Widget getMediaContentBox(bool isTransparent) {
final boxSize =
widget.position == -1 ? widget.boxSize * 2.5 : widget.boxSize;
return Material(
color: Colors.transparent,
child: Opacity(
opacity: isTransparent ? 0.45 : 1.0,
child: InkWell(
// TODO
onTap: widget.onTap,
onHover: (bool isHoverIn) {
print("isHoverIN: $isHoverIn iscancel: $isCancelButtonVisible");
// (isHoverIn) {
if (isHoverIn != isCancelButtonVisible)
setState(() {
isCancelButtonVisible = isHoverIn;
});
},
child: ClipRRect(
borderRadius: BorderRadius.circular(10.0),
child: Container(
height: boxSize,
width: boxSize,
decoration: BoxDecoration(
image: DecorationImage(
image: widget.imageData.image!.image,
fit: BoxFit.cover,
),
border: Border.all(
color: Colors.grey,
width: 5,
),
borderRadius: BorderRadius.circular(12),
),
child: isCancelButtonVisible
? Align(
alignment: Alignment.topRight,
child: IconButton(
iconSize: 20,
icon: const Icon(Icons.cancel),
tooltip: 'rimuovi',
onPressed: () => widget.onRemove(widget.position),
),
)
: null,
)),
),
),
);
}
#override
Widget build(BuildContext context) {
final boxSize =
widget.position == -1 ? widget.boxSize * 2.5 : widget.boxSize;
return Draggable<int>(
onDragCompleted: () => isCancelButtonVisible = false,
maxSimultaneousDrags: 1,
data: widget.position,
child: DragTarget<int>(
builder: (
BuildContext context,
List<dynamic> accepted,
List<dynamic> rejected,
) {
return getMediaContentBox(false);
},
onAccept: (int draggablePosition) {
widget.swap(draggablePosition, widget.position);
print("draggpos: $draggablePosition");
print("pos: ${widget.position}");
},
),
feedback: getMediaContentBox(true),
childWhenDragging: Container(
height: boxSize,
width: boxSize,
color: Colors.grey[200],
),
);
}
}
You can use this widget
class HoverWidget extends StatefulWidget {
final Widget child;
final Widget hoverChild;
final Function(PointerEnterEvent event) onHover;
const HoverWidget(
{Key key,
#required this.child,
#required this.hoverChild,
#required this.onHover})
: assert(child != null && hoverChild != null && onHover != null),
super(key: key);
#override
_HoverWidgetState createState() => _HoverWidgetState();
}
class _HoverWidgetState extends State<HoverWidget> {
bool _isHover = false;
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (event) {
setState(() {
_isHover = true;
});
widget.onHover(event);
},
onExit: (event) {
setState(() {
_isHover = false;
});
},
child: _isHover ? widget.hoverChild : widget.child,
);
}
}

How to create swipe button left and right?

How to create swipe button left and right ?
i have try create Swipe button to confirmation (swipe left to right)
my code:
class SwipeOnOffButton extends StatefulWidget {
final String timer;
final double timerSize;
final TextStyle timerStyle;
final double height;
final double width;
final Color backgroundColor;
final Color disableBackgroundColor;
final Color replaceBackgroundColor;
final Color foregroundColor;
final Color iconColor;
final IconData leftIcon;
final IconData rightIcon;
final double iconSize;
final String text;
final TextStyle textStyle;
final VoidCallback onConfirmation;
final BorderRadius foregroundRadius;
final BorderRadius backgroundRadius;
final bool disable;
const SwipeOnOffButton({
Key key,
this.timer,
this.timerSize,
this.timerStyle,
this.height = 48.0,
this.width = 300,
this.backgroundColor,
this.disableBackgroundColor,
this.foregroundColor,
this.iconColor,
this.leftIcon,
this.text,
this.textStyle,
#required this.onConfirmation,
this.foregroundRadius,
this.backgroundRadius,
this.rightIcon,
this.iconSize,
this.replaceBackgroundColor,
this.disable = false,
});
#override
State<StatefulWidget> createState() {
return SwipeOnOffButtonState();
}
}
class SwipeOnOffButtonState extends State<SwipeOnOffButton> {
double _position = 0;
int _duration = 0;
bool _isSwipe = false;
double getPosition() {
if (widget.disable) _position = 0;
if (_position < 0) {
return 0;
} else if (_position > widget.width - widget.height) {
return widget.width - widget.height;
} else {
return _position;
}
}
Color getColor() {
if (!widget.disable) {
if (_position > 0) {
return widget.replaceBackgroundColor ?? Colors.green;
} else {
return widget.backgroundColor ?? Colors.orange;
}
} else {
return widget.disableBackgroundColor ?? Colors.black;
}
}
void updatePosition(details) {
if (!widget.disable) {
if (details is DragEndDetails) {
setState(() {
_duration = 100;
_position = _isSwipe ? widget.width : 0;
});
} else if (details is DragUpdateDetails) {
setState(() {
_duration = 0;
_position = _isSwipe
? _position
: details.localPosition.dx - (widget.height / 2);
});
}
} else {
_position = 0;
}
}
void swipeReleased(details) {
if (_position > widget.width - widget.height) {
widget.onConfirmation();
_isSwipe = true;
}
updatePosition(details);
}
#override
Widget build(BuildContext context) {
return Container(
height: widget.height,
width: widget.width,
decoration: BoxDecoration(
borderRadius: widget.backgroundRadius ??
BorderRadius.all(Radius.circular(widget.height)),
color: !widget.disable
? widget.backgroundColor ?? Colors.orange
: Colors.black,
),
child: Stack(
children: <Widget>[
AnimatedContainer(
height: widget.height,
width: getPosition() + widget.height,
duration: Duration(milliseconds: _duration),
curve: Curves.bounceOut,
decoration: BoxDecoration(
borderRadius: widget.backgroundRadius ??
BorderRadius.all(Radius.circular(widget.height)),
color: getColor()),
),
Center(
child: Text(
widget.text ?? "Start",
style: widget.textStyle ??
ThemeData.textTheme.buttonText1
.copyWith(color: Colors.white),
),
),
AnimatedPositioned(
duration: Duration(milliseconds: _duration),
curve: Curves.bounceOut,
left: getPosition(),
child: GestureDetector(
onPanUpdate: (details) => updatePosition(details),
onPanEnd: (details) => swipeReleased(details),
child: !_isSwipe
? Container(
height: widget.height,
width: widget.height,
decoration: BoxDecoration(
borderRadius: widget.foregroundRadius ??
BorderRadius.all(
Radius.circular(widget.height / 2),
),
color: widget.foregroundColor ??
Colors.white.withOpacity(0.24),
),
child: Icon(
widget.leftIcon ?? Icons.arrow_circle_up_rounded,
color: widget.iconColor ?? Colors.white,
size: widget.iconSize ?? 20.0,
),
)
: Container(
height: widget.height,
width: widget.height,
decoration: BoxDecoration(
borderRadius: widget.foregroundRadius ??
BorderRadius.all(
Radius.circular(widget.height / 2),
),
color: widget.foregroundColor ??
Colors.white.withOpacity(0.24),
),
child: Icon(
widget.rightIcon ?? Icons.check_circle_rounded,
color: widget.iconColor ?? Colors.white,
size: widget.iconSize ?? 20.0,
),
),
),
),
],
),
);
}
}
but i need swipe button have value bool (swipe to right=true and swipe to left=false)
so, i try to improve my swipe button. but, i can't find solution
could you help me to fix this problem?
Use lite_rolling_switch package
LiteRollingSwitch(
value: true,
textOn: 'active',
textOff: 'inactive',
colorOn: Colors.deepOrange,
colorOff: Colors.blueGrey,
iconOn: Icons.lightbulb_outline,
iconOff: Icons.power_settings_new,
onChanged: (bool state) {
print('turned ${(state) ? 'on' : 'off'}');
},
),

Buttons' shaders are cutted off using a flutter plug-in

I created some buttons for my flutter app menu and then I tried to give them some clicky animations.
I found a plug-in that was meant for me, so I edited the code from it but I got some problems.
As you can see from the images below, the buttons' shadows are cutted off, and I don't know how to fix them.
(I just need to know how to solve this little issue)
That’s the plug-in I edited:
import 'package:flutter/material.dart';
class AnimatedButton extends StatefulWidget {
final GestureTapCallback onPressed;
final Widget child;
final bool enabled;
final double padding;
final bool isCircular;
final Color color;
final Color shadowColor;
final double height;
final double width;
final ShadowDegree shadowDegree;
final int duration;
const AnimatedButton({
Key key,
#required this.onPressed,
#required this.child,
#required this.isCircular,
this.padding = 0.0,
this.enabled = true,
this.color = Colors.blue,
this.shadowColor = Colors.blueAccent,
this.height = 64,
this.shadowDegree = ShadowDegree.light,
this.width = 200,
this.duration = 70,
}) : assert(child != null),
super(key: key);
#override
_AnimatedButtonState createState() => _AnimatedButtonState();
}
class _AnimatedButtonState extends State<AnimatedButton> {
static const Curve _curve = Curves.easeIn;
static const double _shadowHeight = 4;
double _position = 4;
#override
Widget build(BuildContext context) {
final double _height = widget.height - _shadowHeight;
return GestureDetector(
// width here is required for centering the button in parent
child: Container(
width: widget.width,
height: _height + _shadowHeight,
child: Stack(
children: <Widget>[
// Shadow
Positioned(
bottom: -8,
child: Container(
height: _height,
width: widget.width,
decoration: BoxDecoration(
color: widget.enabled
? widget.shadowColor
: darken(Colors.grey, widget.shadowDegree),
borderRadius: BorderRadius.all(
widget.isCircular ? Radius.circular(37) : Radius.circular(30)
)
)
)
),
// Button
AnimatedPositioned(
curve: _curve,
duration: Duration(milliseconds: widget.duration),
bottom: _position,
child: Container(
height: _height,
width: widget.width,
decoration: BoxDecoration(
color: widget.enabled ? widget.color : Colors.grey,
borderRadius: BorderRadius.all(
widget.isCircular ? Radius.circular(37) : Radius.circular(27)
)
),
child: Padding(
padding: EdgeInsets.only(top: widget.padding),
child: Center(
child: widget.child
),
)
)
)
]
)
),
onTapDown: widget.enabled ? _pressed : null,
onTapUp: widget.enabled ? _unPressedOnTapUp : null,
onTapCancel: widget.enabled ? _unPressed : null
);
}
void _pressed(_) {
setState(() {
_position = -4;
});
}
void _unPressedOnTapUp(_) => _unPressed();
// top
void _unPressed() {
setState(() {
_position = 4;
});
widget.onPressed();
}
}
Color darken(Color color, ShadowDegree degree) {
double amount = degree == ShadowDegree.dark ? 0.5 : 0.12;
final hsl = HSLColor.fromColor(color);
final hslDark = hsl.withLightness((hsl.lightness + amount).clamp(0.0, 1.0));
return hslDark.toColor();
}
enum ShadowDegree { light, dark }
And that is how a button is made:
AnimatedButton(
color: Color(0xff4388fc),
child: Text(
"LOCAL",
style: TextStyles().textStyleMenuButtonText()
),
height: 80,
width: 250,
shadowColor: Color(0xff2963d4),
isCircular: false,
padding: 6.0,
onPressed: () {
ModeSelectorDialog().getDialog(
context,
MediaQuery.of(context).size.height,
MediaQuery.of(context).size.width
);
}
),
images
I got 5 buttons, but their shadows are cutted off, and I don't know why
That's how I want them to be
Overwrite the default constructor of a Stack by setting the overflow
overflow: Overflow.visible
Your build method of the animated button should look like this:
Widget build(BuildContext context) {
final double _height = widget.height - _shadowHeight;
return GestureDetector(
// width here is required for centering the button in parent
child: Container(
width: widget.width,
height: _height + _shadowHeight,
child: Stack(overflow: Overflow.visible, children: <Widget>[
// Shadow
Positioned(
bottom: -8,
child: Container(
height: _height,
width: widget.width,
decoration: BoxDecoration(
color: widget.enabled
? widget.shadowColor
: darken(Colors.grey, widget.shadowDegree),
borderRadius: BorderRadius.all(widget.isCircular
? Radius.circular(37)
: Radius.circular(30))))),
// Button
AnimatedPositioned(
curve: _curve,
duration: Duration(milliseconds: widget.duration),
bottom: _position,
child: Container(
height: _height,
width: widget.width,
decoration: BoxDecoration(
color: widget.enabled ? widget.color : Colors.grey,
borderRadius: BorderRadius.all(widget.isCircular
? Radius.circular(37)
: Radius.circular(27))),
child: Padding(
padding: EdgeInsets.only(top: widget.padding),
child: Center(child: widget.child),
)))
])),
onTapDown: widget.enabled ? _pressed : null,
onTapUp: widget.enabled ? _unPressedOnTapUp : null,
onTapCancel: widget.enabled ? _unPressed : null);
}