kurdish_date_picker 1.3.1
kurdish_date_picker: ^1.3.1 copied to clipboard
A professional Kurdish-first Date Picker and Calendar for Flutter: Sorani RTL, Kurdish day/month names, Kurdish numerals, and single/range/multiple selection with theming.
import 'package:flutter/material.dart';
import 'package:kurdish_date_picker/kurdish_date_picker.dart';
void main() {
runApp(const KurdishDatePickerExampleApp());
}
class KurdishDatePickerExampleApp extends StatefulWidget {
const KurdishDatePickerExampleApp({super.key});
@override
State<KurdishDatePickerExampleApp> createState() =>
_KurdishDatePickerExampleAppState();
}
class _KurdishDatePickerExampleAppState
extends State<KurdishDatePickerExampleApp> {
ThemeMode _themeMode = ThemeMode.light;
void _setThemeMode(ThemeMode mode) => setState(() => _themeMode = mode);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'بڕگەی رێکەوتی کوردی',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
brightness: Brightness.light,
colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal),
),
darkTheme: ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.teal,
brightness: Brightness.dark,
),
),
themeMode: _themeMode,
home: HomePage(
themeMode: _themeMode,
onThemeModeChanged: _setThemeMode,
),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({
super.key,
required this.themeMode,
required this.onThemeModeChanged,
});
final ThemeMode themeMode;
final ValueChanged<ThemeMode> onThemeModeChanged;
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
KurdishLocale _locale = KurdishLocale.sorani;
NumberSystem _numberSystem = NumberSystem.kurdish;
DateTime? _singleDate;
KurdishDateRange? _range;
final List<DateTime> _multiDates = [];
int _weekStart = DateTime.saturday;
bool _responsive = true;
// Interactive customization demo state (section "Customization").
Color _customSelected = Colors.deepPurple;
Color _customToday = Colors.amber;
Color _customWeekend = Colors.pink;
double _customRadius = 16;
double _customCellSize = 48;
double _customTextSize = 14;
// A controller the demo uses to navigate the calendar programmatically.
final KurdishCalendarController _controller = KurdishCalendarController();
// Events fall on fixed dates so the developer can see the indicators.
// A couple of days carry more than one event to demonstrate the tooltip and
// the "+N" overflow indicator.
final List<KurdishCalendarEvent> _events = [
KurdishCalendarEvent(
date: DateTime(2026, 9, 3), title: 'چاوپێکەوتن (Meeting)'),
KurdishCalendarEvent(
date: DateTime(2026, 9, 3), title: 'نانی نیوەڕۆ (Lunch)'),
KurdishCalendarEvent(
date: DateTime(2026, 9, 3), title: 'راهێنان (Training)'),
KurdishCalendarEvent(
date: DateTime(2026, 9, 3), title: 'پێداچوونەوە (Review)'),
KurdishCalendarEvent(
date: DateTime(2026, 9, 3), title: 'بڵاوکراوە (Release)'),
KurdishCalendarEvent(
date: DateTime(2026, 9, 10), title: 'پڕۆژە (Project deadline)'),
KurdishCalendarEvent(
date: DateTime(2026, 9, 21), title: 'کۆبوونەوە (Board meeting)'),
KurdishCalendarEvent(
date: DateTime(2026, 9, 27), title: 'ڕۆژی لەدایکبوون (Birthday)'),
];
@override
void dispose() {
_controller.dispose();
super.dispose();
}
// Convenience so section titles and demo labels stay short.
bool get _isRtlLocale => _locale != KurdishLocale.english;
Future<void> _pickDate() async {
final date = await showKurdishDatePicker(
context: context,
initialDate: _singleDate ?? DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime(2030),
locale: _locale,
numberSystem: _numberSystem,
weekStart: _weekStart,
responsive: _responsive,
events: _events,
);
if (date != null) {
setState(() => _singleDate = date);
}
}
Future<void> _pickRange() async {
final range = await showKurdishDateRangePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime(2030),
locale: _locale,
numberSystem: _numberSystem,
weekStart: _weekStart,
responsive: _responsive,
events: _events,
);
if (range != null) {
setState(() => _range = range);
}
}
Future<void> _pickMulti() async {
final dates = await showKurdishDateMultiPicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime(2030),
locale: _locale,
numberSystem: _numberSystem,
weekStart: _weekStart,
responsive: _responsive,
events: _events,
);
if (dates != null && mounted) {
setState(() {
_multiDates
..clear()
..addAll(dates);
});
}
}
String _fmt(DateTime date) => KurdishDateFormatter.format(
date,
locale: _locale,
numberSystem: _numberSystem,
);
String _fmtHijri(DateTime date) {
final hijri = KurdishHijriDate.fromGregorian(date);
final localization = KurdishDateFormatter.localization(_locale);
return hijri.format(
monthName: localization.hijriMonthNames[hijri.month - 1],
formatInt: (v) => KurdishNumberFormatter.toSystem(v, _numberSystem),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('بڕگەی رێکەوتی کوردی'),
actions: [
PopupMenuButton<ThemeMode>(
tooltip: 'دۆخ (Mode)',
initialValue: widget.themeMode,
onSelected: widget.onThemeModeChanged,
itemBuilder: (context) => const [
PopupMenuItem(
value: ThemeMode.light,
child: Text('دۆخی ڕووناک'),
),
PopupMenuItem(
value: ThemeMode.dark,
child: Text('دۆخی تاریک'),
),
PopupMenuItem(
value: ThemeMode.system,
child: Text('سیستەم'),
),
],
),
],
),
body: Directionality(
// The whole example screen uses Kurdish RTL layout.
textDirection: TextDirection.rtl,
child: Center(
child: ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 520),
child: ListView(
padding: const EdgeInsets.all(16),
children: [
// -----------------------------------------------------------------
// 0. Locale & numeral system switcher
// -----------------------------------------------------------------
Card(
elevation: 0,
color: Theme.of(context).colorScheme.surfaceContainerHighest,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'زمان و سیستەمی ژمارە',
style: Theme.of(context)
.textTheme
.titleSmall
?.copyWith(fontWeight: FontWeight.bold),
),
const SizedBox(height: 8),
SegmentedButton<KurdishLocale>(
segments: const [
ButtonSegment(
value: KurdishLocale.sorani,
label: Text('سۆرانی'),
),
ButtonSegment(
value: KurdishLocale.english,
label: Text('EN'),
),
],
selected: {_locale},
onSelectionChanged: (selection) =>
setState(() => _locale = selection.first),
),
const SizedBox(height: 8),
SegmentedButton<NumberSystem>(
segments: const [
ButtonSegment(
value: NumberSystem.kurdish,
label: Text('ژمارەی کوردی ٢٠٢٦'),
),
ButtonSegment(
value: NumberSystem.latin,
label: Text('2026'),
),
],
selected: {_numberSystem},
onSelectionChanged: (selection) =>
setState(() => _numberSystem = selection.first),
),
const SizedBox(height: 8),
SegmentedButton<int>(
segments: const [
ButtonSegment(
value: DateTime.saturday,
label: Text('شەممە'),
),
ButtonSegment(
value: DateTime.sunday,
label: Text('یەکشەممە'),
),
ButtonSegment(
value: DateTime.monday,
label: Text('دووشەممە'),
),
],
selected: {_weekStart},
onSelectionChanged: (selection) =>
setState(() => _weekStart = selection.first),
),
const SizedBox(height: 8),
SwitchListTile(
contentPadding: EdgeInsets.zero,
dense: true,
title:
const Text('متناسب لجميع الشاشات (Responsive)'),
value: _responsive,
onChanged: (v) => setState(() => _responsive = v),
),
],
),
),
),
const SizedBox(height: 16),
// -----------------------------------------------------------------
// 1. Basic Kurdish Date Picker (single) with events
// -----------------------------------------------------------------
_sectionTitle('1. بڕگەی سادە (Basic + Events)'),
Row(
children: [
FilledButton.tonalIcon(
onPressed: () => _controller.previousMonth(),
icon: const Icon(Icons.chevron_left),
label: const Text('پێشوو'),
),
FilledButton.tonalIcon(
onPressed: () => _controller.nextMonth(),
icon: const Icon(Icons.chevron_right),
label: const Text('دوواتر'),
),
],
),
const SizedBox(height: 8),
KurdishDatePicker(
controller: _controller,
initialDate: DateTime.now(),
firstDate: DateTime(2020),
lastDate: DateTime(2030),
locale: _locale,
numberSystem: _numberSystem,
selectionMode: DateSelectionMode.single,
showTodayButton: true,
weekStart: _weekStart,
responsive: _responsive,
events: _events,
onDateSelected: (date) => setState(() => _singleDate = date),
),
const SizedBox(height: 8),
_resultLine(
'رێکەوتەکەت:',
_singleDate == null
? 'نەهەڵبژێردراوە'
: _fmt(_singleDate!)),
const Divider(height: 32),
// -----------------------------------------------------------------
// 2. Dialog APIs
// -----------------------------------------------------------------
_sectionTitle('2. بڕگەی دیالۆگ (Dialog)'),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
FilledButton.icon(
onPressed: _pickDate,
icon: const Icon(Icons.calendar_today),
label: const Text('هەڵبژاردنی رۆژ'),
),
FilledButton.tonalIcon(
onPressed: _pickRange,
icon: const Icon(Icons.date_range),
label: const Text('ماوە (Range)'),
),
FilledButton.tonalIcon(
onPressed: _pickMulti,
icon: const Icon(Icons.calendar_month),
label: const Text('چەند رۆژ (Multiple)'),
),
],
),
const SizedBox(height: 8),
_resultLine(
'رۆژ:', _singleDate == null ? '—' : _fmt(_singleDate!)),
_resultLine(
'ماوە:',
_range == null
? '—'
: '${_fmt(_range!.start)} → ${_fmt(_range!.end)}'),
if (_multiDates.isNotEmpty)
_resultLine(
'چەند رۆژ (${KurdishNumberFormatter.toSystem(_multiDates.length, _numberSystem)}):',
_multiDates.map(_fmt).join('، '),
),
const Divider(height: 32),
// -----------------------------------------------------------------
// 3. RTL + Kurdish numerals focus
// -----------------------------------------------------------------
_sectionTitle('3. RTL و ژمارە کوردییەکان (RTL + Numerals)'),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: _isRtlLocale
? Theme.of(context).colorScheme.primaryContainer
: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(14),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
_isRtlLocale
? 'ئەم بڕگەیە بە شێوازی RTL دەردەکەوێت و هەموو ژمارەکان بە سیستەمی ${_numberSystem == NumberSystem.latin ? 'لاتینی' : '٢٠٢٦'}ن'
: 'English + Latin numerals (LTR)',
style: Theme.of(context).textTheme.bodyMedium,
),
const SizedBox(height: 8),
KurdishMonthPicker(
locale: _locale,
year: 2026,
selectedMonth: DateTime.now().month,
numberSystem: _numberSystem,
),
],
),
),
const Divider(height: 32),
// -----------------------------------------------------------------
// 3.5 Customization (interactive theme controls)
// -----------------------------------------------------------------
_sectionTitle('3.5 تایبەتکردن (Customization)'),
Card(
elevation: 0,
color: Theme.of(context).colorScheme.surfaceContainerHighest,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_colorRow('رەنگی هەڵبژێردراو', _customSelected,
(c) => setState(() => _customSelected = c)),
_colorRow('رەنگی ئەمڕۆ', _customToday,
(c) => setState(() => _customToday = c)),
_colorRow('رەنگی کۆتایی هەفتە', _customWeekend,
(c) => setState(() => _customWeekend = c)),
const SizedBox(height: 12),
Text(
'گۆشەی گەورە (Border radius): ${_customRadius.round()}',
style: Theme.of(context).textTheme.bodyMedium,
),
Slider(
value: _customRadius,
min: 0,
max: 32,
onChanged: (v) =>
setState(() => _customRadius = v)),
Text(
'قەبارەی خانە (Cell size): ${_customCellSize.round()}',
style: Theme.of(context).textTheme.bodyMedium,
),
Slider(
value: _customCellSize,
min: 32,
max: 72,
onChanged: (v) =>
setState(() => _customCellSize = v)),
Text(
'قەبارەی دەق (Text size): ${_customTextSize.round()}',
style: Theme.of(context).textTheme.bodyMedium,
),
Slider(
value: _customTextSize,
min: 10,
max: 22,
onChanged: (v) =>
setState(() => _customTextSize = v)),
],
),
),
),
const SizedBox(height: 12),
KurdishDatePicker(
initialDate: DateTime.now(),
locale: _locale,
numberSystem: _numberSystem,
showTodayButton: true,
themeData: KurdishDatePickerTheme(
selectedColor: _customSelected,
todayColor: _customToday,
weekendColor: _customWeekend,
borderRadius:
BorderRadius.all(Radius.circular(_customRadius)),
cellSize: _customCellSize,
textTheme: Theme.of(context)
.textTheme
.apply(fontSizeFactor: _customTextSize / 14),
),
onDateSelected: (date) {},
),
const Divider(height: 32),
// -----------------------------------------------------------------
// 4. Custom theme
// -----------------------------------------------------------------
_sectionTitle('4. تێمای تایبەت (Custom Theme)'),
KurdishDatePicker(
initialDate: DateTime.now(),
locale: _locale,
numberSystem: _numberSystem,
showTodayButton: true,
themeData: const KurdishDatePickerTheme(
selectedColor: Colors.deepPurple,
todayColor: Colors.amber,
weekendColor: Colors.pink,
borderRadius: BorderRadius.all(Radius.circular(16)),
headerColor: Colors.deepPurple,
headerTextColor: Colors.white,
),
onDateSelected: (date) {},
),
const Divider(height: 32),
// -----------------------------------------------------------------
// 5. Range picker (inline)
// -----------------------------------------------------------------
_sectionTitle('5. ماوە (Range)'),
KurdishDatePicker(
initialDate: DateTime.now(),
locale: _locale,
numberSystem: _numberSystem,
selectionMode: DateSelectionMode.range,
showTodayButton: true,
onRangeSelected: (s, e) =>
setState(() => _range = KurdishDateRange(s, e)),
),
_resultLine(
'ماوە:',
_range == null
? 'نەهەڵبژێردراوە'
: '${_fmt(_range!.start)} → ${_fmt(_range!.end)}'),
const Divider(height: 32),
// -----------------------------------------------------------------
// 6. Multiple picker (inline)
// -----------------------------------------------------------------
_sectionTitle('6. چەند رۆژ (Multiple)'),
KurdishDatePicker(
initialDate: DateTime.now(),
locale: _locale,
numberSystem: _numberSystem,
selectionMode: DateSelectionMode.multiple,
showTodayButton: true,
onDatesSelected: (dates) => setState(() {
_multiDates
..clear()
..addAll(dates);
}),
),
_resultLine(
'ژمارەی هەڵبژێردراو:',
KurdishNumberFormatter.toSystem(
_multiDates.length, _numberSystem),
),
const Divider(height: 32),
// -----------------------------------------------------------------
// 7. Disabled dates + first/last restrictions
// -----------------------------------------------------------------
_sectionTitle('7. رۆژە ناچالاکەکان (Disabled)'),
KurdishDatePicker(
initialDate: DateTime(2026, 9, 1),
firstDate: DateTime(2026, 9, 1),
lastDate: DateTime(2026, 9, 30),
locale: _locale,
numberSystem: _numberSystem,
disabledDates: [
DateTime(2026, 9, 10),
DateTime(2026, 9, 15),
DateTime(2026, 9, 20),
],
onDateSelected: (date) {},
),
const Divider(height: 32),
// -----------------------------------------------------------------
// 8. Date input
// -----------------------------------------------------------------
_sectionTitle('8. خانەی داخڵکردنی رێکەوت (Date Input)'),
KurdishDateInput(
locale: _locale,
numberSystem: _numberSystem,
initialDate: DateTime.now(),
onDateChanged: (date) {},
),
const Divider(height: 32),
// -----------------------------------------------------------------
// 9. Formatting helpers
// -----------------------------------------------------------------
_sectionTitle('9. ژمارە و فۆرماتکردن (Formatting)'),
_resultLine('toSystem(2026):',
KurdishNumberFormatter.toSystem(2026, _numberSystem)),
_resultLine(
'format date:',
KurdishDateFormatter.format(
DateTime(2026, 9, 3),
locale: _locale,
numberSystem: _numberSystem,
)),
_resultLine(
'formatNumeric:',
KurdishDateFormatter.formatNumeric(
DateTime(2026, 6, 15),
numberSystem: _numberSystem,
)),
_resultLine('toLatinString(٢٠٢٦):',
KurdishNumberFormatter.toLatinString('٢٠٢٦')),
const SizedBox(height: 8),
// -----------------------------------------------------------------
// 10. Hijri (Islamic) date conversion
// -----------------------------------------------------------------
_sectionTitle('10. رێکەوتی کۆچی (Hijri)'),
_resultLine('Hijri (ئەمڕۆ):', _fmtHijri(DateTime.now())),
_resultLine(
'Hijri (هەڵبژێردراو):',
_singleDate == null
? 'نەهەڵبژێردراوە'
: _fmtHijri(_singleDate!)),
const SizedBox(height: 24),
],
),
),
),
),
);
}
Widget _sectionTitle(String text) {
return Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Text(
text,
style: Theme.of(context)
.textTheme
.titleMedium
?.copyWith(fontWeight: FontWeight.bold),
),
);
}
Widget _resultLine(String label, String value) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'$label ',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.w600),
),
Expanded(
child: Text(
value,
textAlign: TextAlign.left,
style: Theme.of(context).textTheme.bodyMedium,
),
),
],
),
);
}
// A tappable color swatch that cycles through a small palette, used by the
// interactive customization demo so developers can preview theme colors live.
static const List<Color> _palette = [
Colors.deepPurple,
Colors.teal,
Colors.indigo,
Colors.orange,
Colors.redAccent,
Colors.green,
];
Widget _colorRow(String label, Color color, ValueChanged<Color> onChange) {
final index = _palette.indexOf(color);
final next = _palette[(index + 1) % _palette.length];
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
GestureDetector(
onTap: () => onChange(next),
child: Container(
width: 28,
height: 28,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
border: Border.all(
color: Theme.of(context).colorScheme.outline, width: 1.5),
),
),
),
const SizedBox(width: 12),
Text(label, style: Theme.of(context).textTheme.bodyMedium),
],
),
);
}
}