diff --git a/lib/main.dart b/lib/main.dart index 28afb6d..9260cb3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -368,183 +368,186 @@ class _HomeScreenState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Sunset Alarm')), - body: Padding( - padding: const EdgeInsets.all(16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // --- Location section --- - const Text( - 'Location', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 8), - Row( - children: [ - Expanded( - child: TextField( - controller: _latController, - keyboardType: const TextInputType.numberWithOptions( - signed: true, - decimal: true, - ), - decoration: const InputDecoration( - labelText: 'Latitude', - border: OutlineInputBorder(), - ), - onChanged: (_) => _savePrefs(), - ), - ), - const SizedBox(width: 8), - Expanded( - child: TextField( - controller: _lngController, - keyboardType: const TextInputType.numberWithOptions( - signed: true, - decimal: true, - ), - decoration: const InputDecoration( - labelText: 'Longitude', - border: OutlineInputBorder(), - ), - onChanged: (_) => _savePrefs(), - ), - ), - const SizedBox(width: 8), - IconButton.filledTonal( - icon: const Icon(Icons.my_location), - tooltip: 'Get GPS Location', - onPressed: _getGpsLocation, - ), - ], - ), - const SizedBox(height: 16), - - // --- Sunset time section --- - const Text( - 'Sunset', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 8), - Row( - children: [ - Expanded( - child: TextField( - controller: _sunsetController, - decoration: InputDecoration( - labelText: 'Sunset Time (HH:MM)', - border: OutlineInputBorder(), - errorText: _sunsetErrorText, - suffixIcon: IconButton( - icon: Icon(Icons.access_time), - onPressed: () async { - final picked = await showTimePicker( - context: context, - initialTime: TimeOfDay.now(), - ); - if (picked != null) { - _sunsetController.text = - '${picked.hour.toString().padLeft(2, '0')}:' - '${picked.minute.toString().padLeft(2, '0')}'; - setState( - () => - _sunsetErrorText = _validateTime( - _sunsetController.text, - ), - ); - if (_alarmEnabled) { - _scheduleAlarm(); - } - } - }, - ), - ), - onChanged: (v) { - final val = _validateTime(v); - if (val == null) { - _savePrefs(); - if (_alarmEnabled) { - _scheduleAlarm(); - } - } - setState(() { - _sunsetErrorText = val; - }); - }, - ), - ), - const SizedBox(width: 8), - IconButton.filledTonal( - icon: - _loading - ? const SizedBox( - width: 20, - height: 20, - child: CircularProgressIndicator(strokeWidth: 2), - ) - : const Icon(Icons.cloud_done_outlined), - tooltip: 'Fetch Open-Meteo Sunset Data', - onPressed: () { - if (!_loading) { - _fetchSunset(); - } - }, - ), - ], - ), - const SizedBox(height: 16), - - const Text( - 'Alert', - style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), - ), - const SizedBox(height: 8), - - // --- Minutes before --- - Row( - children: [ - if (!_alarmEnabled) + resizeToAvoidBottomInset: true, // default, but be explicit + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // --- Location section --- + const Text( + 'Location', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), + Row( + children: [ Expanded( child: TextField( - controller: _minutesController, - keyboardType: TextInputType.number, - decoration: InputDecoration( + controller: _latController, + keyboardType: const TextInputType.numberWithOptions( + signed: true, + decimal: true, + ), + decoration: const InputDecoration( + labelText: 'Latitude', border: OutlineInputBorder(), - labelText: 'Minutes Before Sunset', - errorText: _minuteErrorText, + ), + onChanged: (_) => _savePrefs(), + ), + ), + const SizedBox(width: 8), + Expanded( + child: TextField( + controller: _lngController, + keyboardType: const TextInputType.numberWithOptions( + signed: true, + decimal: true, + ), + decoration: const InputDecoration( + labelText: 'Longitude', + border: OutlineInputBorder(), + ), + onChanged: (_) => _savePrefs(), + ), + ), + const SizedBox(width: 8), + IconButton.filledTonal( + icon: const Icon(Icons.my_location), + tooltip: 'Get GPS Location', + onPressed: _getGpsLocation, + ), + ], + ), + const SizedBox(height: 16), + + // --- Sunset time section --- + const Text( + 'Sunset', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), + Row( + children: [ + Expanded( + child: TextField( + controller: _sunsetController, + decoration: InputDecoration( + labelText: 'Sunset Time (HH:MM)', + border: OutlineInputBorder(), + errorText: _sunsetErrorText, + suffixIcon: IconButton( + icon: Icon(Icons.access_time), + onPressed: () async { + final picked = await showTimePicker( + context: context, + initialTime: TimeOfDay.now(), + ); + if (picked != null) { + _sunsetController.text = + '${picked.hour.toString().padLeft(2, '0')}:' + '${picked.minute.toString().padLeft(2, '0')}'; + setState( + () => + _sunsetErrorText = _validateTime( + _sunsetController.text, + ), + ); + if (_alarmEnabled) { + _scheduleAlarm(); + } + } + }, + ), ), onChanged: (v) { - final val = int.tryParse(v); - setState(() { - if (val != null) { - _minuteErrorText = null; - _minutesBefore = val; - } else { - _minuteErrorText = 'Invalid Minute Value'; + final val = _validateTime(v); + if (val == null) { + _savePrefs(); + if (_alarmEnabled) { + _scheduleAlarm(); } - _plural = (_minutesBefore == 1) ? '' : 's'; + } + setState(() { + _sunsetErrorText = val; }); }, ), ), - - // --- Toggle --- - Expanded( - child: SwitchListTile( - //title: const Text('Enable Sunset Alarm'), - title: Text(_alarmEnabled ? 'Sunset Alarm Enabled' : ''), - subtitle: Text( - _alarmEnabled - ? '${_minutesBefore} Minute${_plural} Before @ $_alarmDisplayTime' - : '', - ), - value: _alarmEnabled, - onChanged: _toggleAlarm, + const SizedBox(width: 8), + IconButton.filledTonal( + icon: + _loading + ? const SizedBox( + width: 20, + height: 20, + child: CircularProgressIndicator(strokeWidth: 2), + ) + : const Icon(Icons.cloud_done_outlined), + tooltip: 'Fetch Open-Meteo Sunset Data', + onPressed: () { + if (!_loading) { + _fetchSunset(); + } + }, ), - ), - ], - ), - ], + ], + ), + const SizedBox(height: 16), + + const Text( + 'Alert', + style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 8), + + // --- Minutes before --- + Row( + children: [ + if (!_alarmEnabled) + Expanded( + child: TextField( + controller: _minutesController, + keyboardType: TextInputType.number, + decoration: InputDecoration( + border: OutlineInputBorder(), + labelText: 'Minutes Before Sunset', + errorText: _minuteErrorText, + ), + onChanged: (v) { + final val = int.tryParse(v); + setState(() { + if (val != null) { + _minuteErrorText = null; + _minutesBefore = val; + } else { + _minuteErrorText = 'Invalid Minute Value'; + } + _plural = (_minutesBefore == 1) ? '' : 's'; + }); + }, + ), + ), + + // --- Toggle --- + Expanded( + child: SwitchListTile( + //title: const Text('Enable Sunset Alarm'), + title: Text(_alarmEnabled ? 'Sunset Alarm Enabled' : ''), + subtitle: Text( + _alarmEnabled + ? '${_minutesBefore} Minute${_plural} Before @ $_alarmDisplayTime' + : '', + ), + value: _alarmEnabled, + onChanged: _toggleAlarm, + ), + ), + ], + ), + ], + ), ), ), );