Implemented calendar widget and bloc.

This commit is contained in:
Faris Armoush
2025-05-06 14:59:54 +03:00
parent 428cd34492
commit ad41a2a87e
10 changed files with 305 additions and 41 deletions

View File

@ -0,0 +1,17 @@
import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
part 'analytics_date_picker_event.dart';
class AnalyticsDatePickerBloc extends Bloc<AnalyticsDatePickerEvent, DateTime> {
AnalyticsDatePickerBloc() : super(DateTime.now()) {
on<UpdateAnalyticsDatePickerEvent>(_onUpdateAnalyticsDatePickerEvent);
}
void _onUpdateAnalyticsDatePickerEvent(
UpdateAnalyticsDatePickerEvent event,
Emitter<DateTime> emit,
) {
emit(event.date);
}
}

View File

@ -0,0 +1,17 @@
part of 'analytics_date_picker_bloc.dart';
sealed class AnalyticsDatePickerEvent extends Equatable {
const AnalyticsDatePickerEvent();
@override
List<Object?> get props => [];
}
final class UpdateAnalyticsDatePickerEvent extends AnalyticsDatePickerEvent {
const UpdateAnalyticsDatePickerEvent(this.date);
final DateTime date;
@override
List<Object?> get props => [date];
}

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/bloc/analytics_tab_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_communities_sidebar.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_communities_sidebar.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_page_tabs_and_children.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_page_tabs_and_children.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_by_phases/energy_consumption_by_phases_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/blocs/energy_consumption_by_phases/energy_consumption_by_phases_bloc.dart';

View File

@ -1,57 +1,92 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_date_picker_bloc/analytics_date_picker_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/month_picker_widget.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/constants/assets.dart';
class AnalyticsDateFilterButton extends StatelessWidget { class AnalyticsDateFilterButton extends StatefulWidget {
const AnalyticsDateFilterButton({super.key}); const AnalyticsDateFilterButton({super.key});
static final _color = ColorsManager.blackColor.withValues(alpha: 0.8); static final _color = ColorsManager.blackColor.withValues(alpha: 0.8);
@override
State<AnalyticsDateFilterButton> createState() =>
_AnalyticsDateFilterButtonState();
}
class _AnalyticsDateFilterButtonState extends State<AnalyticsDateFilterButton> {
late final AnalyticsDatePickerBloc _analyticsDatePickerBloc;
@override
void initState() {
_analyticsDatePickerBloc = AnalyticsDatePickerBloc();
super.initState();
}
@override
void dispose() {
_analyticsDatePickerBloc.close();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return TextButton.icon( return BlocProvider.value(
style: TextButton.styleFrom( value: _analyticsDatePickerBloc,
foregroundColor: _color, child: Builder(builder: (context) {
shape: RoundedRectangleBorder( final selectedDate = context.watch<AnalyticsDatePickerBloc>().state;
borderRadius: BorderRadius.circular(16), return TextButton.icon(
side: const BorderSide( style: TextButton.styleFrom(
color: ColorsManager.greyColor, foregroundColor: AnalyticsDateFilterButton._color,
width: 1, shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: const BorderSide(
color: ColorsManager.greyColor,
width: 1,
),
),
backgroundColor: ColorsManager.transparentColor,
padding: const EdgeInsets.symmetric(
horizontal: 32,
vertical: 16,
),
), ),
), icon: SvgPicture.asset(
backgroundColor: ColorsManager.transparentColor, Assets.blankCalendar,
padding: const EdgeInsets.symmetric( height: 20,
horizontal: 32, width: 20,
vertical: 16, colorFilter:
), ColorFilter.mode(AnalyticsDateFilterButton._color, BlendMode.srcIn),
), ),
icon: SvgPicture.asset( label: Text(
Assets.blankCalendar, _formatDate(selectedDate),
height: 20, style: const TextStyle(
width: 20, fontWeight: FontWeight.w700,
colorFilter: ColorFilter.mode(_color, BlendMode.srcIn), ),
), ),
label: Text( onPressed: () {
_concatenateDate(DateTime(2024, 1), DateTime(2024, 12)), showDialog(
style: const TextStyle( context: context,
fontWeight: FontWeight.w700, builder: (context) => MonthPickerWidget(
), selectedDate: selectedDate,
), onDateSelected: (value) {
onPressed: () {}, _analyticsDatePickerBloc.add(
UpdateAnalyticsDatePickerEvent(value),
);
},
),
);
},
);
}),
); );
} }
String _formatDate(DateTime date) { String _formatDate(DateTime? date) {
final formatter = DateFormat('MMM yyyy'); final formatter = DateFormat('MMMM yyyy');
final formattedDate = formatter.format(date); final formattedDate = formatter.format(date ?? DateTime.now());
return formattedDate; return formattedDate;
} }
String _concatenateDate(DateTime startDate, DateTime endDate) {
final formattedStartDate = _formatDate(startDate);
final formattedEndDate = _formatDate(endDate);
return '$formattedStartDate - $formattedEndDate';
}
} }

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/bloc/analytics_tab_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/color_manager.dart';

