swipe history added
parent
f6e2ef676c
commit
c5f863b05c
@ -0,0 +1,164 @@
|
|||||||
|
import 'package:flutter/animation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:test_sa/extensions/int_extensions.dart';
|
||||||
|
import 'package:test_sa/new_views/app_style/app_color.dart';
|
||||||
|
|
||||||
|
class CircularAnimationWithProgressIndicator extends StatefulWidget {
|
||||||
|
Widget child;
|
||||||
|
CircularAnimationWithProgressIndicator({Key key, this.child}) : super(key: key);
|
||||||
|
@override
|
||||||
|
_CircularAnimationWithProgressIndicatorState createState() =>
|
||||||
|
_CircularAnimationWithProgressIndicatorState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CircularAnimationWithProgressIndicatorState
|
||||||
|
extends State<CircularAnimationWithProgressIndicator>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
AnimationController _controller;
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
// Animation controller for progress indicator
|
||||||
|
_controller = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(seconds: 40), // Duration of one full rotation
|
||||||
|
)..repeat(); // Repeat animation continuously
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
// Animated CircularProgressIndicator
|
||||||
|
AnimatedBuilder(
|
||||||
|
animation: _controller,
|
||||||
|
builder: (context, child) {
|
||||||
|
return Transform.rotate(
|
||||||
|
angle: _controller.value * 2 * 3.1416, // Full rotation
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: SizedBox(
|
||||||
|
width: 100.toScreenHeight,
|
||||||
|
height: 100.toScreenWidth,
|
||||||
|
child: const CircularProgressIndicator(
|
||||||
|
strokeWidth: 3.0,
|
||||||
|
backgroundColor: AppColor.primary30,
|
||||||
|
|
||||||
|
value: null, // Infinite animation
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Static container in the center
|
||||||
|
widget.child?? const SizedBox(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CircularAnimatedContainer extends StatefulWidget {
|
||||||
|
Widget child;
|
||||||
|
|
||||||
|
CircularAnimatedContainer({Key key, @required this.child}) : super(key: key);
|
||||||
|
@override
|
||||||
|
_CircularAnimatedContainerState createState() => _CircularAnimatedContainerState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _CircularAnimatedContainerState extends State<CircularAnimatedContainer>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
AnimationController _controller;
|
||||||
|
Animation<double> _animation;
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = AnimationController(
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
vsync: this,
|
||||||
|
)..repeat();
|
||||||
|
|
||||||
|
_animation = CurvedAnimation(
|
||||||
|
parent: _controller,
|
||||||
|
curve: Curves.easeInOut, // Applies the ease-in-out effect
|
||||||
|
);
|
||||||
|
// Repeats animation
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Center(
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
widget.child,
|
||||||
|
AnimatedBuilder(
|
||||||
|
animation: _animation,
|
||||||
|
builder: (context, child) {
|
||||||
|
return CustomPaint(
|
||||||
|
painter: CircularProgressPainter(
|
||||||
|
progress: _animation.value),
|
||||||
|
size: Size(100.toScreenHeight, 100.toScreenWidth),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CircularProgressPainter extends CustomPainter {
|
||||||
|
final double progress;
|
||||||
|
|
||||||
|
CircularProgressPainter({@required this.progress});
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
|
||||||
|
|
||||||
|
final Paint paint = Paint()
|
||||||
|
..color = AppColor.primary80
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 3
|
||||||
|
..strokeCap = StrokeCap.round;
|
||||||
|
|
||||||
|
final center = Offset(size.width / 2, size.height / 2);
|
||||||
|
final radius = size.width / 2.05;
|
||||||
|
final double startAngle = 2.5 * 3.141592653589793 * progress;
|
||||||
|
final double sweepAngle = 2 * 3.141592653589793 * progress;
|
||||||
|
// const double startAngle = -90 * (3.141592653589793 / 180);
|
||||||
|
// final double sweepAngle = 2.05 * 3.141592653589793 * progress;
|
||||||
|
|
||||||
|
canvas.drawArc(
|
||||||
|
Rect.fromCircle(center: center, radius: radius),
|
||||||
|
startAngle,
|
||||||
|
sweepAngle,
|
||||||
|
false,
|
||||||
|
paint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(covariant CustomPainter oldDelegate) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
class SwipeHistory {
|
||||||
|
final int id;
|
||||||
|
final String swipeTypeName;
|
||||||
|
final String userName;
|
||||||
|
final String siteName;
|
||||||
|
final String pointName;
|
||||||
|
final String swipeTime;
|
||||||
|
final bool isSuccess;
|
||||||
|
final String errorMessage;
|
||||||
|
|
||||||
|
SwipeHistory({
|
||||||
|
this.id,
|
||||||
|
this.swipeTypeName,
|
||||||
|
this.userName,
|
||||||
|
this.siteName,
|
||||||
|
this.pointName,
|
||||||
|
this.swipeTime,
|
||||||
|
this.isSuccess,
|
||||||
|
this.errorMessage,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory SwipeHistory.fromJson(Map<String, dynamic> json) {
|
||||||
|
return SwipeHistory(
|
||||||
|
id: json['id'],
|
||||||
|
swipeTypeName: json['swipeTypeName'],
|
||||||
|
userName: json['userName'],
|
||||||
|
siteName: json['siteName'],
|
||||||
|
pointName: json['pointName'],
|
||||||
|
swipeTime: json['swipeTime']!=null? DateTime.parse(json['swipeTime']).toIso8601String():'',
|
||||||
|
isSuccess: json['isSuccess'],
|
||||||
|
errorMessage: json['errorMessage'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return {
|
||||||
|
'id': id,
|
||||||
|
'swipeTypeName': swipeTypeName,
|
||||||
|
'userName': userName,
|
||||||
|
'siteName': siteName,
|
||||||
|
'pointName': pointName,
|
||||||
|
'swipeTime': swipeTime,
|
||||||
|
'isSuccess': isSuccess,
|
||||||
|
'errorMessage': errorMessage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,189 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:test_sa/controllers/providers/api/user_provider.dart';
|
||||||
|
import 'package:test_sa/extensions/context_extension.dart';
|
||||||
|
import 'package:test_sa/extensions/int_extensions.dart';
|
||||||
|
import 'package:test_sa/extensions/string_extensions.dart';
|
||||||
|
import 'package:test_sa/extensions/text_extensions.dart';
|
||||||
|
import 'package:test_sa/extensions/widget_extensions.dart';
|
||||||
|
import 'package:test_sa/new_views/app_style/app_color.dart';
|
||||||
|
import 'package:test_sa/new_views/common_widgets/app_filled_button.dart';
|
||||||
|
import 'package:test_sa/new_views/common_widgets/default_app_bar.dart';
|
||||||
|
import 'package:test_sa/views/widgets/date_and_time/date_picker.dart';
|
||||||
|
import 'package:test_sa/views/widgets/loaders/lazy_loading.dart';
|
||||||
|
import 'package:test_sa/views/widgets/loaders/no_item_found.dart';
|
||||||
|
|
||||||
|
|
||||||
|
import 'models/swipe_transaction_history.dart';
|
||||||
|
|
||||||
|
class SwipeHistoryView extends StatefulWidget {
|
||||||
|
static const routeName = '/swipe_list_view';
|
||||||
|
|
||||||
|
const SwipeHistoryView({Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SwipeHistoryView> createState() => _SwipeHistoryViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class _SwipeHistoryViewState extends State<SwipeHistoryView> {
|
||||||
|
DateTime dateFrom = DateTime.now();
|
||||||
|
DateTime dateTo = DateTime.now();
|
||||||
|
UserProvider _userProvider;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
getSwipeHistory();
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void getSwipeHistory () {
|
||||||
|
_userProvider = Provider.of<UserProvider>(context,listen:false);
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
await _userProvider.getSwipeTransactionHistory(userId: _userProvider.user.userID,dateFrom: dateFrom,dateTo: dateTo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: const DefaultAppBar(title: 'Swipe History',),
|
||||||
|
body: Column(
|
||||||
|
crossAxisAlignment:CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
|
||||||
|
8.height,
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
ADatePicker(
|
||||||
|
label: context.translation.from,
|
||||||
|
date: dateFrom,
|
||||||
|
from: DateTime(DateTime.now().year - 5, DateTime.now().month, DateTime.now().day),
|
||||||
|
formatDateWithTime: true,
|
||||||
|
onDatePicker: (selectedDate) {
|
||||||
|
if (selectedDate != null) {
|
||||||
|
showTimePicker(
|
||||||
|
context: context,
|
||||||
|
initialTime: TimeOfDay.now(),
|
||||||
|
).then((selectedTime) {
|
||||||
|
// Handle the selected date and time here.
|
||||||
|
if (selectedTime != null) {
|
||||||
|
DateTime selectedDateTime = DateTime(
|
||||||
|
selectedDate.year,
|
||||||
|
selectedDate.month,
|
||||||
|
selectedDate.day,
|
||||||
|
selectedTime.hour,
|
||||||
|
selectedTime.minute,
|
||||||
|
);
|
||||||
|
if (selectedDateTime != null) {
|
||||||
|
setState(() {
|
||||||
|
dateFrom = selectedDateTime;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
).expanded,
|
||||||
|
8.width,
|
||||||
|
ADatePicker(
|
||||||
|
label: context.translation.to,
|
||||||
|
date: dateTo,
|
||||||
|
from: DateTime(DateTime.now().year - 5, DateTime.now().month, DateTime.now().day),
|
||||||
|
formatDateWithTime: true,
|
||||||
|
onDatePicker: (selectedDate) {
|
||||||
|
if (selectedDate != null) {
|
||||||
|
showTimePicker(
|
||||||
|
context: context,
|
||||||
|
initialTime: TimeOfDay.now(),
|
||||||
|
).then((selectedTime) {
|
||||||
|
// Handle the selected date and time here.
|
||||||
|
if (selectedTime != null) {
|
||||||
|
DateTime selectedDateTime = DateTime(
|
||||||
|
selectedDate.year,
|
||||||
|
selectedDate.month,
|
||||||
|
selectedDate.day,
|
||||||
|
selectedTime.hour,
|
||||||
|
selectedTime.minute,
|
||||||
|
);
|
||||||
|
if (selectedDateTime != null) {
|
||||||
|
setState(() {
|
||||||
|
dateTo = selectedDateTime;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
).expanded,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
12.height,
|
||||||
|
|
||||||
|
AppFilledButton(label: context.translation.search, maxWidth: false, onPressed: getSwipeHistory),
|
||||||
|
8.height,
|
||||||
|
Divider(thickness: 2,),
|
||||||
|
|
||||||
|
Consumer<UserProvider>(
|
||||||
|
builder: (context, snapshot,child) {
|
||||||
|
return SwipeHistoryList(snapshot.swipeHistory ?? [], snapshot.isLoading).expanded;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
|
||||||
|
],
|
||||||
|
).paddingAll(20),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SwipeHistoryList extends StatelessWidget {
|
||||||
|
List<SwipeHistory> list;
|
||||||
|
bool isLoading;
|
||||||
|
|
||||||
|
SwipeHistoryList(this.list, this.isLoading, {Key key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return (list.isEmpty && !isLoading)
|
||||||
|
? NoItemFound(message: context.translation.noDataFound)
|
||||||
|
: ListView.separated(
|
||||||
|
padding: EdgeInsets.only(top: 12.toScreenHeight),
|
||||||
|
itemBuilder: (cxt, index) {
|
||||||
|
if (isLoading) return const SizedBox().toRequestShimmer(cxt, isLoading);
|
||||||
|
return SwipeHistoryCard(list[index]);
|
||||||
|
},
|
||||||
|
separatorBuilder: (cxt, index) => 12.height,
|
||||||
|
itemCount: isLoading ? 6 : list.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class SwipeHistoryCard extends StatelessWidget {
|
||||||
|
final SwipeHistory swipeHistory;
|
||||||
|
final bool showShadow;
|
||||||
|
const SwipeHistoryCard(this.swipeHistory, {Key key, this.showShadow = true}) : super(key: key);
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(swipeHistory.swipeTime.toServiceRequestDetailsFormat, textAlign: TextAlign.end, style: AppTextStyles.tinyFont.copyWith(color: context.isDark ? AppColor.neutral10 : AppColor.neutral50)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
8.height,
|
||||||
|
'${context.translation.swipeTypeName}: ${swipeHistory.swipeTypeName?.cleanupWhitespace?.capitalizeFirstOfEach}'.bodyText(context),
|
||||||
|
'${context.translation.userName}: ${swipeHistory.userName}'.bodyText(context),
|
||||||
|
'${context.translation.siteName}: ${swipeHistory.siteName}'.bodyText(context),
|
||||||
|
'${context.translation.pointName}: ${swipeHistory.pointName}'.bodyText(context),
|
||||||
|
8.height,
|
||||||
|
],
|
||||||
|
).toShadowContainer(context, showShadow: showShadow);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue