fix(block2): sharing sheet overflow + location button feedback/robustness

Reported: sheet overflowed 7.7px with the keyboard up, and 'use my location'
seemed to do nothing.
- Config sheet is now scrollable (SingleChildScrollView) — no keyboard overflow.
- 'Use my location' shows an inline spinner + inline error (the old snackbar was
  hidden BEHIND the bottom sheet, so failures were invisible). Message is now
  actionable ('check location is on and the permission is granted').
- Provider hardening: request permission first; on permanent denial open app
  settings; if location services are off open location settings; time-limit the
  fix and fall back to last-known position so it doesn't hang indoors.

Analyzer clean; market tests green.
This commit is contained in:
vjrj 2026-07-10 11:09:12 +02:00
parent 7cba4f7fcf
commit 1a81db9bf0
9 changed files with 69 additions and 31 deletions

View file

@ -410,6 +410,8 @@ class _ConfigSheetState extends State<_ConfigSheet> {
final _area = TextEditingController();
final _servers = TextEditingController();
bool _loading = true;
bool _locationBusy = false;
String? _locationError;
@override
void initState() {
@ -433,16 +435,21 @@ class _ConfigSheetState extends State<_ConfigSheet> {
/// geohash so no precise fix is stored.
Future<void> _useLocation() async {
final provider = widget.location;
if (provider == null) return;
final messenger = ScaffoldMessenger.of(context);
final t = context.t;
if (provider == null || _locationBusy) return;
setState(() {
_locationBusy = true;
_locationError = null;
});
final loc = await provider.currentCoarseLatLon();
if (!mounted) return;
if (loc == null) {
messenger.showSnackBar(SnackBar(content: Text(t.market.locationFailed)));
return;
}
setState(() => _area.text = Geohash.encode(loc.lat, loc.lon, precision: 5));
setState(() {
_locationBusy = false;
if (loc == null) {
_locationError = context.t.market.locationFailed;
} else {
_area.text = Geohash.encode(loc.lat, loc.lon, precision: 5);
}
});
}
@override
@ -464,7 +471,8 @@ class _ConfigSheetState extends State<_ConfigSheet> {
),
child: _loading
? const SizedBox(height: 120, child: Center(child: CircularProgressIndicator()))
: Column(
: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
@ -496,9 +504,23 @@ class _ConfigSheetState extends State<_ConfigSheet> {
alignment: AlignmentDirectional.centerStart,
child: TextButton.icon(
key: const Key('market.useLocation'),
icon: const Icon(Icons.my_location, size: 18),
icon: _locationBusy
? const SizedBox(
width: 16,
height: 16,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Icon(Icons.my_location, size: 18),
label: Text(t.market.useLocation),
onPressed: _useLocation,
onPressed: _locationBusy ? null : _useLocation,
),
),
if (_locationError != null)
Padding(
padding: const EdgeInsets.only(top: 4),
child: Text(
_locationError!,
style: const TextStyle(color: Color(0xFFB3261E), fontSize: 12),
),
),
const SizedBox(height: 8),
@ -541,6 +563,7 @@ class _ConfigSheetState extends State<_ConfigSheet> {
),
],
),
),
);
}
}