View File

@ -1,6 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/bloc/analytics_tab_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_tab/analytics_tab_bloc.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/enums/analytics_page_tab.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_date_filter_button.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_date_filter_button.dart';
import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_page_tab_button.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/analytics_page_tab_button.dart';

View File

@ -0,0 +1,194 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class MonthPickerWidget extends StatefulWidget {
const MonthPickerWidget({
super.key,
required this.selectedDate,
required this.onDateSelected,
});
final DateTime selectedDate;
final ValueChanged<DateTime>? onDateSelected;
@override
State<MonthPickerWidget> createState() => _MonthPickerWidgetState();
}
class _MonthPickerWidgetState extends State<MonthPickerWidget> {
late int _currentYear;
int? _selectedMonth;
static const _monthNames = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
@override
void initState() {
super.initState();
_currentYear = widget.selectedDate.year;
_selectedMonth = widget.selectedDate.month - 1;
}
@override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Theme.of(context).colorScheme.surface,
child: Container(
padding: const EdgeInsetsDirectional.all(20),
width: 320,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
_buildYearSelector(),
_buildMonthsGrid(),
const SizedBox(height: 20),
Row(
spacing: 12,
mainAxisAlignment: MainAxisAlignment.end,
children: [
FilledButton(
onPressed: () => Navigator.pop(context),
style: FilledButton.styleFrom(
fixedSize: const Size(106, 40),
backgroundColor: const Color(0xFFEDF2F7),
padding: const EdgeInsetsDirectional.symmetric(
vertical: 10,
horizontal: 16,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
child: Text(
'Cancel',
style: context.textTheme.titleSmall?.copyWith(
fontSize: 14,
fontWeight: FontWeight.w600,
color: ColorsManager.grey700,
),
),
),
FilledButton(
onPressed: () {
Navigator.pop(context);
final date = DateTime(
_currentYear,
_selectedMonth! + 1,
);
widget.onDateSelected?.call(date);
},
style: FilledButton.styleFrom(
fixedSize: const Size(106, 40),
backgroundColor: ColorsManager.vividBlue.withValues(
alpha: 0.7,
),
padding: const EdgeInsetsDirectional.symmetric(
vertical: 10,
horizontal: 16,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
child: Text(
'Done',
style: context.textTheme.titleSmall?.copyWith(
fontSize: 14,
fontWeight: FontWeight.w600,
color: ColorsManager.whiteColors,
),
),
),
],
),
],
),
),
);
}
Row _buildYearSelector() {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'$_currentYear',
style: context.textTheme.titleSmall?.copyWith(
fontSize: 14,
fontWeight: FontWeight.w500,
color: ColorsManager.grey700,
),
),
const Spacer(),
IconButton(
onPressed: () => setState(() => _currentYear = _currentYear - 1),
icon: const Icon(
Icons.chevron_left,
color: ColorsManager.grey700,
),
),
IconButton(
onPressed: () => setState(() => _currentYear = _currentYear + 1),
icon: const Icon(
Icons.chevron_right,
color: ColorsManager.grey700,
),
),
],
);
}
Widget _buildMonthsGrid() {
return GridView.builder(
shrinkWrap: true,
itemCount: 12,
physics: const NeverScrollableScrollPhysics(),
padding: const EdgeInsets.all(8),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 2.5,
mainAxisSpacing: 8,
mainAxisExtent: 30,
),
itemBuilder: (context, index) {
final isSelected = _selectedMonth == index;
return InkWell(
onTap: () => setState(() => _selectedMonth = index),
child: Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: isSelected
? ColorsManager.vividBlue.withValues(alpha: 0.7)
: const Color(0xFFEDF2F7),
borderRadius:
isSelected ? BorderRadius.circular(15) : BorderRadius.zero,
),
child: Text(
_monthNames[index],
style: context.textTheme.titleSmall?.copyWith(
fontSize: 12,
color: isSelected
? ColorsManager.whiteColors
: ColorsManager.blackColor.withValues(alpha: 0.8),
fontWeight: FontWeight.w500,
),
),
),
);
},
);
}
}

View File

@ -72,4 +72,5 @@ abstract class ColorsManager {
//background: #F8F8F8; //background: #F8F8F8;
static const Color vividBlue = Color(0xFF023DFE); static const Color vividBlue = Color(0xFF023DFE);
static const Color semiTransparentRed = Color(0x99FF0000); static const Color semiTransparentRed = Color(0x99FF0000);
static const Color grey700 = Color(0xFF2D3748);
} }