moisey312

TireserviceDetailsScreen

Sep 16th, 2020
797
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import 'dart:async';
  2.  
  3. import 'package:expandable/expandable.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter/services.dart';
  6. import 'package:flutter_svg/flutter_svg.dart';
  7. import 'package:qr_flutter/qr_flutter.dart';
  8. import 'package:tireservice/analytics/Analytics.dart';
  9. import 'package:tireservice/dto/AdsDto.dart';
  10. import 'package:tireservice/dto/TireserviceComment.dart';
  11. import 'package:tireservice/dto/TireserviceDto.dart';
  12. import 'package:tireservice/repository/Database.dart';
  13. import 'package:tireservice/ui/AdsUI.dart';
  14. import 'package:tireservice/ui/ImageViewerUI.dart';
  15. import 'package:tireservice/ui/ReviewSendingScreen.dart';
  16. import 'package:tireservice/ui/Style.dart';
  17. import 'package:tireservice/ui/UIComponents.dart';
  18. import 'package:shimmer/shimmer.dart';
  19. import 'package:tireservice/utils/Utils.dart';
  20. import 'package:url_launcher/url_launcher.dart';
  21.  
  22. class TireserviceDetailsScreen extends StatefulWidget {
  23.   final TireserviceDto tireservice;
  24.  
  25.   const TireserviceDetailsScreen({Key key, @required this.tireservice})
  26.       : super(key: key);
  27.  
  28.   @override
  29.   State<StatefulWidget> createState() => _TireserviceDetailsScreenState();
  30. }
  31.  
  32. class _TireserviceDetailsScreenState extends State<TireserviceDetailsScreen> {
  33.   static const callButtonContainerHeight = 72.0;
  34.   static const adsHeaderHeight = 50.0;
  35.   final _database = Database();
  36.   List<TireserviceComment> _comments = [];
  37.   var commentsLoaded = false;
  38.   StreamSubscription _getCommentsSubscription;
  39.   StreamSubscription _getTireserviceSubscription;
  40.   TireserviceDto _actualTireserviceInfo;
  41.   bool isAdsMustShow = false;
  42.   static const platform = const MethodChannel('maps');
  43.   @override
  44.   void initState() {
  45.     super.initState();
  46.     _actualTireserviceInfo = widget.tireservice;
  47.     _fetchTireservice();
  48.     _fetchComments();
  49.     _fetchAds();
  50.  
  51.   }
  52.  
  53.   @override
  54.   void dispose() {
  55.     super.dispose();
  56.     _getCommentsSubscription?.cancel();
  57.     _getTireserviceSubscription?.cancel();
  58.   }
  59.  
  60.   void redraw() {
  61.     this.setState(() {});
  62.   }
  63.  
  64.   _fetchAds() {
  65.     _database.streamAdsByCity(widget.tireservice.city).map((docs) {
  66.       return docs.map((doc) {
  67.         return AdsDto.createFromDocument(doc);
  68.       });
  69.     }).listen((ads) {
  70.       isAdsMustShow = ads.isNotEmpty;
  71.       redraw();
  72.     });
  73.   }
  74.  
  75.   _fetchTireservice() {
  76.     _getTireserviceSubscription = _database
  77.         .streamTireserviceById(_actualTireserviceInfo.tireserviceId)
  78.         .listen((doc) {
  79.       _actualTireserviceInfo = TireserviceDto.createFromDocument(doc);
  80.       redraw();
  81.     });
  82.   }
  83.  
  84.   _callPhone(String phone) async {
  85.     if (phone == null || phone.isEmpty) return;
  86.  
  87.     final validPhone =
  88.         phone.length == 11 ? phone.substring(1) : phone.substring(2);
  89.     final url = "tel:+7$validPhone";
  90.     if (await canLaunch(url)) {
  91.       await launch(url);
  92.     } else {
  93.       throw 'Could not launch $url';
  94.     }
  95.   }
  96.  
  97.   void _fetchComments() {
  98.     _getCommentsSubscription = _database
  99.         .streamTireserviceCommentsById(_actualTireserviceInfo.tireserviceId)
  100.         .asyncMap(
  101.       (documents) {
  102.         return documents.map(
  103.           (document) {
  104.             return TireserviceComment.createFromDocument(document);
  105.           },
  106.         );
  107.       },
  108.     ).listen((comments) {
  109.       this._comments = List.from(comments);
  110.       _comments
  111.           .sort((first, second) => second.timestamp.compareTo(first.timestamp));
  112.       commentsLoaded = true;
  113.       redraw();
  114.     });
  115.   }
  116.  
  117.   showReviewSending() {
  118.     Analytics.showReviewSending();
  119.     showModalBottomSheet(
  120.       useRootNavigator: true,
  121.       isScrollControlled: true,
  122.       shape: RoundedRectangleBorder(
  123.           borderRadius: BorderRadius.vertical(top: Radius.circular(8))),
  124.       context: context,
  125.       builder: (BuildContext bc) {
  126.         return DraggableScrollableSheet(
  127.           initialChildSize: 0.85,
  128.           maxChildSize: 0.85,
  129.           minChildSize: 0.8,
  130.           expand: false,
  131.           builder: (BuildContext context, ScrollController scrollController) {
  132.             return Material(
  133.               borderRadius: BorderRadius.vertical(top: Radius.circular(8)),
  134.               child: ReviewSendingUI(_actualTireserviceInfo.tireserviceId),
  135.             );
  136.           },
  137.         );
  138.       },
  139.     );
  140.   }
  141.  
  142.   _showImageViewer() {
  143.     Navigator.of(context).push(PageRouteBuilder(
  144.       pageBuilder: (context, anim1, anim2) =>
  145.           ImageViewer(_actualTireserviceInfo.picturesURLs),
  146.       settings: RouteSettings(name: 'ImageViewer'),
  147.     )); //;
  148.   }
  149.  
  150.   _setStatusBarToBlackText() {
  151.     SystemChrome.setSystemUIOverlayStyle(
  152.         SystemUiOverlayStyle(statusBarBrightness: Brightness.light));
  153.   }
  154.  
  155.   @override
  156.   Widget build(BuildContext context) {
  157.     _setStatusBarToBlackText();
  158. //    adsHeaderElement = adsHeader().createElement();
  159.     return Scaffold(
  160.       resizeToAvoidBottomInset: true,
  161.       body: Stack(
  162.         children: <Widget>[
  163.           Container(
  164.               color: Colors.white,
  165.               child: ListView(
  166.                 children: buildContent(),
  167.               )),
  168.           Align(
  169.             alignment: Alignment.bottomCenter,
  170.             child: Container(
  171.               color: Colors.white,
  172.               child: SafeArea(
  173.                 top: false,
  174.                 child: _showAdsPanel(),
  175.               ),
  176.             ),
  177.           )
  178.         ],
  179.       ),
  180.     );
  181.   }
  182.  
  183.   List<Widget> buildContent() {
  184.     var listWidgets = <Widget>[];
  185.     listWidgets.add(imageRow());
  186.     listWidgets.add(subtitleRow("О шиномонтаже"));
  187.     listWidgets.add(addressRow());
  188.     if (_actualTireserviceInfo.getAdditionalsString().isNotEmpty)
  189.       listWidgets.add(additionalsRow());
  190.  
  191.     if (_actualTireserviceInfo.promo != null &&
  192.         _actualTireserviceInfo.promo.isNotEmpty) {
  193.       listWidgets.add(subtitleRow("Акции"));
  194.       listWidgets.add(promoRow());
  195.       listWidgets.add(showThisToWorker());
  196.     }
  197.  
  198.     listWidgets.add(subtitleRow("Рейтинг и отзывы"));
  199.     listWidgets.add(reviewsRow());
  200.     listWidgets.add(reviewsContainerRow());
  201.  
  202.     var totalBottomContainerHeight = callButtonContainerHeight;
  203.     if (isAdsMustShow) {
  204.       totalBottomContainerHeight += adsHeaderHeight + 16;
  205.     }
  206.  
  207.     listWidgets.add(SizedBox.fromSize(
  208.       size: Size.fromHeight(totalBottomContainerHeight),
  209.     ));
  210.     return listWidgets;
  211.   }
  212.  
  213.   Widget _showAdsPanel() {
  214.     final bottomPanelWidgets = List<Widget>();
  215.     if (isAdsMustShow) {
  216.       bottomPanelWidgets.add(adsHeader());
  217.     }
  218.     bottomPanelWidgets.add(bottomButtonWidget());
  219.     return Column(
  220.       mainAxisSize: MainAxisSize.min,
  221.       children: bottomPanelWidgets,
  222.     );
  223.   }
  224.  
  225.   Widget adsHeader() {
  226.     return Material(
  227.       elevation: 1,
  228.       child: ExpandablePanel(
  229.         header: Material(
  230.           color: Colors.blue[50],
  231.           borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
  232.           child: Shimmer.fromColors(
  233.             child: Container(
  234.               height: adsHeaderHeight,
  235.               decoration: ShapeDecoration(
  236.                 shape: RoundedRectangleBorder(
  237.                   borderRadius: BorderRadius.vertical(top: Radius.circular(8)),
  238.                 ),
  239.               ),
  240.               alignment: Alignment.center,
  241.               width: double.infinity,
  242.               child: Padding(
  243.                 padding: EdgeInsets.fromLTRB(16, 8, 16, 8),
  244.                 child: TextMediumBlack16("Скидки от партнёров!"),
  245.               ),
  246.             ),
  247.             baseColor: Colors.blue,
  248.             highlightColor: Colors.green[200],
  249.             period: const Duration(milliseconds: 3000),
  250.           ),
  251.         ),
  252.         collapsed: Container(),
  253.         expanded: expandedFooter(),
  254.         tapHeaderToExpand: true,
  255.         tapBodyToCollapse: true,
  256.         hasIcon: false,
  257.       ),
  258.     );
  259.   }
  260.  
  261.   Widget expandedFooter() {
  262.     return Column(
  263.       children: <Widget>[
  264.         AdsUI(widget.tireservice.city, AdCallSource.details),
  265.       ],
  266.     );
  267.   }
  268.  
  269.   Widget bottomButtonWidget() {
  270.     return Container(
  271.       height: callButtonContainerHeight,
  272.       alignment: Alignment.center,
  273.       child: Row(
  274.         mainAxisAlignment: MainAxisAlignment.spaceAround,
  275.         mainAxisSize: MainAxisSize.max,
  276.         children: [
  277.           CallButton(
  278.             onPressed: () {
  279.               Analytics.logOnCallBtnTapped(widget.tireservice);
  280.               _callPhone(widget.tireservice.phone);
  281.             },
  282.           ),
  283.           InkWell(
  284.             onTap: ()async{
  285.               await platform.invokeMethod('get2gis',{"lon":_actualTireserviceInfo.longitude, "lat":_actualTireserviceInfo.latitude});
  286.             },
  287.             child: Container(
  288.               height: callButtonContainerHeight*0.6,
  289.               child: Image.asset(
  290.                 'assets/icons/2gis.png',
  291.               ),
  292.             ),
  293.           ),
  294.           InkWell(
  295.             onTap: ()async{
  296.               await platform.invokeMethod('getYandex',{"lon":_actualTireserviceInfo.longitude, "lat":_actualTireserviceInfo.latitude});
  297.             },
  298.             child: Container(
  299.               height: callButtonContainerHeight*0.6,
  300.               child: Image.asset(
  301.                 'assets/icons/yandex_maps.png',
  302.               ),
  303.             ),
  304.           ),
  305.         ],
  306.       ),
  307.     );
  308.   }
  309.  
  310.   Widget promoRow() {
  311.     return Padding(
  312.       padding: EdgeInsets.fromLTRB(10, 0, 10, 10),
  313.       child: Card(
  314.         elevation: 6,
  315.         color: Colors.yellow[100],
  316.         child: Column(
  317.           children: <Widget>[
  318.             Container(
  319.               padding: EdgeInsets.all(16),
  320.               child: TextBlack14(
  321.                 _actualTireserviceInfo.promo,
  322.                 useEllipsisOverflow: false,
  323.               ),
  324.             ),
  325.             QrImage(
  326.               padding: EdgeInsets.only(bottom: 16),
  327.               data:
  328.                   "$saleUrl?text=${_actualTireserviceInfo.promo}&address=${_actualTireserviceInfo.address}",
  329.               version: QrVersions.auto,
  330.               size: 100.0,
  331.             ),
  332.           ],
  333.         ),
  334.       ),
  335.     );
  336.   }
  337.  
  338.   Widget showThisToWorker() {
  339.     return Padding(
  340.       padding: EdgeInsets.fromLTRB(20, 0, 20, 0),
  341.       child: Expanded(
  342.         child: Text(
  343.           'Для получения скидки - покажи данный QR работнику шиномонтажа',
  344.           style: TextStyle(
  345.             color: Colors.black54,
  346.           ),
  347.           textAlign: TextAlign.center,
  348.         ),
  349.       ),
  350.     );
  351.   }
  352.  
  353.   Widget subtitleRow(String text) {
  354.     return Container(
  355.       color: Colors.white,
  356.       child: Padding(
  357.         padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
  358.         child: Row(
  359.           children: <Widget>[
  360.             Expanded(
  361.               child: Padding(
  362.                 padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
  363.                 child: TextMediumBlack18(text),
  364.               ),
  365.             ),
  366.           ],
  367.         ),
  368.       ),
  369.     );
  370.   }
  371.  
  372.   Widget smallSubtitleRow(String text) {
  373.     return Container(
  374.       color: Colors.white,
  375.       child: Padding(
  376.         padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
  377.         child: Row(
  378.           children: <Widget>[
  379.             Expanded(
  380.               child: Padding(
  381.                 padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
  382.                 child: Text54MediumBlack16(text),
  383.               ),
  384.             ),
  385.           ],
  386.         ),
  387.       ),
  388.     );
  389.   }
  390.  
  391.   Widget closeButton() {
  392.     return Align(
  393.       alignment: Alignment.topLeft,
  394.       child: InkWell(
  395.         onTap: () {
  396.           Navigator.pop(context);
  397.         },
  398.         child: Padding(
  399.           padding: EdgeInsets.all(16),
  400.           child: Icon(
  401.             Icons.close,
  402.             color: Colors.white,
  403.             size: 20,
  404.           ),
  405.         ),
  406.       ),
  407.     );
  408.   }
  409.  
  410.   Widget titleRowTransparent() {
  411.     return Container(
  412.       color: Colors.transparent,
  413.       child: Padding(
  414.         padding: EdgeInsets.fromLTRB(16, 8, 16, 8),
  415.         child: Row(
  416.           children: <Widget>[
  417.             Expanded(
  418.               child: Padding(
  419.                 padding: EdgeInsets.fromLTRB(0, 0, 0, 0),
  420.                 child: TextMediumWhite18(
  421.                     "${_actualTireserviceInfo.getTireserviceTypeString()} ${_actualTireserviceInfo.title}"),
  422.               ),
  423.             ),
  424.           ],
  425.         ),
  426.       ),
  427.     );
  428.   }
  429.  
  430.   Widget addressRow() {
  431.     return Container(
  432.       color: Colors.white,
  433.       child: Padding(
  434.         padding: EdgeInsets.fromLTRB(16, 8, 16, 8),
  435.         child: Row(
  436.           children: <Widget>[
  437.             Text54Black14("Адрес:"),
  438.             Expanded(
  439.               child: Padding(
  440.                 padding: EdgeInsets.fromLTRB(8, 0, 16, 0),
  441.                 child: TextBlack14(_actualTireserviceInfo.address),
  442.               ),
  443.             ),
  444.           ],
  445.         ),
  446.       ),
  447.     );
  448.   }
  449.  
  450.   Widget additionalsRow() {
  451.     return Container(
  452.       color: Colors.white,
  453.       child: Padding(
  454.         padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
  455.         child: Row(
  456.           crossAxisAlignment: CrossAxisAlignment.start,
  457.           children: <Widget>[
  458.             Text54Black14("Дополнительно:"),
  459.             Expanded(
  460.               child: Padding(
  461.                 padding: EdgeInsets.fromLTRB(8, 0, 16, 0),
  462.                 child: TextBlack14(
  463.                   _actualTireserviceInfo.getAdditionalsString(),
  464.                   useEllipsisOverflow: false,
  465.                 ),
  466.               ),
  467.             ),
  468.           ],
  469.         ),
  470.       ),
  471.     );
  472.   }
  473.  
  474.   Widget timeOpenRow() {
  475.     return Container(
  476.       color: Colors.white,
  477.       child: Padding(
  478.         padding: EdgeInsets.fromLTRB(16, 0, 16, 10),
  479.         child: Row(
  480.           children: <Widget>[
  481.             SvgPicture.asset("assets/icons/clock_grey_16.svg"),
  482.             Expanded(
  483.               child: Padding(
  484.                 padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
  485.                 child: Text54Black12(_actualTireserviceInfo.getTimeWorking()),
  486.               ),
  487.             ),
  488.           ],
  489.         ),
  490.       ),
  491.     );
  492.   }
  493.  
  494.   Widget timeOpenRowTransparent() {
  495.     return Container(
  496.       color: Colors.transparent,
  497.       child: Padding(
  498.         padding: EdgeInsets.fromLTRB(16, 0, 16, 10),
  499.         child: Row(
  500.           children: <Widget>[
  501.             Icon(Icons.access_time, color: Colors.white, size: 20),
  502.             Expanded(
  503.               child: Padding(
  504.                 padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
  505.                 child:
  506.                     TextMediumWhite14(_actualTireserviceInfo.getTimeWorking()),
  507.               ),
  508.             ),
  509.           ],
  510.         ),
  511.       ),
  512.     );
  513.   }
  514.  
  515.   Widget reviewsRow() {
  516.     return Container(
  517.       color: Colors.white,
  518.       child: Padding(
  519.         padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
  520.         child: Row(
  521.           children: <Widget>[
  522.             TextBlack14(_actualTireserviceInfo.getRatingString()),
  523.             RatingBarReadOnly(rating: _actualTireserviceInfo.rating),
  524.             Spacer(),
  525.             FlatButton(
  526.               onPressed: showReviewSending,
  527.               child: TextBlue16("Написать отзыв"),
  528.             ),
  529.           ],
  530.         ),
  531.       ),
  532.     );
  533.   }
  534.  
  535.   Widget reviewsContainerRow() {
  536.     var emptyText = commentsLoaded
  537.         ? "У этого шиномонтажа ещё нет ни одного отзыва"
  538.         : "Загружаем отзывы...";
  539.     return Stack(
  540.       children: <Widget>[
  541.         Padding(
  542.           padding: EdgeInsets.fromLTRB(16, 30, 16, 30),
  543.           child: Center(
  544.             child: Text54Black12(emptyText),
  545.           ),
  546.         ),
  547.         Container(
  548.           child: ListView.builder(
  549.             itemCount: _comments.length,
  550.             itemBuilder: (context, index) => Padding(
  551.                 padding: EdgeInsets.fromLTRB(16, 10, 16, 0),
  552.                 child: reviewWidget(_comments[index])),
  553.             shrinkWrap: true,
  554.             physics: const NeverScrollableScrollPhysics(),
  555.           ),
  556.         ),
  557.       ],
  558.     );
  559.   }
  560.  
  561.   Widget reviewWidget(TireserviceComment comment) {
  562.     return Container(
  563.       child: Material(
  564.         borderRadius: BorderRadius.circular(8),
  565.         color: Color.fromARGB(255, 246, 248, 250),
  566.         elevation: 0,
  567.         child: Column(
  568.           mainAxisSize: MainAxisSize.min,
  569.           children: <Widget>[
  570.             Padding(
  571.               padding: EdgeInsets.fromLTRB(16, 16, 16, 16),
  572.               child: Row(
  573.                 children: <Widget>[
  574.                   Icon(
  575.                     Icons.account_circle,
  576.                     size: 30,
  577.                     color: Colors.grey,
  578.                   ),
  579.                   Expanded(
  580.                     child: Padding(
  581.                       padding: EdgeInsets.fromLTRB(16, 0, 16, 0),
  582.                       child: TextBlack14(comment.userName),
  583.                     ),
  584.                   ),
  585.                   Text54Black12(comment.getDateString())
  586.                 ],
  587.               ),
  588.             ),
  589.             Padding(
  590.               padding: EdgeInsets.fromLTRB(16, 0, 16, 16),
  591.               child: Align(
  592.                 alignment: Alignment.centerLeft,
  593.                 child: TextBlack14(
  594.                   comment.body,
  595.                   useEllipsisOverflow: false,
  596.                 ),
  597.               ),
  598.             ),
  599.             Container(
  600.               child: Align(
  601.                 alignment: Alignment.centerRight,
  602.                 child: Padding(
  603.                   padding: EdgeInsets.fromLTRB(16, 0, 16, 16),
  604.                   child: RatingBarReadOnlySmall(
  605.                     rating: comment.rating,
  606.                   ),
  607.                 ),
  608.               ),
  609.             )
  610.           ],
  611.         ),
  612.       ),
  613.     );
  614.   }
  615.  
  616.   Widget imageRow() {
  617.     var rowSize = 200.0;
  618.     return InkWell(
  619.       onTap: _showImageViewer,
  620.       child: Stack(
  621.         alignment: Alignment.bottomLeft,
  622.         children: <Widget>[
  623.           Container(
  624.             constraints: BoxConstraints.expand(height: rowSize),
  625.             foregroundDecoration: BoxDecoration(color: Colors.black38),
  626.             child: Hero(
  627.               tag: _actualTireserviceInfo.picturesURLs.first,
  628.               child: Image(
  629.                 fit: BoxFit.fitWidth,
  630.                 image: NetworkImage(_actualTireserviceInfo.picturesURLs.first),
  631.                 loadingBuilder: (BuildContext context, Widget child,
  632.                     ImageChunkEvent loadingProgress) {
  633.                   if (loadingProgress == null) return child;
  634.                   return Shimmer.fromColors(
  635.                     child: Container(
  636.                       child: Icon(
  637.                         Icons.image,
  638.                         size: rowSize,
  639.                       ),
  640.                     ),
  641.                     baseColor: Colors.grey[300],
  642.                     highlightColor: Colors.white,
  643.                     period: const Duration(milliseconds: 500),
  644.                   );
  645.                 },
  646.               ),
  647.             ),
  648.           ),
  649.           Container(
  650.             constraints: BoxConstraints.expand(height: rowSize),
  651.             alignment: Alignment.bottomLeft,
  652.             child: Column(
  653.               children: <Widget>[
  654.                 closeButton(),
  655.                 Spacer(),
  656.                 titleRowTransparent(),
  657.                 timeOpenRowTransparent(),
  658.               ],
  659.             ),
  660.           ),
  661.         ],
  662.       ),
  663.     );
  664.   }
  665. }
  666.  
RAW Paste Data