Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import 'dart:async';
- import 'dart:typed_data';
- import 'package:flutter/cupertino.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:google_maps_flutter/google_maps_flutter.dart';
- import 'package:intl/date_symbol_data_local.dart';
- import 'package:lite_rolling_switch/lite_rolling_switch.dart';
- import 'package:maps_toolkit/maps_toolkit.dart' as mapUtils;
- import 'package:tireservice/analytics/Analytics.dart';
- import 'package:tireservice/dto/TireserviceDto.dart';
- import 'package:tireservice/dto/CityDto.dart';
- import 'package:tireservice/repository/Database.dart';
- import 'package:tireservice/ui/AdsUI.dart';
- import 'package:tireservice/ui/BusinessFilterUI.dart';
- import 'package:tireservice/ui/TireserviceDetailsScreen.dart';
- import 'package:tireservice/ui/CustomColors.dart';
- import 'package:tireservice/ui/FilterUI.dart';
- import 'package:tireservice/ui/SearchAnywhereDelegate.dart';
- import 'package:tireservice/ui/Style.dart';
- import 'package:tireservice/ui/UIComponents.dart';
- import 'package:tireservice/utils/Extensions.dart';
- import 'dart:ui' as ui;
- import 'package:shimmer/shimmer.dart';
- import 'package:location/location.dart';
- import 'package:url_launcher/url_launcher.dart';
- class MapScreen extends StatefulWidget {
- @override
- State<StatefulWidget> createState() => _MapScreenState();
- }
- class _MapScreenState extends State<MapScreen> {
- static const cardHeight = 120.0;
- static const LatLng centerMsk = const LatLng(55.746438, 37.621644);
- static const LatLng centerKzn = const LatLng(55.825397, 49.115336);
- static const double defaultMapZoom = 11.0;
- Set<Marker> _businessMarkers = Set();
- final List<TireserviceDto> _filteredBusiness = List();
- Set<Marker> _markers = Set();
- final Set<Marker> _filteredMarkers = Set();
- List<TireserviceDto> _tireservices = List();
- final List<TireserviceDto> _filteredTireservices = List();
- var _location = new Location()
- ..changeSettings(distanceFilter: 30, interval: 3000);
- final _database = Database();
- PageController _listController = PageController(
- viewportFraction: 0.85,
- );
- LatLng _lastMapPosition = centerKzn;
- double _lastMapZoomLevel = defaultMapZoom;
- String _mapStyle;
- GoogleMapController _googleMapController;
- LocationData _currentLocation;
- StreamSubscription _getTireservicesSubscription;
- StreamSubscription _getCitySubscription;
- String _currentsUserCity;
- CityDto _currentCityDto;
- FilterEngine _filterEngine = FilterEngine();
- String _currentSearchedPromo;
- Marker _myLocation;
- var adsOnMapVisible = false;
- var userAgreedLocationPermission = false;
- var rotation = 0.0;
- bool isBusiness = false;
- @override
- void initState() {
- super.initState();
- initializeDateFormatting("ru_RU");
- rootBundle.loadString('assets/default_map_style.json').then((string) {
- _mapStyle = string;
- print('map is loaded');
- });
- _invalidateCitySelection();
- _restoreState();
- _createMyLocation();
- _location.hasPermission().then((hasPermission) {
- if (hasPermission != null) {
- userAgreedLocationPermission = hasPermission.index!=0?true:false;
- _listenLocationChange();
- }
- });
- }
- @override
- void deactivate() {
- _database.saveUserMapLocation(_lastMapPosition);
- _database.saveUserMapZoomLevel(_lastMapZoomLevel);
- super.deactivate();
- }
- @override
- void dispose() {
- _getTireservicesSubscription?.cancel();
- _getCitySubscription?.cancel();
- super.dispose();
- }
- _listenLocationChange() {
- _location.onLocationChanged().listen((locationData) {
- userAgreedLocationPermission = true;
- mapUtils.LatLng lastKnownLocation;
- if (_currentLocation != null) {
- lastKnownLocation = mapUtils.LatLng(
- _currentLocation.latitude, _currentLocation.longitude);
- }
- _currentLocation = locationData;
- if (_myLocation != null) {
- if (lastKnownLocation != null) {
- final currentLocation = mapUtils.LatLng(
- _currentLocation.latitude, _currentLocation.longitude);
- rotation = mapUtils.SphericalUtil.computeHeading(
- lastKnownLocation, currentLocation);
- }
- _invalidateCarIcon();
- }
- redraw();
- });
- }
- _invalidateCarIcon() {
- if (_myLocation != null && _currentLocation != null) {
- _myLocation = _myLocation.copyWith(
- visibleParam: userAgreedLocationPermission,
- rotationParam: rotation,
- positionParam:
- LatLng(_currentLocation.latitude, _currentLocation.longitude),
- );
- this._filteredMarkers.add(_myLocation);
- redraw();
- }
- }
- _createMyLocation() {
- _createMyLocationMarker().then((marker) {
- this._myLocation = marker;
- _invalidateCarIcon();
- });
- }
- _restoreState() {
- _database.getUserMapZoomLevel().then((zoom) {
- if (zoom != null) {
- _lastMapZoomLevel = zoom;
- redraw();
- }
- });
- _database.getUserMapLocation().then((position) {
- if (position != null) {
- _lastMapPosition = position;
- redraw();
- }
- });
- }
- myCurrentLocation() async {
- try {
- _currentLocation = await _location.getLocation();
- print("locationLatitude: ${_currentLocation.latitude.toString()}");
- print("locationLongitude: ${_currentLocation.longitude.toString()}");
- userAgreedLocationPermission = true;
- _invalidateCarIcon();
- } on PlatformException catch (e) {
- if (e.code == 'PERMISSION_DENIED') {
- String error = 'Permission denied';
- print(error);
- }
- _currentLocation = null;
- }
- }
- Future<Marker> _createMyLocationMarker() async {
- int size = 80;
- final iconAsset = 'assets/icons/car.png';
- final Uint8List markerIcon = await getBytesFromAsset(iconAsset, size);
- return Marker(
- rotation: rotation,
- anchor: Offset(0.5, 0.5),
- markerId: MarkerId("myLocation"),
- position: LatLng(0.0, 0.0),
- zIndex: 2.0,
- visible: false,
- icon: BitmapDescriptor.fromBytes(markerIcon),
- );
- }
- void _onCameraMove(CameraPosition position) {
- _lastMapPosition = position.target;
- _lastMapZoomLevel = position.zoom;
- }
- Future<Uint8List> getBytesFromAsset(String path, int width) async {
- ByteData data = await rootBundle.load(path);
- ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List(),
- targetWidth: width);
- ui.FrameInfo fi = await codec.getNextFrame();
- return (await fi.image.toByteData(format: ui.ImageByteFormat.png))
- .buffer
- .asUint8List();
- }
- Future<Marker> createMarkerFromTireservice(TireserviceDto tireservice) async {
- String iconAsset = TireserviceDtoAssets(tireservice).getIconAsset();
- int size;
- double zIndex = 0.0;
- if (tireservice.isPremium()) {
- size = 40;
- zIndex = 1.0;
- } else if (tireservice.isForTaxi()) {
- size = 40;
- zIndex = 2.0;
- } else if (tireservice.isMobileTireservice()) {
- size = 30;
- } else {
- size = 30;
- }
- final Uint8List markerIcon = await getBytesFromAsset(iconAsset, size);
- var position = tireservice.getLatLng();
- return Marker(
- anchor: Offset(0.5, 0.5),
- markerId: MarkerId(tireservice.tireserviceId),
- position: position,
- zIndex: zIndex,
- icon: BitmapDescriptor.fromBytes(markerIcon),
- onTap: () {
- _performSelectTireserviceInList(tireservice);
- });
- }
- _performSelectTireserviceInList(TireserviceDto tireservice) {
- _listController.animateToPage(
- _getPageForTireservice(tireservice.tireserviceId),
- duration: Duration(milliseconds: 250),
- curve: Curves.fastLinearToSlowEaseIn);
- }
- int _getPageForTireservice(String id) {
- return this
- ._filteredTireservices
- .indexWhere((tireservice) => tireservice.tireserviceId == id);
- }
- void _onMapCreated(GoogleMapController controller) {
- this._googleMapController = controller;
- _googleMapController.setMapStyle(_mapStyle);
- _invalidateCitySelection();
- }
- _fetchTireservices() {
- _getTireservicesSubscription = _database
- .streamTireservicesByCity(_currentsUserCity)
- .asyncMap((documents) {
- return documents.map((document) {
- return TireserviceDto.createFromDocument(document);
- }).where((tireservice) {
- var isPromoSearchValid = true;
- if (_currentSearchedPromo != null) {
- isPromoSearchValid = tireservice.promo == null
- ? false
- : tireservice.promo.contains(_currentSearchedPromo);
- }
- return isPromoSearchValid &&
- _filterEngine.isTireserviceValid(tireservice);
- }).map((tireservice) {
- _tireservices.removeWhere((prevTireservice) {
- return prevTireservice.tireserviceId == tireservice.tireserviceId;
- });
- _tireservices.add(tireservice);
- return createMarkerFromTireservice(tireservice);
- });
- }).map((list) {
- list.toList().removeWhere((value) => value == null);
- return list.toSet();
- }).listen((set) {
- Future.wait(set).then((set) {
- this._markers = set.toSet();
- this._filteredMarkers.clear();
- this._filteredMarkers.addAll(this._markers);
- _invalidateCarIcon();
- this._filteredTireservices.clear();
- this._filteredTireservices.addAll(this._tireservices);
- redraw();
- });
- });
- }
- _fetchCityDto() {
- _getCitySubscription =
- _database.streamCityByName(_currentsUserCity).listen((doc) {
- _currentCityDto = CityDto.createFromDocument(doc);
- redraw();
- this._markers.clear();
- this._filteredMarkers.clear();
- this._tireservices.clear();
- this._filteredTireservices.clear();
- _invalidateCarIcon();
- _fetchTireservices();
- });
- }
- _callPhone(String phone) async {
- if (phone == null || phone.isEmpty) return;
- final validPhone =
- phone.length == 11 ? phone.substring(1) : phone.substring(2);
- final url = "tel:+7$validPhone";
- if (await canLaunch(url)) {
- await launch(url);
- } else {
- throw 'Could not launch $url';
- }
- }
- _invalidateCitySelection() {
- _database.getUserCity().then((userCity) {
- _currentsUserCity = userCity;
- _fetchCityDto();
- });
- }
- _onApplyFilter(FilterEngine filterEngine) {
- this._filterEngine = filterEngine;
- Analytics.applyFilter(filterEngine.filterState);
- _invalidateCitySelection();
- }
- _showSearch() async {
- Analytics.showSearch();
- await showSearch(
- context: context,
- delegate: SearchAnywhereDelegate(_currentsUserCity),
- ).then((SearchResult searchResult) {
- if (searchResult != null) {
- if (searchResult.foundedTireservice != null) {
- _performSelectTireserviceInList(searchResult.foundedTireservice);
- } else if (searchResult.promo != null) {
- _currentSearchedPromo = searchResult.promo;
- _invalidateCitySelection();
- _googleMapController.animateCamera(CameraUpdate.zoomTo(8));
- }
- }
- });
- }
- _showAdsOnMap() async {
- Analytics.showAdsOnMap();
- adsOnMapVisible = true;
- redraw();
- }
- _hideAdsOnMap() {
- adsOnMapVisible = false;
- redraw();
- }
- CameraTargetBounds _getCurrentBounds() {
- return CameraTargetBounds(
- LatLngBounds(
- southwest: _currentCityDto?.getSouthWest(),
- northeast: _currentCityDto?.getNorthEast()),
- );
- }
- MinMaxZoomPreference _getZoomPreference() {
- return MinMaxZoomPreference(
- _currentCityDto.minZoom.toDouble(), _currentCityDto.maxZoom.toDouble());
- }
- void redraw() {
- this.setState(() {});
- }
- void _showFiltersTireServices(context) {
- Analytics.showFilter();
- showModalBottomSheet(
- isScrollControlled: true,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.vertical(top: Radius.circular(8))),
- context: context,
- builder: (BuildContext bc) {
- return FilterUI(
- _filterEngine, _invalidateCitySelection, _onApplyFilter);
- },
- );
- }
- void _showBusinessTireServices(context) {
- Analytics.showBusiness();
- showModalBottomSheet(
- isScrollControlled: true,
- shape: RoundedRectangleBorder(
- borderRadius: BorderRadius.vertical(top: Radius.circular(8))),
- context: context,
- builder: (BuildContext bc) {
- return BusinessUI(_invalidateCitySelection);
- },
- );
- }
- void _setStatusBarToWhiteText() {
- SystemChrome.setSystemUIOverlayStyle(
- SystemUiOverlayStyle(statusBarBrightness: Brightness.dark));
- }
- @override
- Widget build(BuildContext context) {
- _setStatusBarToWhiteText();
- return MaterialApp(
- home: Scaffold(
- body: Stack(
- children: <Widget>[
- map(),
- mapControls(),
- searchText(),
- search(),
- isBusiness?Container():tireserviceList(),
- ],
- ),
- ),
- );
- }
- Widget map() {
- return GoogleMap(
- myLocationButtonEnabled: false,
- compassEnabled: false,
- zoomControlsEnabled: false,
- mapToolbarEnabled: false,
- cameraTargetBounds: _currentCityDto == null
- ? CameraTargetBounds.unbounded
- : _getCurrentBounds(),
- minMaxZoomPreference: _currentCityDto == null
- ? MinMaxZoomPreference.unbounded
- : _getZoomPreference(),
- markers: isBusiness?null:_filteredMarkers,
- onMapCreated: _onMapCreated,
- onCameraMove: _onCameraMove,
- initialCameraPosition: CameraPosition(
- target: _lastMapPosition,
- zoom: _lastMapZoomLevel,
- ),
- );
- }
- Widget search() {
- return Padding(
- padding: const EdgeInsets.fromLTRB(16, 50, 16, cardHeight),
- child: Align(
- alignment: Alignment.topLeft,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: <Widget>[
- FloatingActionButton(
- mini: true,
- onPressed: _showSearch,
- materialTapTargetSize: MaterialTapTargetSize.padded,
- backgroundColor: Colors.white,
- child: const Icon(
- Icons.search,
- size: 20.0,
- color: Colors.black87,
- ),
- ),
- adsMapArea(),
- ],
- ),
- ),
- );
- }
- Widget searchText() {
- if (_currentSearchedPromo == null) return Container();
- return Padding(
- padding: const EdgeInsets.fromLTRB(20, 54, 16, 0),
- child: Align(
- alignment: Alignment.topLeft,
- child: Container(
- decoration: BoxDecoration(
- color: Colors.white,
- borderRadius: BorderRadius.circular(20),
- ),
- padding: EdgeInsets.fromLTRB(46, 0, 16, 0),
- height: 40,
- width: double.infinity,
- child: Row(
- children: <Widget>[
- Expanded(
- child: Text54Black14(_currentSearchedPromo),
- ),
- InkWell(
- onTap: () {
- _currentSearchedPromo = null;
- _invalidateCitySelection();
- },
- child: Icon(Icons.close, size: 20.0, color: Colors.black87),
- )
- ],
- ),
- ),
- ),
- );
- }
- Widget mapControls() {
- return Align(
- alignment: FractionalOffset.centerRight,
- child: Column(
- children: <Widget>[
- Spacer(),
- roundButton(Icons.add, () {
- _googleMapController.animateCamera(CameraUpdate.zoomIn());
- }, Colors.white),
- roundButton(Icons.remove, () {
- _googleMapController.animateCamera(CameraUpdate.zoomOut());
- }, Colors.white),
- roundButton(Icons.my_location, () async {
- await myCurrentLocation();
- _googleMapController.animateCamera(CameraUpdate.newLatLng(
- LatLng(_currentLocation.latitude, _currentLocation.longitude)));
- }, Colors.white),
- roundButton(
- Icons.filter_list,
- () => isBusiness?_showBusinessTireServices(context):_showFiltersTireServices(context),
- Colors.white //showFilters(context),
- ),
- roundButton(Icons.business_center, () {
- setState(() {
- isBusiness = !isBusiness;
- });
- }, isBusiness?Colors.blue:Colors.white),
- Spacer(),
- ],
- ),
- );
- }
- // Widget mapControlsAdsButton() {
- // return Align(
- // alignment: FractionalOffset.centerLeft,
- // child: Column(
- // children: <Widget>[
- // Spacer(),
- // roundButton(Icons.add, () {
- // _googleMapController.animateCamera(CameraUpdate.zoomIn());
- // }),
- // ],
- // ),
- // );
- // }
- Widget roundButton(IconData icon, Function onTap, Color color) {
- return Padding(
- padding: EdgeInsets.fromLTRB(0, 8, 8, 0),
- child: Material(
- color: Colors.white,
- borderRadius: new BorderRadius.circular(20.0),
- child: InkWell(
- borderRadius: new BorderRadius.circular(20.0),
- onTap: onTap,
- child: Container(
- child: ClipRRect(
- borderRadius: new BorderRadius.circular(20.0),
- child: Container(
- decoration: BoxDecoration(color: color),
- width: 36,
- height: 36,
- child: Icon(
- icon,
- size: 20,
- ),
- )),
- ),
- ),
- ),
- );
- }
- Widget tireserviceList() {
- return Align(
- alignment: Alignment.bottomLeft,
- child: SafeArea(
- child: Container(
- height: 140,
- child: PageView.builder(
- controller: _listController,
- onPageChanged: (index) {
- var tireservice = _filteredTireservices[index];
- _googleMapController.animateCamera(
- CameraUpdate.newLatLng(tireservice.getLatLng()),
- );
- },
- scrollDirection: Axis.horizontal,
- itemCount: _filteredTireservices.length,
- itemBuilder: (context, index) => Padding(
- padding: EdgeInsets.all(8.0),
- child: _tireserviceCard(_filteredTireservices[index]),
- ),
- ),
- ),
- ),
- );
- }
- Widget businessList() {
- return Align(
- alignment: Alignment.bottomLeft,
- child: SafeArea(
- child: Container(
- height: 140,
- child: PageView.builder(
- controller: _listController,
- onPageChanged: (index) {
- var tireservice = _filteredTireservices[index];
- _googleMapController.animateCamera(
- CameraUpdate.newLatLng(tireservice.getLatLng()),
- );
- },
- scrollDirection: Axis.horizontal,
- itemCount: _filteredTireservices.length,
- itemBuilder: (context, index) => Padding(
- padding: EdgeInsets.all(8.0),
- child: _tireserviceCard(_filteredTireservices[index]),
- ),
- ),
- ),
- ),
- );
- }
- Widget _tireserviceCard(TireserviceDto info) {
- return InkWell(
- onTap: () {
- Navigator.push(
- context,
- _createRoute(info),
- );
- },
- child: Container(
- child: new FittedBox(
- child: Material(
- color: Colors.white,
- elevation: 2.0,
- borderRadius: BorderRadius.circular(24.0),
- shadowColor: Color(0x802196F3),
- child: Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: <Widget>[
- const SizedBox(
- width: 8,
- ),
- Container(
- width: cardHeight,
- height: cardHeight,
- child: ClipRRect(
- borderRadius: new BorderRadius.circular(20.0),
- child: Container(
- decoration: new BoxDecoration(color: Colors.grey[200]),
- child: Hero(
- tag: "tireserviceImage${info.tireserviceId}",
- child: Image(
- fit: BoxFit.fitHeight,
- image: NetworkImage(info.picturesURLs.first),
- loadingBuilder: (BuildContext context, Widget child,
- ImageChunkEvent loadingProgress) {
- if (loadingProgress == null) return child;
- return Shimmer.fromColors(
- child: Container(
- child: Icon(
- Icons.image,
- size: cardHeight,
- ),
- ),
- baseColor: Colors.grey[300],
- highlightColor: Colors.white,
- period: const Duration(milliseconds: 500),
- );
- },
- ),
- ),
- ),
- ),
- ),
- Container(
- child: Padding(
- padding: const EdgeInsets.all(8.0),
- child: Container(
- width: 200,
- height: cardHeight,
- child: Padding(
- padding: const EdgeInsets.fromLTRB(4, 4, 4, 0),
- child: Column(
- children: <Widget>[
- Expanded(
- child: Align(
- alignment: Alignment.centerLeft,
- child: TextMediumBlack16(info.title),
- ),
- ),
- Padding(
- padding:
- const EdgeInsets.fromLTRB(0, 2, 0, 0),
- child: Row(
- children: <Widget>[
- Text54Black14(
- "${info.getRatingString()}"),
- RatingBarReadOnlySmall(
- rating: info.rating),
- ],
- ),
- ),
- Padding(
- padding:
- const EdgeInsets.fromLTRB(0, 6, 0, 0),
- child: Row(
- children: <Widget>[
- Text54Black12("Время работы: "),
- Text54Black12(info.getTimeWorking()),
- ],
- ),
- ),
- Spacer(),
- Row(
- mainAxisSize: MainAxisSize.min,
- children: <Widget>[
- // Spacer(),
- CallButtonSmall(
- onPressed: () {
- Analytics.logOnCallBtnTappedFromMap(
- info);
- _callPhone(info.phone);
- },
- ),
- Spacer(),
- // ToNavigationButtonSmall(
- // onPressed: () {},
- // ),
- Spacer(),
- ],
- )
- ],
- ),
- )),
- ),
- ),
- ],
- )),
- ),
- ),
- );
- }
- Widget adsMapArea() {
- final stack = List<Widget>();
- if (adsOnMapVisible) {
- stack.add(Card(
- color: Colors.white,
- shape:
- RoundedRectangleBorder(borderRadius: BorderRadius.circular(20.0)),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.end,
- children: <Widget>[
- InkWell(
- onTap: _hideAdsOnMap,
- child: Padding(
- padding: EdgeInsets.all(12),
- child: Icon(
- Icons.close,
- size: 20,
- ),
- ),
- ),
- AdsUI(_currentsUserCity, AdCallSource.map),
- ],
- ),
- ));
- stack.add(
- Align(
- alignment: Alignment.center,
- child: Padding(
- padding: EdgeInsets.all(16),
- child: TextMediumBlack16("Скидки от партнёров!"),
- )),
- );
- }
- stack.add(FloatingActionButton(
- heroTag: "adsFloating",
- mini: true,
- onPressed: _showAdsOnMap,
- materialTapTargetSize: MaterialTapTargetSize.padded,
- backgroundColor: CustomColors.yellow,
- child: const Icon(
- Icons.card_giftcard,
- size: 20.0,
- color: Colors.black87,
- ),
- ));
- return Stack(children: stack);
- }
- Route _createRoute(TireserviceDto info) {
- return PageRouteBuilder(
- pageBuilder: (context, animation, secondaryAnimation) =>
- TireserviceDetailsScreen(tireservice: info),
- transitionsBuilder: (context, animation, secondaryAnimation, child) {
- var begin = Offset(0.0, 1.0);
- var end = Offset.zero;
- var curve = Curves.ease;
- var tween =
- Tween(begin: begin, end: end).chain(CurveTween(curve: curve));
- return SlideTransition(
- position: animation.drive(tween),
- child: child,
- );
- },
- settings: RouteSettings(name: 'TireserviceDetailsScreen'),
- );
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement