main, fix older versions of Android not being able to see bottom widgets when keyboard is open.
This commit is contained in:
1 parent
f5e368a902
commit
7ab13dd4de
1 file changed
+169
-166
+169
-166
@@ -368,183 +368,186 @@ class _HomeScreenState extends State<HomeScreen> {
|
||||
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,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user