import 'dart:ui'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:go_router/go_router.dart'; import 'package:proto_portal/scraper.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; import '../widgets/faded_scroll_view.dart'; import '../widgets/service_card.dart'; class StationPage extends StatefulWidget { static GoRoute route = GoRoute( path: "/station", builder: (context, state) => StationPage(stationName: state.uri.queryParameters["station"],) ); String? stationName; StationPage({ this.stationName }); @override State createState() => _StationPageState(); } class _StationPageState extends State { GtiStation? _station = null; List? services = null; @override void initState() { // TODO: implement initState super.initState(); if (widget.stationName != null) { setStation(widget.stationName!); } } Future setStation(String stationName) async { if (true) { List results = await GtiAuth.of(context).searchStations(stationName.trim()); if (results.isNotEmpty) { setState(() { _station = results.first; widget.stationName = _station!.label; }); } else { setState(() { widget.stationName = null; _station = null; }); return; } // Get Services. List fetchedServices = await GtiAuth.of(context).getServicesForStation(_station!); setState(() { services = fetchedServices; }); } } Future selectStation(GtiStation station) async { setState(() { _station = station; widget.stationName = station.label; }); // Get Services. List fetchedServices = await GtiAuth.of(context).getServicesForStation(station); setState(() { services = fetchedServices; }); } @override Widget build(BuildContext context) { double topPadding = MediaQuery.of(context).padding.top; double bottomPadding = MediaQuery.of(context).padding.bottom; print("Built page page"); if (_station == null) { return StationSearch( onSelected: (station) { // GoRouter.of(context).go( // "${StationPage.route.path}?station=${station.code}" // ); selectStation(station); print("Selected station: ${station.label}"); }, ); } return _scaffoldBgAndTing( context: context, child: Column( children: [ SizedBox(height: topPadding), Column( children: [ // Container( // height: 40, // width: double.infinity, // alignment: Alignment.center, // child: SvgPicture.asset( // "assets/logo.svg", // alignment: AlignmentGeometry.center, // width: 250, // colorFilter: ColorFilter.mode(Colors.white, BlendMode.srcIn), // ), // ), Container( height: 40, alignment: Alignment.center, child: Text( _station?.label ?? "Unknown Station" ).h4, ) ], ), const SizedBox(height: 4,), // Content Expanded( child: services == null ? Center(child: CircularProgressIndicator()) : services!.isEmpty ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(LucideIcons.trainTrack, size: 48), SizedBox(height: 16), Text("No services available").muted, ], ), ) : FadedScrollView( padding: const EdgeInsets.all(4.0), fadeHeight: 20, easingDistance: 5, child: Padding( padding: EdgeInsets.zero, child: Column( spacing: 8, children: [ for (final service in services!) ServiceCard.fromService( service: service, station: _station, showActions: true, ).withMargin(horizontal: 4), ], ), ), ), ), NavigationBar( padding: EdgeInsets.only( bottom: bottomPadding ), children: [ NavigationItem( child: Icon( LucideIcons.house ), ), NavigationItem( child: Icon( LucideIcons.house ), ), NavigationItem( child: Icon( LucideIcons.house ), ), NavigationItem( child: Icon( LucideIcons.house ), ) ], ) ] ) ); } } class StationSearch extends StatefulWidget{ Function(GtiStation) onSelected; StationSearch({ required this.onSelected }); @override State createState() => _StationSearchState(); } class _StationSearchState extends State { TextEditingController _controller = TextEditingController(); List _results = []; @override Widget build(BuildContext context) { double topPadding = MediaQuery.of(context).padding.top; double bottomPadding = MediaQuery.of(context).padding.bottom; return _scaffoldBgAndTing( context: context, child: Column( children: [ SizedBox(height: topPadding), Expanded( child: SingleChildScrollView( child: Column( children: [ if (_results.isEmpty) Text( "No results" ).bold.large.withPadding(all: 16) else for (final GtiStation station in _results.sublist(0, _results.length-1)) ...[ _stationButton(station), Divider() ], if (_results.isNotEmpty) _stationButton(_results.last) ], ), ) ), Divider( color: Theme.of(context).colorScheme.border, ), OutlinedContainer( borderRadius: BorderRadius.zero, borderWidth: 0, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Type station name" ).small.semiBold, const SizedBox(height: 10,), TextField( controller: _controller, placeholder: Text( "e.g. Redhill" ), onChanged: (value) async { List results = await GtiAuth.of(context).searchStations(value); setState(() { _results = results; }); } ), SizedBox( height: bottomPadding, ) ], ).withPadding(all: 16), ) ], ) ); } Widget _stationButton(GtiStation station) { return Button.ghost( onPressed: () { widget.onSelected(station); }, alignment: Alignment.centerLeft, child: Basic( title: Text( station.label ).h4, ), ).sized(width: double.infinity).withPadding(all: 8); } } Widget _scaffoldBgAndTing({ required BuildContext context, required Widget child, }) { // light/dark mode color Color bgColor = Colors.black; if (Theme.of(context).brightness == Brightness.light) { bgColor = Colors.white; } else { bgColor = Colors.black; } return Scaffold( child: Stack( children: [ // Image goes BEHIND the BackdropFilter Container( decoration: BoxDecoration( image: DecorationImage( image: AssetImage("assets/background-1.jpeg"), fit: BoxFit.cover, ), ), ), // BackdropFilter blurs the image above BackdropFilter( filter: ImageFilter.blur( sigmaY: 10.0, sigmaX: 10.0, ), child: Container( color: bgColor.withOpacity(0.3), // Or add a tint if desired ), ), Positioned.fill( child: Column( children: [ Container( height: 120, decoration: BoxDecoration( gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.black.withOpacity(0.8), Colors.black.withOpacity(0.5), Colors.transparent, ], ), ), ) ], ), ), child, ], ), ); }