ujs_date_picker 0.1.1
ujs_date_picker: ^0.1.1 copied to clipboard
Custom date and time pickers. Dates use DD/MM/YYYY. Times use HH:mm.
example/lib/main.dart
import 'package:ujs_date_picker/ujs_date_picker.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const DatePickerExampleApp());
}
class DatePickerExampleApp extends StatelessWidget {
const DatePickerExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Date picker example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF1F6B4A)),
scaffoldBackgroundColor: const Color(0xFFF6F3EE),
),
home: const DatePickerDemo(),
);
}
}
class DatePickerDemo extends StatefulWidget {
const DatePickerDemo({super.key});
@override
State<DatePickerDemo> createState() => _DatePickerDemoState();
}
class _DatePickerDemoState extends State<DatePickerDemo> {
final _formKey = GlobalKey<FormState>();
var _type = DatePickerType.spin;
var _timeType = TimePickerType.spin;
var _use24Hour = true;
var _theme = CustomDatePickerThemeData.minimal;
DateTime? _date;
DateTimeRange? _range;
TimeOfDay? _time;
bool get _minimal => _theme == CustomDatePickerThemeData.minimal;
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Form(
key: _formKey,
child: ListView(
padding: const EdgeInsets.fromLTRB(24, 28, 24, 24),
children: [
const Text(
'Date picker',
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.w700,
color: Color(0xFF1C2430),
),
),
const SizedBox(height: 8),
const Text(
'Dates use DD/MM/YYYY. Times use HH:mm, on a 24-hour or 12-hour clock.',
style: TextStyle(
fontSize: 15,
height: 1.4,
color: Color(0xFF5E6772),
),
),
const SizedBox(height: 20),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
for (final type in DatePickerType.values)
ChoiceChip(
label: Text(type.name),
selected: _type == type,
onSelected: (_) {
setState(() {
_type = type;
_date = null;
_range = null;
});
},
),
],
),
const SizedBox(height: 12),
Wrap(
spacing: 8,
children: [
ChoiceChip(
label: const Text('Minimal'),
selected: _minimal,
onSelected: (_) {
setState(() => _theme = CustomDatePickerThemeData.minimal);
},
),
ChoiceChip(
label: const Text('Material'),
selected: !_minimal,
onSelected: (_) {
setState(() => _theme = CustomDatePickerThemeData.material);
},
),
],
),
const SizedBox(height: 24),
if (_type == DatePickerType.range)
CustomDateRangeField(
key: const ValueKey('range-field'),
theme: _theme,
onChanged: (value) => setState(() => _range = value),
validator: (value) => value == null ? 'Select a range' : null,
)
else
CustomDateField(
key: ValueKey(_type),
type: _type,
theme: _theme,
onChanged: (value) => setState(() => _date = value),
validator: (value) => value == null ? 'Select a date' : null,
),
const SizedBox(height: 20),
_ResultCard(date: _date, range: _range, isRange: _type == DatePickerType.range),
const SizedBox(height: 28),
const Text(
'Time',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w700,
color: Color(0xFF1C2430),
),
),
const SizedBox(height: 12),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
for (final type in TimePickerType.values)
ChoiceChip(
label: Text(type.name),
selected: _timeType == type,
onSelected: (_) {
setState(() {
_timeType = type;
_time = null;
});
},
),
ChoiceChip(
label: const Text('24-hour'),
selected: _use24Hour,
onSelected: (_) => setState(() => _use24Hour = true),
),
ChoiceChip(
label: const Text('12-hour'),
selected: !_use24Hour,
onSelected: (_) => setState(() => _use24Hour = false),
),
],
),
const SizedBox(height: 16),
CustomTimeField(
key: ValueKey('time-${_timeType.name}-$_use24Hour'),
initialValue: _time,
type: _timeType,
use24Hour: _use24Hour,
theme: _theme,
onChanged: (value) => setState(() => _time = value),
validator: (value) => value == null ? 'Select a time' : null,
),
const SizedBox(height: 20),
_TimeResultCard(time: _time, use24Hour: _use24Hour),
const SizedBox(height: 24),
FilledButton(
onPressed: () {
if (!_formKey.currentState!.validate()) return;
final dateText = _type == DatePickerType.range
? '${formatDdMmYyyy(_range!.start)} – ${formatDdMmYyyy(_range!.end)}'
: formatDdMmYyyy(_date!);
final message =
'Saved $dateText ${formatHhMm(_time!, use24Hour: _use24Hour)}';
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(SnackBar(content: Text(message)));
},
style: FilledButton.styleFrom(
backgroundColor: _theme.accent,
foregroundColor: _theme.onAccent,
minimumSize: const Size.fromHeight(52),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
),
child: const Text('Save'),
),
],
),
),
),
);
}
}
class _TimeResultCard extends StatelessWidget {
const _TimeResultCard({required this.time, required this.use24Hour});
final TimeOfDay? time;
final bool use24Hour;
@override
Widget build(BuildContext context) {
final empty = time == null;
return Container(
width: double.infinity,
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(18),
border: Border.all(color: const Color(0xFFE6E1D8)),
),
child: Text(
empty ? 'No time selected' : formatHhMm(time!, use24Hour: use24Hour),
style: TextStyle(
color: empty ? const Color(0xFF8B93A1) : const Color(0xFF1C2430),
fontSize: empty ? 15 : 28,
fontWeight: FontWeight.w700,
),
),
);
}
}
class _ResultCard extends StatelessWidget {
const _ResultCard({
required this.date,
required this.range,
required this.isRange,
});
final DateTime? date;
final DateTimeRange? range;
final bool isRange;
@override
Widget build(BuildContext context) {
final text = isRange
? (range == null
? 'No range selected'
: '${formatDdMmYyyy(range!.start)} – ${formatDdMmYyyy(range!.end)}')
: (date == null ? 'No date selected' : formatDdMmYyyy(date!));
return Container(
width: double.infinity,
padding: const EdgeInsets.all(18),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(18),
border: Border.all(color: const Color(0xFFE6E1D8)),
),
child: Text(
text,
style: TextStyle(
color: (isRange ? range == null : date == null)
? const Color(0xFF8B93A1)
: const Color(0xFF1C2430),
fontSize: (isRange ? range == null : date == null) ? 15 : 28,
fontWeight: FontWeight.w700,
),
),
);
}
}