import 'package:flutter/material.dart';
/// User-location marker with a simple fallback while the asset loads.
class UserLocationMarker extends StatefulWidget {
const UserLocationMarker({
super.key,
this.size = 58,
});
final double size;
@override
State<UserLocationMarker> createState() => _UserLocationMarkerState();
}
class _UserLocationMarkerState extends State<UserLocationMarker> {
bool _imageReady = false;
bool _precacheStarted = false;
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (_precacheStarted) return;
_precacheStarted = true;
const provider = AssetImage('assets/images/user_location_marker.png');
precacheImage(provider, context).then((_) {
if (mounted) {
setState(() => _imageReady = true);
}
}).catchError((_) {});
}
@override
Widget build(BuildContext context) {
final size = widget.size;
return IgnorePointer(
child: SizedBox.square(
dimension: size,
child: Stack(
alignment: Alignment.center,
clipBehavior: Clip.none,
children: [
_InstantLocationFallback(size: size),
AnimatedOpacity(
opacity: _imageReady ? 1 : 0,
duration: const Duration(milliseconds: 140),
curve: Curves.easeOut,
child: Image.asset(
'assets/images/user_location_marker.png',
width: size,
height: size,
fit: BoxFit.contain,
filterQuality: FilterQuality.medium,
gaplessPlayback: true,
errorBuilder: (context, error, stackTrace) {
return const SizedBox.shrink();
},
),
),
],
),
),
);
}
}
class _InstantLocationFallback extends StatelessWidget {
const _InstantLocationFallback({required this.size});
final double size;
@override
Widget build(BuildContext context) {
final core = size * .28;
final halo = size * .55;
return Stack(
alignment: Alignment.center,
children: [
Container(
width: halo,
height: halo,
decoration: const BoxDecoration(
color: Color(0x33E03868),
shape: BoxShape.circle,
),
),
Container(
width: core,
height: core,
decoration: BoxDecoration(
color: const Color(0xFFE03868),
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 3),
boxShadow: const [
BoxShadow(
color: Color(0x33000000),
blurRadius: 6,
offset: Offset(0, 2),
),
],
),
child: Icon(
Icons.person_rounded,
color: Colors.white,
size: core * .62,
),
),
],
);
}
}

