rsflutterapp

Bahnhofsfoto-App for Android and iOS using Flutter
git clone git://archive.git.mtrnord.blog/MTRNord/rsflutterapp.git
Log | Files | Refs | README

commit c4fee15580f1ed09dc2f315ef909dce5c62d0f2e
parent 089fc59d6c9c82605949faf173dda890e10ef095
Author: Marcel <mtrnord1@gmail.com>
Date:   Sun, 23 Feb 2020 16:47:41 +0100

Add Bahnhof-Details

Took 1 hour 17 minutes

Diffstat:
Mlib/main.dart | 34++++++----------------------------
Mlib/models/stations.dart | 29++++++++++++++++++++++++++++-
Alib/views/DetailsPage.dart | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/views/HomePage.dart | 23+++++++++++++++++++++--
Mlib/views/tabs/MapTab.dart | 24++++++++++++++++++++++--
Mpubspec.lock | 49++++++++++++++++++++++++++++++++++++++++++++++++-
Mpubspec.yaml | 2++
7 files changed, 223 insertions(+), 34 deletions(-)

diff --git a/lib/main.dart b/lib/main.dart @@ -2,14 +2,10 @@ import 'package:bloc/bloc.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:http/http.dart' as http; import 'blocs/AuthenticationBloc.dart'; -import 'blocs/MapBloc.dart'; import 'components/LoadingIndicator.dart'; import 'events/AuthenticationEvent.dart'; -import 'repos/RailwayStationsApiClient.dart'; -import 'repos/RailwayStationsRepository.dart'; import 'repos/UserRepository.dart'; import 'states/AuthenticationState.dart'; import 'views/HomePage.dart'; @@ -39,13 +35,6 @@ void main() { BlocSupervisor.delegate = SimpleBlocDelegate(); final userRepository = UserRepository(); - final RailwayStationsRepository railwayStationsRepository = - RailwayStationsRepository( - railwayStationsApiClient: RailwayStationsApiClient( - httpClient: http.Client(), - ), - ); - runApp( BlocProvider<AuthenticationBloc>( create: (context) { @@ -54,7 +43,6 @@ void main() { }, child: App( userRepository: userRepository, - railwayStationsRepository: railwayStationsRepository, ), ), ); @@ -62,13 +50,11 @@ void main() { class App extends StatelessWidget { final UserRepository userRepository; - final RailwayStationsRepository railwayStationsRepository; - App( - {Key key, - @required this.userRepository, - @required this.railwayStationsRepository}) - : super(key: key); + App({ + Key key, + @required this.userRepository, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -84,18 +70,10 @@ class App extends StatelessWidget { return SplashPage(); } if (state is AuthenticationAuthenticated) { - return BlocProvider( - create: (context) => - MapBloc(railwayStationsRepository: railwayStationsRepository), - child: HomePage(), - ); + return HomePage(); } if (state is AuthenticationNeeded) { - return BlocProvider( - create: (context) => - MapBloc(railwayStationsRepository: railwayStationsRepository), - child: HomePage(), - ); + return HomePage(); } if (state is AuthenticationLoading) { return LoadingIndicator(); diff --git a/lib/models/stations.dart b/lib/models/stations.dart @@ -8,8 +8,14 @@ class Station extends Equatable { final String title; final double lat; final double lon; - + final String photographer; + final String photographerUrl; + final String photoUrl; + final String license; + final String licenseUrl; + final int createdAt; final bool active; + final String ds100; Station({ @required this.country, @@ -18,7 +24,14 @@ class Station extends Equatable { @required this.title, @required this.lat, @required this.lon, + @required this.photographer, + @required this.photographerUrl, + @required this.photoUrl, + @required this.license, + @required this.licenseUrl, + @required this.createdAt, @required this.active, + @required this.ds100, }); @override @@ -29,7 +42,14 @@ class Station extends Equatable { title, lat, lon, + photographer, + photographerUrl, + photoUrl, + license, + licenseUrl, + createdAt, active, + ds100, ]; static Station fromJson(dynamic json) { @@ -40,7 +60,14 @@ class Station extends Equatable { title: json["title"] ?? "", lat: json["lat"] ?? null, lon: json["lon"] ?? null, + photographer: json["photographer"] ?? "", + photographerUrl: json["photographerUrl"] ?? "", + photoUrl: json["photoUrl"] ?? "", + license: json["license"] ?? "", + licenseUrl: json["licenseUrl"] ?? "", + createdAt: json["createdAt"] ?? null, active: json["active"] ?? false, + ds100: json["DS100"] ?? "", ); } } diff --git a/lib/views/DetailsPage.dart b/lib/views/DetailsPage.dart @@ -0,0 +1,96 @@ +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:maps_launcher/maps_launcher.dart'; +import 'package:url_launcher/url_launcher.dart'; + +import '../models/models.dart'; +import '../repos/repositories.dart'; + +class DetailsPage extends StatelessWidget { + final Station station; + final RailwayStationsRepository railwayStationsRepository; + + const DetailsPage( + {Key key, + @required this.station, + @required this.railwayStationsRepository}) + : assert(railwayStationsRepository != null), + super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text("Bahnhofs-Details"), + actions: <Widget>[ + IconButton( + icon: Icon(Icons.directions), + onPressed: () { + MapsLauncher.launchCoordinates(station.lat, station.lon); + }, + ), + IconButton( + icon: Icon(Icons.list), + onPressed: () async { + Country country = (await railwayStationsRepository.getCountries()) + .firstWhere((element) => element.code == station.country); + launch(country.timetableUrlTemplate + .replaceAll("{id}", station.idStr) + .replaceAll("{title}", station.title) + .replaceAll("{DS100}", station.ds100)); + }, + ), + ], + ), + body: Column( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: <Widget>[ + Text( + station.title, + style: Theme.of(context) + .primaryTextTheme + .headline3 + .copyWith(color: Colors.black), + ), + Image.network(station.photoUrl), + Padding( + padding: EdgeInsets.all(8), + child: Row( + children: <Widget>[ + Text("Urheber: "), + RichText( + text: TextSpan( + text: station.photographer, + style: TextStyle( + color: Theme.of(context).primaryColor, + decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + launch(station.photographerUrl); + }, + ), + ), + Text(", "), + RichText( + text: TextSpan( + text: station.license, + style: TextStyle( + color: Theme.of(context).primaryColor, + decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + launch(station.licenseUrl); + }, + ), + ), + ], + ), + ), + ], + ), + ); + } +} diff --git a/lib/views/HomePage.dart b/lib/views/HomePage.dart @@ -1,13 +1,24 @@ -// TODO fix placeholder import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:http/http.dart' as http; +import '../blocs/MapBloc.dart'; +import '../repos/RailwayStationsApiClient.dart'; +import '../repos/repositories.dart'; import 'tabs/MapTab.dart'; import 'tabs/SettingsTab.dart'; class HomePage extends StatelessWidget { @override Widget build(BuildContext context) { + final RailwayStationsRepository railwayStationsRepository = + RailwayStationsRepository( + railwayStationsApiClient: RailwayStationsApiClient( + httpClient: http.Client(), + ), + ); + return DefaultTabController( length: 3, child: Scaffold( @@ -36,9 +47,17 @@ class HomePage extends StatelessWidget { ), ), body: TabBarView( + physics: NeverScrollableScrollPhysics(), children: [ Scaffold( - body: MapTab(), + body: BlocProvider( + create: (context) => MapBloc( + railwayStationsRepository: railwayStationsRepository, + ), + child: MapTab( + railwayStationsRepository: railwayStationsRepository, + ), + ), ), Scaffold( body: Container(), diff --git a/lib/views/tabs/MapTab.dart b/lib/views/tabs/MapTab.dart @@ -1,4 +1,3 @@ -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_map/flutter_map.dart'; @@ -8,9 +7,16 @@ import 'package:latlong/latlong.dart'; import '../../blocs/MapBloc.dart'; import '../../events/MapEvent.dart'; import '../../models/models.dart'; +import '../../repos/repositories.dart'; import '../../states/MapState.dart'; +import '../DetailsPage.dart'; class MapTab extends StatefulWidget { + final RailwayStationsRepository railwayStationsRepository; + + MapTab({@required this.railwayStationsRepository}) + : assert(railwayStationsRepository != null); + @override MapTabState createState() => MapTabState(); } @@ -104,11 +110,25 @@ class MapTabState extends State<MapTab> color: Colors.black12, borderStrokeWidth: 3, ), + onMarkerTap: (Marker marker) { + Station station = stations.firstWhere((element) => + element.lat == marker.point.latitude && + element.lon == marker.point.longitude); + Navigator.of(context).push( + MaterialPageRoute( + builder: (BuildContext context) => DetailsPage( + station: station, + railwayStationsRepository: + widget.railwayStationsRepository, + ), + ), + ); + }, builder: (context, markers) { return ClipRRect( borderRadius: BorderRadius.circular(50), child: Container( - color: CupertinoTheme.of(context).primaryColor, + color: Theme.of(context).primaryColor, child: Center( child: Text( markers.length.toString(), diff --git a/pubspec.lock b/pubspec.lock @@ -184,6 +184,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" fuchsia_remote_debug_protocol: dependency: transitive description: flutter @@ -273,6 +278,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.11.4" + maps_launcher: + dependency: "direct main" + description: + name: maps_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "1.2.0" matcher: dependency: transitive description: @@ -378,6 +390,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" pool: dependency: transitive description: @@ -579,6 +598,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.6" + url_launcher: + dependency: "direct main" + description: + name: url_launcher + url: "https://pub.dartlang.org" + source: hosted + version: "5.4.2" + url_launcher_macos: + dependency: transitive + description: + name: url_launcher_macos + url: "https://pub.dartlang.org" + source: hosted + version: "0.0.1+4" + url_launcher_platform_interface: + dependency: transitive + description: + name: url_launcher_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.6" + url_launcher_web: + dependency: transitive + description: + name: url_launcher_web + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.1+1" uuid: dependency: transitive description: @@ -651,4 +698,4 @@ packages: version: "2.2.0" sdks: dart: ">=2.6.0 <3.0.0" - flutter: ">=1.12.1 <2.0.0" + flutter: ">=1.12.8 <2.0.0" diff --git a/pubspec.yaml b/pubspec.yaml @@ -31,6 +31,8 @@ dependencies: flutter_map: ^0.8.2 latlong: ^0.6.1 flutter_map_marker_cluster: ^0.2.7 + url_launcher: ^5.4.2 + maps_launcher: ^1.2.0 settings_ui: ^0.2.0