import 'package:flutter/material.dart'; class DurationPicker extends StatefulWidget { const DurationPicker({ super.key, required this.onChangedCallback, required this.width, required this.height, this.initialDuration = const Duration(), }); final void Function(Duration duration) onChangedCallback; final double width; final double height; final Duration initialDuration; @override State createState() => _DurationPickerState(); } class _DurationPickerState extends State { String _timeString = ''; @override void initState() { super.initState(); if (widget.initialDuration.inMinutes != 0) { _timeString = '${widget.initialDuration.inHours}${widget.initialDuration.inMinutes % 60}'; } } @override Widget build(BuildContext context) { return Column( mainAxisSize: MainAxisSize.min, children: [ SizedBox( width: widget.width, height: widget.height / 4, child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.end, children: [ Text( _timeString.padLeft(4, '0').substring(0, 2), style: Theme.of(context).textTheme.displayMedium, ), Padding(padding: EdgeInsets.only(left: 5)), Text( 'h', style: Theme.of(context).textTheme.bodyLarge!.copyWith(height: 2), ), Padding(padding: EdgeInsets.only(left: 10)), Text( _timeString.padLeft(4, '0').substring(2, 4), style: Theme.of(context).textTheme.displayMedium, ), Padding(padding: EdgeInsets.only(left: 5)), Text( 'm', style: Theme.of(context).textTheme.bodyLarge!.copyWith(height: 2), ), ], ), ), Padding(padding: EdgeInsets.only(top: 10)), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: widget.width / 3, height: widget.height / 4, child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), child: ElevatedButton( style: _buttonTheme, onPressed: () => _addTime(7), child: Text('7'), ), ), ), SizedBox( width: widget.width / 3, height: widget.height / 4, child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), child: ElevatedButton( style: _buttonTheme, onPressed: () => _addTime(8), child: Text('8'), ), ), ), SizedBox( width: widget.width / 3, height: widget.height / 4, child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), child: ElevatedButton( style: _buttonTheme, onPressed: () => _addTime(9), child: Text('9'), ), ), ), ], ), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: widget.width / 3, height: widget.height / 4, child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), child: ElevatedButton( style: _buttonTheme, onPressed: () => _addTime(4), child: Text('4'), ), ), ), SizedBox( width: widget.width / 3, height: widget.height / 4, child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), child: ElevatedButton( style: _buttonTheme, onPressed: () => _addTime(5), child: Text('5'), ), ), ), SizedBox( width: widget.width / 3, height: widget.height / 4, child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), child: ElevatedButton( style: _buttonTheme, onPressed: () => _addTime(6), child: Text('6'), ), ), ), ], ), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: widget.width / 3, height: widget.height / 4, child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), child: ElevatedButton( style: _buttonTheme, onPressed: () => _addTime(1), child: Text('1'), ), ), ), SizedBox( width: widget.width / 3, height: widget.height / 4, child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), child: ElevatedButton( style: _buttonTheme, onPressed: () => _addTime(2), child: Text('2'), ), ), ), SizedBox( width: widget.width / 3, height: widget.height / 4, child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), child: ElevatedButton( style: _buttonTheme, onPressed: () => _addTime(3), child: Text('3'), ), ), ), ], ), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: widget.width / 3, height: widget.height / 4, child: Padding(padding: EdgeInsets.all(0)), ), SizedBox( width: widget.width / 3, height: widget.height / 4, child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), child: ElevatedButton( style: _buttonTheme, onPressed: () => _addTime(0), child: Text('0'), ), ), ), SizedBox( width: widget.width / 3, height: widget.height / 4, child: Padding( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), child: ElevatedButton( style: _buttonTheme, onPressed: _removeTime, child: Icon( Icons.backspace, size: 24, ), ), ), ), ], ), ], ); } void _addTime(int time) { if (_timeString.length >= 4) return; if (_timeString.isEmpty && time == 0) return; setState(() { _timeString += time.toString(); }); widget.onChangedCallback(_duration); } void _removeTime() { if (_timeString.isEmpty) return; setState(() { _timeString = _timeString.substring(0, _timeString.length - 1); }); widget.onChangedCallback(_duration); } Duration get _duration { return Duration( hours: int.parse(_timeString.padLeft(4, '0').substring(0, 2)), minutes: int.parse(_timeString.padLeft(4, '0').substring(2, 4)), ); } ButtonStyle get _buttonTheme => ElevatedButton.styleFrom( textStyle: Theme.of(context).textTheme.displaySmall, ); }