Update default location to Thrissur and remove Whitefield, Bengaluru

This commit is contained in:
Rishad7594
2026-04-07 20:49:40 +05:30
parent 685c6755d8
commit 7bc396bdde
11 changed files with 944 additions and 284 deletions

View File

@@ -93,7 +93,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
setState(() => _loading = true);
final prefs = await SharedPreferences.getInstance();
_username = prefs.getString('display_name') ?? prefs.getString('username') ?? '';
final storedLocation = prefs.getString('location') ?? 'Whitefield, Bengaluru';
final storedLocation = prefs.getString('location') ?? 'Thrissur';
// Fix legacy lat,lng strings saved before the reverse-geocoding fix
final coordMatch = RegExp(r'^(-?\d+\.?\d*),\s*(-?\d+\.?\d*)$').firstMatch(storedLocation);
if (coordMatch != null) {
@@ -467,7 +467,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
onTap: () {
Navigator.of(context).pop();
if (ev.id != null) {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => LearnMoreScreen(eventId: ev.id, initialEvent: ev)));
Navigator.of(context).push(MaterialPageRoute(builder: (_) => LearnMoreScreen(eventId: ev.id, initialEvent: ev, heroTag: 'event-hero-${ev.id}-search')));
}
},
);
@@ -919,7 +919,7 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
onTap: () {
Navigator.of(context).pop();
if (ev.id != null) {
Navigator.of(context).push(MaterialPageRoute(builder: (_) => LearnMoreScreen(eventId: ev.id, initialEvent: ev)));
Navigator.of(context).push(MaterialPageRoute(builder: (_) => LearnMoreScreen(eventId: ev.id, initialEvent: ev, heroTag: 'event-hero-${ev.id}-sheet')));
}
},
child: Container(
@@ -1197,129 +1197,181 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
);
}
/// Returns the image URL for a given event (for blurred bg).
String? _getEventImageUrl(EventModel event) {
if (event.thumbImg != null && event.thumbImg!.isNotEmpty) return event.thumbImg;
if (event.images.isNotEmpty && event.images.first.image.isNotEmpty) return event.images.first.image;
return null;
}
Widget _buildHeroSection() {
return SafeArea(
bottom: false,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Top bar: location pill + search button
Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: _openLocationSearch,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(25),
border: Border.all(color: Colors.white.withOpacity(0.2)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.location_on_outlined, color: Colors.white, size: 18),
const SizedBox(width: 6),
Text(
_location.length > 20 ? '${_location.substring(0, 20)}...' : _location,
style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500),
child: ValueListenableBuilder<int>(
valueListenable: _heroPageNotifier,
builder: (context, currentPage, _) {
final currentImg = _heroEvents.isNotEmpty ? _getEventImageUrl(_heroEvents[currentPage.clamp(0, _heroEvents.length - 1)]) : null;
return Stack(
children: [
// ── Blurred background image layer ──
if (currentImg != null && currentImg.isNotEmpty)
Positioned.fill(
child: ClipRect(
child: AnimatedSwitcher(
duration: const Duration(milliseconds: 500),
child: CachedNetworkImage(
key: ValueKey(currentImg),
imageUrl: currentImg,
memCacheWidth: 200,
memCacheHeight: 200,
fit: BoxFit.cover,
placeholder: (_, __) => const SizedBox.shrink(),
errorWidget: (_, __, ___) => const SizedBox.shrink(),
imageBuilder: (context, imageProvider) => Stack(
fit: StackFit.expand,
children: [
ImageFiltered(
imageFilter: ImageFilter.blur(sigmaX: 30, sigmaY: 30),
child: Image(
image: imageProvider,
fit: BoxFit.cover,
),
),
Container(
color: Colors.black.withOpacity(0.35),
),
],
),
const SizedBox(width: 4),
const Icon(Icons.keyboard_arrow_down, color: Colors.white, size: 18),
],
),
),
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
const NotificationBell(),
const SizedBox(width: 8),
GestureDetector(
onTap: _openEventSearch,
child: Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
shape: BoxShape.circle,
border: Border.all(color: Colors.white.withOpacity(0.2)),
),
child: const Icon(Icons.search, color: Colors.white, size: 24),
),
),
],
),
],
),
),
const SizedBox(height: 24),
// Featured carousel
_heroEvents.isEmpty
? _loading
? const Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: SizedBox(
height: 320,
child: _HeroShimmer(),
),
)
: const SizedBox(
height: 280,
child: Center(
child: Text('No events available',
style: TextStyle(color: Colors.white70)),
),
)
: Column(
children: [
RepaintBoundary(
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onPanDown: (_) => _autoScrollTimer?.cancel(),
onPanEnd: (_) => _startAutoScroll(delay: const Duration(seconds: 3)),
onPanCancel: () => _startAutoScroll(delay: const Duration(seconds: 3)),
child: SizedBox(
height: 320,
child: PageView.builder(
controller: _heroPageController,
onPageChanged: (page) {
_heroPageNotifier.value = page;
// 8s delay after manual swipe for full read time
_startAutoScroll(delay: const Duration(seconds: 8));
},
itemCount: _heroEvents.length,
itemBuilder: (context, index) {
// Scale animation: active card = 1.0, adjacent = 0.94
return AnimatedBuilder(
animation: _heroPageController,
builder: (context, child) {
double scale = index == _heroPageNotifier.value ? 1.0 : 0.94;
if (_heroPageController.position.haveDimensions) {
scale = (1.0 -
(_heroPageController.page! - index).abs() * 0.06)
.clamp(0.94, 1.0);
}
return Transform.scale(scale: scale, child: child);
},
child: _buildHeroEventImage(_heroEvents[index]),
);
},
// ── Foreground content ──
Column(
mainAxisSize: MainAxisSize.min,
children: [
// Top bar: location pill + search button
Padding(
padding: const EdgeInsets.fromLTRB(16, 12, 16, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: _openLocationSearch,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
borderRadius: BorderRadius.circular(25),
border: Border.all(color: Colors.white.withOpacity(0.2)),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
const Icon(Icons.location_on_outlined, color: Colors.white, size: 18),
const SizedBox(width: 6),
Text(
_location.length > 20 ? '${_location.substring(0, 20)}...' : _location,
style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w500),
),
const SizedBox(width: 4),
const Icon(Icons.keyboard_arrow_down, color: Colors.white, size: 18),
],
),
),
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
const NotificationBell(),
const SizedBox(width: 8),
GestureDetector(
onTap: _openEventSearch,
child: Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.15),
shape: BoxShape.circle,
border: Border.all(color: Colors.white.withOpacity(0.2)),
),
child: const Icon(Icons.search, color: Colors.white, size: 24),
),
),
],
),
],
),
const SizedBox(height: 16),
// Pagination dots
_buildCarouselDots(),
],
),
const SizedBox(height: 24),
],
),
const SizedBox(height: 24),
// Featured carousel
_heroEvents.isEmpty
? _loading
? const Padding(
padding: EdgeInsets.symmetric(horizontal: 8),
child: SizedBox(
height: 320,
child: _HeroShimmer(),
),
)
: const SizedBox(
height: 280,
child: Center(
child: Text('No events available',
style: TextStyle(color: Colors.white70)),
),
)
: Column(
children: [
RepaintBoundary(
child: GestureDetector(
behavior: HitTestBehavior.translucent,
onPanDown: (_) => _autoScrollTimer?.cancel(),
onPanEnd: (_) => _startAutoScroll(delay: const Duration(seconds: 3)),
onPanCancel: () => _startAutoScroll(delay: const Duration(seconds: 3)),
child: SizedBox(
height: 320,
child: PageView.builder(
controller: _heroPageController,
onPageChanged: (page) {
_heroPageNotifier.value = page;
// 8s delay after manual swipe for full read time
_startAutoScroll(delay: const Duration(seconds: 8));
},
itemCount: _heroEvents.length,
itemBuilder: (context, index) {
// Scale animation: active card = 1.0, adjacent = 0.94
return AnimatedBuilder(
animation: _heroPageController,
builder: (context, child) {
double scale = index == _heroPageNotifier.value ? 1.0 : 0.94;
if (_heroPageController.position.haveDimensions) {
scale = (1.0 -
(_heroPageController.page! - index).abs() * 0.06)
.clamp(0.94, 1.0);
}
return Transform.scale(scale: scale, child: child);
},
child: _buildHeroEventImage(_heroEvents[index]),
);
},
),
),
),
),
const SizedBox(height: 16),
// Pagination dots
_buildCarouselDots(),
],
),
const SizedBox(height: 24),
],
),
],
);
},
),
);
}
@@ -1390,13 +1442,13 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
'source': 'hero_carousel',
});
Navigator.push(context,
MaterialPageRoute(builder: (_) => LearnMoreScreen(eventId: event.id, initialEvent: event)));
MaterialPageRoute(builder: (_) => LearnMoreScreen(eventId: event.id, initialEvent: event, heroTag: 'event-hero-${event.id}-carousel')));
}
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Hero(
tag: 'event-hero-${event.id}',
tag: 'event-hero-${event.id}-carousel',
child: ClipRRect(
borderRadius: BorderRadius.circular(radius),
child: Stack(
@@ -1815,11 +1867,11 @@ class _HomeScreenState extends State<HomeScreen> with SingleTickerProviderStateM
return GestureDetector(
onTap: () {
if (event.id != null) {
Navigator.push(context, MaterialPageRoute(builder: (_) => LearnMoreScreen(eventId: event.id, initialEvent: event)));
Navigator.push(context, MaterialPageRoute(builder: (_) => LearnMoreScreen(eventId: event.id, initialEvent: event, heroTag: 'event-hero-${event.id}-top')));
}
},
child: Hero(
tag: 'event-hero-${event.id}',
tag: 'event-hero-${event.id}-top',
child: Container(
width: 150,
decoration: BoxDecoration(