Skip to content

Instantly share code, notes, and snippets.

@antiv
Created June 11, 2022 20:21
Show Gist options
  • Save antiv/339823f661840a47f9fcb79cc05ad37e to your computer and use it in GitHub Desktop.
Save antiv/339823f661840a47f9fcb79cc05ad37e to your computer and use it in GitHub Desktop.
Cupertino like DateTime picker with secunds
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:intl/intl.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
final DateFormat _formatter = DateFormat('dd.MM.yyyy HH:mm:ss');
final TextEditingController _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Center(
child: SizedBox(
width: 500,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
controller: _controller,
textInputAction: TextInputAction.next,
keyboardType: TextInputType.datetime,
decoration: InputDecoration(
labelText: 'Insert date or select clock icon',
hintText: 'Insert date',
prefixIcon: const Icon(Icons.calendar_today),
suffixIcon: InkWell(
onTap: () {
showDialog(
context: context,
builder: (BuildContext context) {
return _buildAlertDialog(context, _controller);
});
},
child: const Icon(Icons.access_time_outlined)),
),
onChanged: (val) {
// _referencedDocumentNumberController.text = val;
},
),
const SizedBox(height: 5.0),
const Text('Cupertino like Date Time picker with secunds!')
],
),
),
);
}
AlertDialog _buildAlertDialog(BuildContext context, controller) {
return AlertDialog(
// contentPadding: EdgeInsets.zero,
insetPadding: EdgeInsets.zero,
content: SizedBox(
width: 500,
height: MediaQuery.of(context).size.height * 0.7,
child: TimeSelector(
currentTime: DateTime.now(),
labelTime: "Select date",
labelDate: 'Select time',
dmyLabels: const ['Day', 'Month', 'Year'],
hmsLabels: const ['Hour', 'Minute', 'Second'],
onConfirm: (date) {
setState(() {
controller.text = _formatter.format(date);
});
Navigator.of(context).pop(date);
},
onClose: () => Navigator.of(context).pop(),
),
),
);
}
}
class TimeSelector extends StatefulWidget {
const TimeSelector({
Key? key,
this.labelTime,
this.labelDate,
this.dmyLabels,
this.hmsLabels,
this.currentTime,
this.onClose,
this.onConfirm,
}) : super(key: key);
final String? labelTime;
final String? labelDate;
final List<String>? dmyLabels;
final List<String>? hmsLabels;
final DateTime? currentTime;
final void Function()? onClose;
final void Function(DateTime date)? onConfirm;
@override
State<TimeSelector> createState() => _TimeSelectorState();
static Future<DateTime?> showFullScreenSelector({
required BuildContext context,
DateTime? currentTime,
String? labelTime,
String? labelDate,
List<String>? dmyLabels,
List<String>? hmsLabels,
}) {
return showGeneralDialog<DateTime?>(
context: context,
barrierDismissible: false,
transitionDuration: const Duration(milliseconds: 150),
transitionBuilder: (context, animation, secondaryAnimation, child) {
return FadeTransition(opacity: animation, child: child);
},
pageBuilder: (context, animation, secondaryAnimation) {
return TimeSelector(
currentTime: currentTime,
labelDate: labelDate,
labelTime: labelTime,
dmyLabels: dmyLabels,
hmsLabels: hmsLabels,
onConfirm: (date) => Navigator.of(context).pop(date),
onClose: () => Navigator.of(context).pop(),
);
},
);
}
}
class _TimeSelectorState extends State<TimeSelector> {
late DateTime currentTime = widget.currentTime ?? DateTime.now();
bool _dateSelected = false;
double fontSize = 42;
double itemExtent = 48;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Center(
child: Padding(
padding: EdgeInsets.zero,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Text(_dateSelected ? widget.labelTime ?? '' : widget.labelDate ?? '',
style: const TextStyle(fontSize: 24)),
),
Expanded(
child: CupertinoTheme(
data: CupertinoThemeData(
brightness: Brightness.light,
textTheme: CupertinoTextThemeData(
pickerTextStyle: Theme.of(context)
.textTheme
.headline5
?.copyWith(
fontSize: fontSize,
color: Theme.of(context).primaryColor),
),
),
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 400),
switchInCurve: Curves.easeIn,
switchOutCurve: Curves.easeOut,
child: _dateSelected
? Row(
key: const ValueKey('time'),
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
...[for (var i = 0; i <= 2; i++) i]
.map(
(w) => SizedBox(
height: 350,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(widget.hmsLabels![w]),
SizedBox(
height: 300,
width: constraints.maxWidth / 3,
child: Center(
child: CupertinoPicker(
// backgroundColor: Colors.white,
itemExtent: itemExtent,
useMagnifier: true,
magnification: 1.2,
looping: true,
scrollController:
FixedExtentScrollController(
initialItem:
_getInitialItem(w)),
children: [
for (var i = 0;
i <= (w == 0 ? 23 : 59);
i++)
i
]
.map((e) => SizedBox(
height: itemExtent,
child: e < 10
? Text('0$e')
: Text('$e')))
.toList(),
onSelectedItemChanged: (value) {
currentTime =
currentTime.toLocal();
setState(() {
currentTime =
_setSelectedTime(
w, value);
});
},
),
),
),
],
),
),
)
.toList(),
],
)
: Row(
key: const ValueKey('date'),
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: [
...[for (var i = 0; i <= 2; i++) i]
.map(
(w) => SizedBox(
height: 350,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(widget.dmyLabels![w]),
SizedBox(
height: 300,
width: w == 2
? constraints.maxWidth / 4 * 2
: constraints.maxWidth / 4,
child: Center(
child: CupertinoPicker(
// backgroundColor: Colors.white,
itemExtent: itemExtent,
useMagnifier: true,
magnification: 1.2,
looping: true,
scrollController:
FixedExtentScrollController(
initialItem:
_getInitialItem(w,
date: true)),
children: [
for (var i = 1;
i <=
(w == 0
? 31
: w == 1
? 12
: 5);
i++)
i
]
.map((e) => SizedBox(
height: itemExtent,
child: w == 2
? Text(
'${2017 + e}')
: e < 10
? Text('0$e')
: Text('$e')))
.toList(),
onSelectedItemChanged: (value) {
currentTime =
currentTime.toLocal();
setState(() {
currentTime =
_setSelectedDate(
w, value);
});
},
),
),
),
],
),
),
)
.toList(),
],
),
),
),
),
Text(DateFormat('dd.MM.yyyy HH:mm:ss').format(currentTime),
style: Theme.of(context)
.textTheme
.headline5
?.copyWith(
color: Theme.of(context).primaryColor),),
Padding(
padding: const EdgeInsets.all(8.0),
child: ActionButtons(
setData: () {
if (_dateSelected == true) {
widget.onConfirm?.call(currentTime);
} else {
setState(() {
_dateSelected = true;
});
}
},
cleanForm: () => Navigator.of(context).pop(),
),
)
],
);
}),
),
),
),
);
}
_getInitialItem(int w, {bool? date}) {
if (date == true) {
if (w == 0) return currentTime.day - 1;
if (w == 1) return currentTime.month - 1;
if (w == 2) return currentTime.year - 2018;
} else {
if (w == 0) return currentTime.hour;
if (w == 1) return currentTime.minute;
if (w == 2) return currentTime.second;
}
}
_setSelectedTime(int w, int value) {
return DateTime(
currentTime.year,
currentTime.month,
currentTime.day,
w == 0 ? value : currentTime.hour,
w == 1 ? value : currentTime.minute,
w == 2 ? value : currentTime.second,
currentTime.millisecond,
currentTime.microsecond);
}
_setSelectedDate(int w, int value) {
return DateTime(
w == 2 ? value + 2018 : currentTime.year,
w == 1 ? value + 1 : currentTime.month,
w == 0 ? value + 1 : currentTime.day,
currentTime.hour,
currentTime.minute,
currentTime.second,
currentTime.millisecond,
currentTime.microsecond);
}
}
class ActionButtons extends StatelessWidget {
const ActionButtons({Key? key, this.setData, this.cleanForm, this.okText, this.cancelText}) : super(key: key);
final String? okText;
final Function? setData;
final String? cancelText;
final Function? cleanForm;
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ElevatedButton.icon(
label: Text(okText ?? "OK"),
onPressed: () => {if (setData != null) setData!()},
icon: const Icon(Icons.check),
style: ButtonStyle(
fixedSize: MaterialStateProperty.all(
Size(constraints.maxWidth / 2 - 5, 30)),
),
),
ElevatedButton.icon(
label: Text(cancelText ?? 'Cancel'),
onPressed: () => {
if (cleanForm != null) cleanForm!()
},
icon: const Icon(Icons.clear),
style: ButtonStyle(
fixedSize: MaterialStateProperty.all(
Size(constraints.maxWidth / 2 - 5, 30)),
),
),
],
);
}
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment