feat: update login, event detail, theme, and API client

- Improved event detail page with carousel, map, and layout fixes
- Updated login screen with video background and glassmorphism
- API client development mode with mock responses
- Theme manager and main app updates

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 08:57:25 +05:30
parent 4acf75902c
commit e0f34398c2
6 changed files with 662 additions and 268 deletions

View File

@@ -1,6 +1,7 @@
// lib/screens/learn_more_screen.dart
import 'dart:async';
import 'dart:ui';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:share_plus/share_plus.dart';
@@ -222,107 +223,125 @@ class _LearnMoreScreenState extends State<LearnMoreScreen> {
}
final screenHeight = MediaQuery.of(context).size.height;
final imageHeight = screenHeight * 0.50;
final overlap = 30.0;
final imageHeight = screenHeight * 0.45;
final topPadding = MediaQuery.of(context).padding.top;
return Scaffold(
backgroundColor: theme.scaffoldBackgroundColor,
body: Stack(
children: [
// ── LAYER 1: Image carousel (background) ──
_buildImageCarousel(theme, imageHeight),
// ── LAYER 2: Scrollable content with overlapping white card ──
// ── Scrollable content (carousel + card scroll together) ──
SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Transparent spacer — shows the image behind
SizedBox(height: imageHeight - overlap),
// Image carousel (scrolls with content)
_buildImageCarousel(theme, imageHeight),
// White card with rounded top corners overlapping image
Container(
width: double.infinity,
decoration: BoxDecoration(
color: theme.scaffoldBackgroundColor,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(28),
),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 20,
offset: const Offset(0, -6),
// Content card with rounded top corners overlapping carousel
Transform.translate(
offset: const Offset(0, -28),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
color: theme.scaffoldBackgroundColor,
borderRadius: const BorderRadius.vertical(
top: Radius.circular(28),
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTitleSection(theme),
_buildAboutSection(theme),
if (_event!.latitude != null && _event!.longitude != null) ...[
_buildVenueSection(theme),
_buildGetDirectionsButton(theme),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 20,
offset: const Offset(0, -6),
),
],
if (_event!.importantInfo.isNotEmpty)
_buildImportantInfoSection(theme),
if (_event!.importantInfo.isEmpty &&
(_event!.importantInformation ?? '').isNotEmpty)
_buildImportantInfoFallback(theme),
const SizedBox(height: 100),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildTitleSection(theme),
_buildAboutSection(theme),
if (_event!.latitude != null && _event!.longitude != null) ...[
_buildVenueSection(theme),
_buildGetDirectionsButton(theme),
],
if (_event!.importantInfo.isNotEmpty)
_buildImportantInfoSection(theme),
if (_event!.importantInfo.isEmpty &&
(_event!.importantInformation ?? '').isNotEmpty)
_buildImportantInfoFallback(theme),
const SizedBox(height: 100),
],
),
),
),
],
),
),
// ── LAYER 3: Floating icon row (above scrollview so taps work) ──
// ── Fixed top bar with back/share/heart buttons ──
Positioned(
top: MediaQuery.of(context).padding.top + 10,
left: 16,
right: 16,
child: Row(
children: [
_squareIconButton(
icon: Icons.arrow_back,
onTap: () => Navigator.pop(context),
top: 0,
left: 0,
right: 0,
child: Container(
padding: EdgeInsets.only(
top: topPadding + 10,
bottom: 10,
left: 16,
right: 16,
),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black.withOpacity(0.5),
Colors.black.withOpacity(0.0),
],
),
// Pill-shaped page indicators (centered)
Expanded(
child: _imageUrls.length > 1
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(_imageUrls.length, (i) {
final active = i == _currentPage;
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.symmetric(horizontal: 3),
width: active ? 18 : 8,
height: 6,
decoration: BoxDecoration(
color: active
? Colors.white
: Colors.white.withOpacity(0.45),
borderRadius: BorderRadius.circular(3),
),
);
}),
)
: const SizedBox.shrink(),
),
_squareIconButton(
icon: Icons.ios_share_outlined,
onTap: _shareEvent,
),
const SizedBox(width: 10),
_squareIconButton(
icon: _wishlisted ? Icons.favorite : Icons.favorite_border,
iconColor: _wishlisted ? Colors.redAccent : Colors.white,
onTap: () => setState(() => _wishlisted = !_wishlisted),
),
],
),
child: Row(
children: [
_squareIconButton(
icon: Icons.arrow_back,
onTap: () => Navigator.pop(context),
),
// Pill-shaped page indicators (centered)
Expanded(
child: _imageUrls.length > 1
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(_imageUrls.length, (i) {
final active = i == _currentPage;
return AnimatedContainer(
duration: const Duration(milliseconds: 300),
margin: const EdgeInsets.symmetric(horizontal: 3),
width: active ? 18 : 8,
height: 6,
decoration: BoxDecoration(
color: active
? Colors.white
: Colors.white.withOpacity(0.45),
borderRadius: BorderRadius.circular(3),
),
);
}),
)
: const SizedBox.shrink(),
),
_squareIconButton(
icon: Icons.ios_share_outlined,
onTap: _shareEvent,
),
const SizedBox(width: 10),
_squareIconButton(
icon: _wishlisted ? Icons.favorite : Icons.favorite_border,
iconColor: _wishlisted ? Colors.redAccent : Colors.white,
onTap: () => setState(() => _wishlisted = !_wishlisted),
),
],
),
),
),
],
@@ -492,7 +511,7 @@ class _LearnMoreScreenState extends State<LearnMoreScreen> {
);
}
/// Square icon button with rounded corners and translucent white background
/// Square icon button with rounded corners and prominent background
Widget _squareIconButton({
required IconData icon,
required VoidCallback onTap,
@@ -501,12 +520,12 @@ class _LearnMoreScreenState extends State<LearnMoreScreen> {
return GestureDetector(
onTap: onTap,
child: Container(
width: 42,
height: 42,
width: 44,
height: 44,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.2),
color: Colors.black.withOpacity(0.35),
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.white.withOpacity(0.3)),
border: Border.all(color: Colors.white.withOpacity(0.4)),
),
child: Icon(icon, color: iconColor, size: 22),
),
@@ -640,26 +659,66 @@ class _LearnMoreScreenState extends State<LearnMoreScreen> {
height: 280,
child: Stack(
children: [
GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(lat, lng),
zoom: 15,
),
mapType: _mapType,
markers: {
Marker(
markerId: const MarkerId('event'),
position: LatLng(lat, lng),
infoWindow: InfoWindow(title: venueLabel),
// Use static map image on web (Google Maps JS SDK not configured),
// native GoogleMap widget on mobile
if (kIsWeb)
GestureDetector(
onTap: _viewLargerMap,
child: Container(
decoration: BoxDecoration(
color: Colors.grey.shade200,
borderRadius: BorderRadius.circular(20),
),
child: Stack(
children: [
Positioned.fill(
child: Image.network(
'https://maps.googleapis.com/maps/api/staticmap?center=$lat,$lng&zoom=15&size=600x300&markers=color:red%7C$lat,$lng&key=',
fit: BoxFit.cover,
errorBuilder: (_, __, ___) => Container(
color: const Color(0xFFE8EAF6),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.map_outlined, size: 48, color: theme.colorScheme.primary),
const SizedBox(height: 8),
Text(
'Tap to view on Google Maps',
style: TextStyle(
color: theme.colorScheme.primary,
fontWeight: FontWeight.w600,
),
),
],
),
),
),
),
],
),
),
},
myLocationButtonEnabled: false,
zoomControlsEnabled: false,
scrollGesturesEnabled: true,
rotateGesturesEnabled: false,
tiltGesturesEnabled: false,
onMapCreated: (c) => _mapController = c,
),
)
else
GoogleMap(
initialCameraPosition: CameraPosition(
target: LatLng(lat, lng),
zoom: 15,
),
mapType: _mapType,
markers: {
Marker(
markerId: const MarkerId('event'),
position: LatLng(lat, lng),
infoWindow: InfoWindow(title: venueLabel),
),
},
myLocationButtonEnabled: false,
zoomControlsEnabled: false,
scrollGesturesEnabled: true,
rotateGesturesEnabled: false,
tiltGesturesEnabled: false,
onMapCreated: (c) => _mapController = c,
),
// "View larger map" top left
Positioned(
@@ -691,36 +750,38 @@ class _LearnMoreScreenState extends State<LearnMoreScreen> {
),
),
// Map type toggle bottom left
Positioned(
bottom: 12,
left: 12,
child: _mapControlButton(
icon: _mapType == MapType.normal
? Icons.satellite_alt
: Icons.map_outlined,
onTap: () {
setState(() {
_mapType = _mapType == MapType.normal
? MapType.satellite
: MapType.normal;
});
},
// Map type toggle bottom left (native only)
if (!kIsWeb)
Positioned(
bottom: 12,
left: 12,
child: _mapControlButton(
icon: _mapType == MapType.normal
? Icons.satellite_alt
: Icons.map_outlined,
onTap: () {
setState(() {
_mapType = _mapType == MapType.normal
? MapType.satellite
: MapType.normal;
});
},
),
),
),
// Map controls toggle bottom right
Positioned(
bottom: 12,
right: 12,
child: _mapControlButton(
icon: Icons.open_with_rounded,
onTap: () => setState(() => _showMapControls = !_showMapControls),
// Map controls toggle bottom right (native only)
if (!kIsWeb)
Positioned(
bottom: 12,
right: 12,
child: _mapControlButton(
icon: Icons.open_with_rounded,
onTap: () => setState(() => _showMapControls = !_showMapControls),
),
),
),
// Directional pad overlay
if (_showMapControls)
// Directional pad overlay (native only)
if (!kIsWeb && _showMapControls)
Positioned.fill(
child: Container(
decoration: BoxDecoration(
@@ -730,7 +791,6 @@ class _LearnMoreScreenState extends State<LearnMoreScreen> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Top row: Up + Zoom In
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@@ -744,7 +804,6 @@ class _LearnMoreScreenState extends State<LearnMoreScreen> {
],
),
const SizedBox(height: 10),
// Middle row: Left + Right
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
@@ -758,7 +817,6 @@ class _LearnMoreScreenState extends State<LearnMoreScreen> {
],
),
const SizedBox(height: 10),
// Bottom row: Down + Zoom Out + Close
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [