From 7f7d839545166396a58413cb87e8dddb3abb0e39 Mon Sep 17 00:00:00 2001 From: Yung Yanni Date: Sun, 21 Mar 2021 20:10:03 +0300 Subject: [PATCH 1/3] =?UTF-8?q?-=20=D0=92=D1=81=D0=B5=20=D0=BA=D0=BD=D0=BE?= =?UTF-8?q?=D0=BF=D0=BA=D0=B8=20=D0=B7=D0=B0=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D1=8B=20=D0=BD=D0=B0=20=D0=B0=D0=BA=D1=82=D1=83=D0=B0=D0=BB?= =?UTF-8?q?=D1=8C=D0=BD=D1=8B=D0=B5.=20-=20=D0=9A=20=D0=BA=D0=B0=D0=B6?= =?UTF-8?q?=D0=B4=D0=BE=D0=B9=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D0=B5=20?= =?UTF-8?q?=D0=B1=D1=8B=D0=BB=D0=BE=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=20=D0=BE=D0=BF=D0=BE=D0=B2=D0=B5=D1=89=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BE=20=D0=BD=D0=B0=D0=B6=D0=B0=D1=82?= =?UTF-8?q?=D0=B8=D0=B8=20=D0=B2=20=D0=BA=D0=BE=D0=BD=D1=81=D0=BE=D0=BB?= =?UTF-8?q?=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 10 +- lib/ui/screen/res/themes.dart | 40 ++++-- lib/ui/screen/sight_details.dart | 46 ++++--- lib/ui/sight_card_widgets/sight_card.dart | 114 +++++++++++++----- .../widgets/custom_bottom_navigation_bar.dart | 44 +++---- lib/ui/widgets/custom_tab_bar/custom_tab.dart | 5 +- lib/ui/widgets/sight_card_icon_buttons.dart | 27 +++++ pubspec.yaml | 2 +- 8 files changed, 200 insertions(+), 88 deletions(-) create mode 100644 lib/ui/widgets/sight_card_icon_buttons.dart diff --git a/lib/main.dart b/lib/main.dart index 0074c13..27f06b7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -23,11 +23,11 @@ class _MyAppState extends State { return MaterialApp( theme: lightTheme, debugShowCheckedModeBanner: false, - //home: SightListScreen(), - home: SightDetails( - sight: mocks[6], - ), - //home: VisitingScreen(), + // home: SightListScreen(), + // home: SightDetails( + // sight: mocks[6], + // ), + home: VisitingScreen(), ); } } diff --git a/lib/ui/screen/res/themes.dart b/lib/ui/screen/res/themes.dart index 4511f7d..0f5700b 100644 --- a/lib/ui/screen/res/themes.dart +++ b/lib/ui/screen/res/themes.dart @@ -11,10 +11,6 @@ final lightTheme = ThemeData.light().copyWith( scaffoldBackgroundColor: AppColors.white, buttonColor: AppColors.ltPrimaryColor, hoverColor: AppColors.white, - floatingActionButtonTheme: FloatingActionButtonThemeData( - backgroundColor: AppColors.ltAccentColor, - foregroundColor: AppColors.ltCardBackground, - ), bottomNavigationBarTheme: BottomNavigationBarThemeData( backgroundColor: AppColors.white, unselectedItemColor: AppColors.ltPrimaryColorLight, @@ -28,6 +24,23 @@ final lightTheme = ThemeData.light().copyWith( bodyText1: body1BoldTextStyle, bodyText2: body2NormalTextStyle, ), + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + textStyle: titleTextStyle, + primary: AppColors.white, + minimumSize: Size.fromHeight(48.0), + elevation: 0.0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0), + ), + ), + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + primary: AppColors.ltPrimaryColor, + textStyle: body2NormalTextStyle, + ), + ), ); final darkTheme = ThemeData.dark().copyWith( @@ -52,8 +65,21 @@ final darkTheme = ThemeData.dark().copyWith( unselectedItemColor: AppColors.inactiveBlack, selectedItemColor: AppColors.white, ), - buttonTheme: ButtonThemeData( - buttonColor: AppColors.dtPrimaryColorDark, - textTheme: ButtonTextTheme.primary, + elevatedButtonTheme: ElevatedButtonThemeData( + style: ElevatedButton.styleFrom( + textStyle: titleTextStyle, + primary: AppColors.dtPrimaryColor, + minimumSize: Size.fromHeight(48.0), + elevation: 0.0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(12.0), + ), + ), + ), + textButtonTheme: TextButtonThemeData( + style: TextButton.styleFrom( + primary: AppColors.dtPrimaryColor, + textStyle: body2NormalTextStyle, + ), ), ); diff --git a/lib/ui/screen/sight_details.dart b/lib/ui/screen/sight_details.dart index a513890..2095f1a 100644 --- a/lib/ui/screen/sight_details.dart +++ b/lib/ui/screen/sight_details.dart @@ -1,3 +1,6 @@ +import 'dart:ffi'; + +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:places/domain/sight.dart'; import 'package:places/theme/colors.dart'; @@ -96,16 +99,17 @@ class _CardDetailsBackButton extends StatelessWidget { child: SizedBox( width: 32, height: 32, - child: FlatButton( - padding: EdgeInsets.all(0), - onPressed: () {}, - color: Theme.of(context).hoverColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), + child: ElevatedButton( + style: ButtonStyle( + padding: MaterialStateProperty.all( + EdgeInsets.all(0), + ), ), + onPressed: () => print('button "back" tapped'), child: Icon( Icons.arrow_back_ios_rounded, size: 15, + color: Theme.of(context).buttonColor, ), ), ), @@ -208,21 +212,25 @@ class _CardDetailsDescription extends StatelessWidget { class _CardDetailsBuildARouteBtn extends StatelessWidget { @override Widget build(BuildContext context) { - return FlatButton.icon( - onPressed: () {}, - color: Colors.green, - height: 48, - minWidth: double.infinity, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12), + return ElevatedButton.icon( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all(Theme + .of(context) + .accentColor), + ), + onPressed: () => print('button "Build a route" tapped'), + icon: SvgPicture.asset( + iconGO, + color: AppColors.white, ), - icon: SvgPicture.asset(iconGO), label: Text( AppTexts.buildARouteBtnText, style: Theme .of(context) .textTheme - .bodyText1, + .bodyText1 + .copyWith(color: AppColors.white), ), ); } @@ -232,8 +240,8 @@ class _CardDetailsBuildARouteBtn extends StatelessWidget { class _CardDetailsToScheduleBtn extends StatelessWidget { @override Widget build(BuildContext context) { - return FlatButton.icon( - onPressed: () {}, + return TextButton.icon( + onPressed: () => print('button "back" tapped'), icon: SvgPicture.asset( iconCalendar, color: AppColors.inactiveBlack, @@ -256,8 +264,8 @@ class _CardDetailsToScheduleBtn extends StatelessWidget { class _CardDetailsToFavoritesBtn extends StatelessWidget { @override Widget build(BuildContext context) { - return FlatButton.icon( - onPressed: () {}, + return TextButton.icon( + onPressed: () => print('button "To favorites" tapped'), icon: SvgPicture.asset( iconHeart, color: Theme diff --git a/lib/ui/sight_card_widgets/sight_card.dart b/lib/ui/sight_card_widgets/sight_card.dart index d6d3571..699a17e 100644 --- a/lib/ui/sight_card_widgets/sight_card.dart +++ b/lib/ui/sight_card_widgets/sight_card.dart @@ -5,6 +5,7 @@ import 'package:places/theme/colors.dart'; import 'package:places/theme/text_styles.dart'; import 'package:places/ui/widgets/network_image_with_loading_indicator'; import 'package:places/theme/custom_icons.dart'; +import 'package:places/ui/widgets/sight_card_icon_buttons.dart'; //TODO если место закрыто, выводить worktime, иначе описание //TODO приделать нормальный worktime вместо заглушки @@ -16,31 +17,34 @@ class SightCard extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: EdgeInsets.all(16), - child: Container( - height: 188, - width: double.infinity, - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - color: Theme.of(context).cardColor), - child: AspectRatio( - aspectRatio: 3.0 / 2.0, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _SightCardHeader( - url: sight.url, - type: sight.type, - ), - _SightCardName( - name: sight.nameSight, - ), - _SightCardDetails( - details: sight.details, - ), - ], + return InkWell( + onTap: () => print('InkWell sight card tapped'), + child: Padding( + padding: EdgeInsets.all(16), + child: Container( + height: 188, + width: double.infinity, + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16), + color: Theme.of(context).cardColor), + child: AspectRatio( + aspectRatio: 3.0 / 2.0, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _SightCardHeader( + url: sight.url, + type: sight.type, + ), + _SightCardName( + name: sight.nameSight, + ), + _SightCardDetails( + details: sight.details, + ), + ], + ), ), ), ), @@ -54,10 +58,18 @@ class _SightCardHeader extends StatelessWidget { Key key, @required this.url, @required this.type, + this.isHeart, + this.isCalendar, + this.isShare, + this.isRemove, }) : assert(url != null && type != null); final String url; final String type; + final bool isHeart; + final bool isCalendar; + final bool isShare; + final bool isRemove; @override Widget build(BuildContext context) { @@ -86,11 +98,7 @@ class _SightCardHeader extends StatelessWidget { Positioned( top: 3, right: 2, - child: IconButton( - icon: SvgPicture.asset(iconHeart), - color: AppColors.white, - onPressed: () {}, - ), + child: ButtonsPackOnCard(isHeart: true,), ), ], ); @@ -167,3 +175,49 @@ class _SightCardDetails extends StatelessWidget { ); } } + +// чуть позже доведу до ума переиспользуемую карточку +/// Widget with sets of buttons on the top right card corner. If you need sightCard for SightListScreen, set [true] to bool isHeart. +/// If you need sightCard for visiting_screen(Want to visit), set [true] to isCalendar for a card with the "Calendar" and "Remove" buttons. +/// If you need sightCard for visiting_screen(Visited), set [true] to isShare for a card with the "Share" and "Remove" buttons. +class ButtonsPackOnCard extends StatelessWidget { + final bool isHeart; + final bool isCalendar; + final bool isShare; + + const ButtonsPackOnCard({ + Key key, + this.isHeart, + this.isCalendar, + this.isShare, + }) : assert( + isHeart != null || (isCalendar != null) || (isShare != null), + ); + + @override + Widget build(BuildContext context) { + if (isShare == true) { + return Row( + children: [ + iconButtonShare, + SizedBox( + width: 15, + ), + iconButtonRemove, + ], + ); + } else if (isCalendar == true) { + return Row( + children: [ + iconButtonCalendar, + SizedBox( + width: 15, + ), + iconButtonRemove, + ], + ); + } else { + return iconButtonHeart; + } + } +} diff --git a/lib/ui/widgets/custom_bottom_navigation_bar.dart b/lib/ui/widgets/custom_bottom_navigation_bar.dart index 5d33f24..1e86c11 100644 --- a/lib/ui/widgets/custom_bottom_navigation_bar.dart +++ b/lib/ui/widgets/custom_bottom_navigation_bar.dart @@ -3,29 +3,24 @@ import 'package:flutter_svg/flutter_svg.dart'; import 'package:places/theme/custom_icons.dart'; import 'package:places/ui/screen/res/themes.dart'; -/// Model of the custom navigation bar -/* -ВОПРОС: как можно избавиться от указания цветов везде? По внутренним ощущениям, выглядит не очень - */ +/// Model of the custom bottom navigation bar + class CustomBottomNavigationBar extends StatelessWidget { @override Widget build(BuildContext context) { return BottomNavigationBar( + onTap: (i) => print('Bottom navigation bar tapped'), type: BottomNavigationBarType.fixed, items: [ BottomNavigationBarItem( icon: SvgPicture.asset( iconList, - color: Theme - .of(context) - .buttonColor, + color: Theme.of(context).buttonColor, ), label: '', activeIcon: SvgPicture.asset( iconListFull, - color: Theme - .of(context) - .buttonColor, + color: Theme.of(context).buttonColor, ), ), BottomNavigationBarItem( @@ -73,35 +68,34 @@ class CustomBottomNavigationBar extends StatelessWidget { .buttonColor, ), ), - // _BottomBarIcon(iconName: iconHeart, activeIconName: iconHeartFull,), + // _BottomBarItem(iconName: iconHeart, activeIconName: iconHeartFull), ], ); } } - -// class _BottomBarIcon extends BottomNavigationBarItem { -// _BottomBarIcon( -// {Key key, @required this.iconName, @required this.activeIconName,}) -// : assert( -// icon != null, +// Пытаюсь сделать переиспользуемый элемент, но не очень получается:( +// class _BottomBarItem extends BottomNavigationBarItem { +// String iconName; +// String activeIconName; +// _BottomBarItem({ +// Key key, +// @required this.iconName, +// @required this.activeIconName, +// }) : assert( +// iconName != null, // activeIconName != null, // ); -// final String iconName; -// final String activeIconName; -// final String label = ''; -// Color iconColor; // -// @override // BottomNavigationBarItem build(BuildContext context) { // return BottomNavigationBarItem( // icon: SvgPicture.asset( -// iconName, -// color: Theme.of(context).buttonColor, +// iconHeart, +// // color: Theme.of(context).buttonColor, // ), // label: '', // activeIcon: SvgPicture.asset( // activeIconName, -// color: Theme.of(context).buttonColor, +// // color: Theme.of(context).buttonColor, // ), // ); // } diff --git a/lib/ui/widgets/custom_tab_bar/custom_tab.dart b/lib/ui/widgets/custom_tab_bar/custom_tab.dart index b7f8cb2..93aaa5d 100644 --- a/lib/ui/widgets/custom_tab_bar/custom_tab.dart +++ b/lib/ui/widgets/custom_tab_bar/custom_tab.dart @@ -20,7 +20,10 @@ class CustomTab extends StatelessWidget { Widget build(BuildContext context) { return Expanded( child: GestureDetector( - onTap: () => onTap?.call(), + onTap: () { + onTap?.call(); + print('tabBar tapped'); + }, child: Container( decoration: BoxDecoration( color: isActive diff --git a/lib/ui/widgets/sight_card_icon_buttons.dart b/lib/ui/widgets/sight_card_icon_buttons.dart new file mode 100644 index 0000000..9bcedd7 --- /dev/null +++ b/lib/ui/widgets/sight_card_icon_buttons.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:places/theme/custom_icons.dart'; +import 'package:places/ui/screen/res/themes.dart'; +import 'package:places/ui/widgets/sight_card_icon_buttons.dart'; +import 'package:places/theme/colors.dart'; + +var iconButtonHeart = IconButton( + icon: SvgPicture.asset(iconHeart), + onPressed: () => print('button "Heart" tapped'), + color: AppColors.white, +); +var iconButtonCalendar = IconButton( + icon: SvgPicture.asset(iconCalendar), + onPressed: () => print('button "Calendar" tapped'), + color: AppColors.white, +); +var iconButtonRemove = IconButton( + icon: SvgPicture.asset(iconRemove), + onPressed: () => print('button "Remove" tapped'), + color: AppColors.white, +); +var iconButtonShare = IconButton( + icon: SvgPicture.asset(iconShare), + onPressed: () => print('button "Share" tapped'), + color: AppColors.white, +); diff --git a/pubspec.yaml b/pubspec.yaml index 00e94a1..6b843d7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: cupertino_icons: ^1.0.0 dependencies: - flutter_svg: ^0.19.0 + flutter_svg: 0.19.3 dev_dependencies: flutter_test: From c5c0aa5c1312885271640c70a3dbe0f70970b047 Mon Sep 17 00:00:00 2001 From: Yung Yanni Date: Wed, 7 Apr 2021 18:28:41 +0300 Subject: [PATCH 2/3] =?UTF-8?q?-=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D1=8D=D0=BA=D1=80=D0=B0=D0=BD=20filters=5Fscreen.dart?= =?UTF-8?q?,=20=D1=81=D0=BE=D0=B4=D0=B5=D1=80=D0=B6=D0=B0=D1=89=D0=B8?= =?UTF-8?q?=D0=B9=20=D0=B7=D0=BD=D0=B0=D1=87=D0=BA=D0=B8=20=D0=BA=D0=B0?= =?UTF-8?q?=D1=82=D0=B5=D0=B3=D0=BE=D1=80=D0=B8=D0=B9=20=D0=BC=D0=B5=D1=81?= =?UTF-8?q?=D1=82,=20=D1=81=D0=BB=D0=B0=D0=B9=D0=B4=D0=B5=D1=80=20=D0=B8?= =?UTF-8?q?=20=D0=BA=D0=BD=D0=BE=D0=BF=D0=BA=D1=83,=20=D1=80=D0=B5=D0=B0?= =?UTF-8?q?=D0=B3=D0=B8=D1=80=D1=83=D1=8E=D1=89=D1=83=D1=8E=20=D0=BD=D0=B0?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=BB=D0=B8=D1=87=D0=B5=D1=81=D1=82=D0=B2=D0=BE?= =?UTF-8?q?=20=D0=B4=D0=BE=D1=81=D1=82=D1=83=D0=BF=D0=BD=D1=8B=D1=85=20?= =?UTF-8?q?=D0=BC=D0=B5=D1=81=D1=82=20=D0=BF=D0=BE=20=D0=B2=D1=8B=D0=B1?= =?UTF-8?q?=D1=80=D0=B0=D0=BD=D0=BD=D1=8B=D0=BC=20=D0=BA=D1=80=D0=B8=D1=82?= =?UTF-8?q?=D0=B5=D1=80=D0=B8=D1=8F=D0=BC=20-=D0=94=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B=20=D0=BC=D0=BE=D0=BA=D0=BE=D0=B2?= =?UTF-8?q?=D1=8B=D0=B5=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5=20=D1=81=20?= =?UTF-8?q?=D1=82=D0=B8=D0=BF=D0=B0=D0=BC=D0=B8=20=D0=BC=D0=B5=D1=81=D1=82?= =?UTF-8?q?=D0=B0=20-=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD?= =?UTF-8?q?=D0=B0=20=D0=BC=D0=BE=D0=B4=D0=B5=D0=BB=D1=8C=20SightType=20?= =?UTF-8?q?=D0=B2=20=D0=BE=D0=B4=D0=BD=D0=BE=D0=B8=D0=BC=D0=B5=D0=BD=D0=BD?= =?UTF-8?q?=D0=BE=D0=BC=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B5,=20=D1=87=D1=82?= =?UTF-8?q?=D0=BE=D0=B1=D1=8B=20=D1=81=D0=B2=D1=8F=D0=B7=D0=B0=D1=82=D1=8C?= =?UTF-8?q?=20Sight.type=20=D0=B8=20=D0=B8=D0=BA=D0=BE=D0=BD=D0=BA=D1=83?= =?UTF-8?q?=20=D0=BD=D0=B0=20=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86?= =?UTF-8?q?=D0=B5=20=D1=84=D0=B8=D0=BB=D1=8C=D1=82=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/constants.dart | 23 +- lib/domain/sight.dart | 4 +- lib/domain/sight_type.dart | 9 + lib/domain/sight_type_icon_helper.dart | 28 +++ lib/main.dart | 4 +- lib/mocks.dart | 67 +++-- lib/theme/colors.dart | 2 +- lib/theme/custom_icons.dart | 8 + .../screen/filters_screen/filters_screen.dart | 231 ++++++++++++++++++ .../type_filter_item_widget.dart | 87 +++++++ lib/ui/screen/res/themes.dart | 16 +- lib/ui/screen/sight_details.dart | 2 - lib/ui/widgets/custom_tab_bar/custom_tab.dart | 20 +- pubspec.yaml | 7 + 14 files changed, 457 insertions(+), 51 deletions(-) create mode 100644 lib/domain/sight_type.dart create mode 100644 lib/domain/sight_type_icon_helper.dart create mode 100644 lib/ui/screen/filters_screen/filters_screen.dart create mode 100644 lib/ui/screen/filters_screen/type_filter_item_widget.dart diff --git a/lib/constants.dart b/lib/constants.dart index 3f67870..5e580ef 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -1,7 +1,3 @@ -import 'dart:ui'; -import 'package:flutter/material.dart'; -import 'package:places/theme/colors.dart'; - /// App text class AppTexts { static const appHeader = 'Список\nинтересных мест'; @@ -19,14 +15,13 @@ class AppTexts { 'Отмечайте понравившиеся\n места и они появятся здесь.'; static const tagPlacesYouVisited = 'Завершите маршрут,\n чтобы место попало сюда.'; -} -// /// Sight card decorations -// class AppDecorations { -// static const cardDecoration = BoxDecoration( -// color: AppColors.ltCardBackground, -// borderRadius: BorderRadius.all( -// Radius.circular(16), -// ), -// ); -// } + static const String filtersDistanceMeters = 'м'; + static const String filtersDistanceKilometers = 'км'; + static const String filtersCategories = 'Категории'; + static const String filtersDistance = 'Расстояние'; + static const String filtersFrom = 'от'; + static const String filtersTo = 'до'; + static const String filtersShow = 'Показать'; + static const String filtersClear = 'Очистить'; +} diff --git a/lib/domain/sight.dart b/lib/domain/sight.dart index 0bb64d3..683e470 100644 --- a/lib/domain/sight.dart +++ b/lib/domain/sight.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; /// Sight model class Sight { final String nameSight; - final double lan; + final double lat; final double lon; final String url; final String details; @@ -15,7 +15,7 @@ class Sight { Sight( {@required this.nameSight, - @required this.lan, + @required this.lat, @required this.lon, @required this.url, @required this.details, diff --git a/lib/domain/sight_type.dart b/lib/domain/sight_type.dart new file mode 100644 index 0000000..da0a71e --- /dev/null +++ b/lib/domain/sight_type.dart @@ -0,0 +1,9 @@ +/// Sight type for Filter screen +/// Converting 'icon name' to 'String' for SvgImage.asset() by [getIconByName] + +class SightType { + final String name; + final String iconName; + + const SightType({this.name, this.iconName}); +} diff --git a/lib/domain/sight_type_icon_helper.dart b/lib/domain/sight_type_icon_helper.dart new file mode 100644 index 0000000..7cad312 --- /dev/null +++ b/lib/domain/sight_type_icon_helper.dart @@ -0,0 +1,28 @@ +import 'package:places/theme/custom_icons.dart'; + +/// Function returns [String] for SvgImage.asset() by icon's name [iconName] + +String getIconByName(String iconName) { + // SvgPicture icon; + switch (iconName) { + case 'Hotel': + iconName = iconHotel; + break; + case 'Restaurant': + iconName = iconRestaurant; + break; + case 'ParticularPlace': + iconName = iconParticularPlace; + break; + case 'Park': + iconName = iconPark; + break; + case 'Museum': + iconName = iconMuseum; + break; + case 'Cafe': + iconName = iconCafe; + break; + } + return iconName; +} diff --git a/lib/main.dart b/lib/main.dart index 27f06b7..62b46e2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -7,6 +7,7 @@ import 'package:places/ui/screen/visiting_screen.dart'; import 'package:places/ui/widgets/empty_favorites_places.dart'; import 'mocks.dart'; import 'package:places/ui/screen/res/themes.dart'; +import 'package:places/ui/screen/filters_screen/filters_screen.dart'; void main() { runApp(MyApp()); @@ -27,7 +28,8 @@ class _MyAppState extends State { // home: SightDetails( // sight: mocks[6], // ), - home: VisitingScreen(), + // home: VisitingScreen(), + home: FiltersScreen(), ); } } diff --git a/lib/mocks.dart b/lib/mocks.dart index f379ff8..0f8b144 100644 --- a/lib/mocks.dart +++ b/lib/mocks.dart @@ -1,85 +1,116 @@ import 'package:flutter/material.dart'; import 'package:places/domain/sight.dart'; +import 'package:places/domain/sight_type.dart'; +/// Categories +final List typeMocks = [ + SightType( + name: 'Отель', + iconName: 'Hotel', + ), + SightType( + name: 'Ресторан', + iconName: 'Restaurant', + ), + SightType( + name: 'Особое место', + iconName: 'ParticularPlace', + ), + SightType( + name: 'Парк', + iconName: 'Park', + ), + SightType( + name: 'Музей', + iconName: 'Museum', + ), + SightType( + name: 'Кафе', + iconName: 'Cafe', + ), +]; + +/// Places final List mocks = [ Sight( nameSight: 'Surf Studio', - lan: 51.664264, + lat: 51.664264, lon: 39.201271, url: 'https://ia.wampi.ru/2020/12/25/OLE30HKCAqA.jpg', details: 'офис Surf Studio. Нормальной фотки не нашел, поэтому вот вам я. В камеру смотрю', - type: 'building', + type: 'Музей', workTime: 'Закрыто до 09:00', openTime: TimeOfDay(hour: 9, minute: 0), closeTime: TimeOfDay(hour: 21, minute: 0), ), Sight( nameSight: 'Мой дом', - lan: 51.700504, + lat: 51.700504, lon: 39.204497, url: 'https://ia.wampi.ru/2020/12/25/IhgbY-glBRY.jpg', details: 'Мой дом. Здесь я сижу и смотрю вас', - type: 'building', + type: 'Музей', workTime: 'Закрыто до 09:00', openTime: TimeOfDay(hour: 7, minute: 0), closeTime: TimeOfDay(hour: 21, minute: 0), ), Sight( nameSight: 'Блошиный рынок', - lan: 51.705625, + lat: 51.705625, lon: 39.137255, url: 'https://ia.wampi.ru/2020/12/25/nsMGyaYgKFI.jpg', details: 'Блошиный рынок. Довольно странное место, но здесь я провожу все выходные покупая ерунду', - type: 'event', + type: 'Парк', workTime: 'Закрыто до 09:00', openTime: TimeOfDay(hour: 9, minute: 0), closeTime: TimeOfDay(hour: 12, minute: 0), ), Sight( nameSight: 'Рейв в лесу', - lan: 51.788332, + lat: 51.788332, lon: 39.205264, url: 'https://ia.wampi.ru/2021/01/16/IMG_20200822_030142.jpg', details: - 'Меня как-то угораздило сюда попасть. Выглядит клево, но жутковато:D', - type: 'event', + 'Меня как-то угораздило сюда попасть. Выглядит клево, но жутковато:D', + type: 'Особое место', workTime: 'Закрыто до 09:00', openTime: TimeOfDay(hour: 22, minute: 0), closeTime: TimeOfDay(hour: 23, minute: 59), ), Sight( nameSight: 'Русский магазин в Гуанчжоу', - lan: 23.098822, + lat: 23.098822, lon: 113.314972, url: 'https://ia.wampi.ru/2021/01/16/IMG_20190921_203644_105.jpg', details: - 'Самое популярное место сбора моделей в Гуанчжоу. Атмосферные вечера с ребятами из разных стран и знакомые продукты из СНГ. Эх, вернуться бы туда на вечерок)', - type: 'place', + 'Самое популярное место сбора моделей в Гуанчжоу. Атмосферные вечера с ребятами из разных стран и знакомые продукты из СНГ. Эх, вернуться бы туда на вечерок)', + type: 'Ресторан', workTime: 'Закрыто до 09:00', openTime: TimeOfDay(hour: 20, minute: 0), closeTime: TimeOfDay(hour: 23, minute: 0), ), Sight( nameSight: 'Шанхай', - lan: 31.273409, + lat: 31.273409, lon: 121.474693, url: 'https://ia.wampi.ru/2021/01/16/IMG_20190226_095603.jpg', details: - 'А здесь я жил и работал в Шанхае. Очень красивый и невероятно современный город', - type: 'place', + 'А здесь я жил и работал в Шанхае. Очень красивый и невероятно современный город', + type: 'Особое место', workTime: 'Закрыто до 09:00', openTime: TimeOfDay(hour: 9, minute: 0), closeTime: TimeOfDay(hour: 21, minute: 0), ), Sight( nameSight: 'Хочу на стажировку', - lan: 51.664264, + lat: 51.664264, lon: 39.201271, url: 'https://ic.wampi.ru/2021/03/11/Y6iQsGiA1qw.jpg', - details: 'Давно хочу у вас работать! Я даже специально купил новый мак на М1 с 16гб!', - type: 'place', + details: + 'Давно хочу у вас работать! Я даже специально купил новый мак на М1 с 16гб!', + type: 'Особое место', workTime: 'Закрыто до 09:00', openTime: TimeOfDay(hour: 9, minute: 0), closeTime: TimeOfDay(hour: 21, minute: 0), diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index f8f698e..d16c467 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -11,7 +11,7 @@ class AppColors { static const ltActiveColor = ltPrimaryColor; /// Dark theme colors - static const dtGreen = Color(0xFF6ADA6F); + static const dtAccentColor = Color(0xFF6ADA6F); static const dtRed = Color(0xFFCF2A2A); static const dtPrimaryColorDark = Color(0xFF1A1A20); static const dtPrimaryColor = Color(0xFF21222C); diff --git a/lib/theme/custom_icons.dart b/lib/theme/custom_icons.dart index cce137b..5577e1a 100644 --- a/lib/theme/custom_icons.dart +++ b/lib/theme/custom_icons.dart @@ -17,3 +17,11 @@ const iconShare = 'assets/icons/Share.svg'; const iconGO = 'assets/icons/GO.svg'; const iconGOBig = 'assets/icons/GOBig.svg'; const iconEmptyFavoritesPlaces = 'assets/icons/EmptyFavoritesPlaces.svg'; + +const iconHotel = 'assets/icons/Hotel.svg'; +const iconRestaurant = 'assets/icons/Restaurant.svg'; +const iconParticularPlace = 'assets/icons/ParticularPlace.svg'; +const iconPark = 'assets/icons/Park.svg'; +const iconMuseum = 'assets/icons/Museum.svg'; +const iconCafe = 'assets/icons/Cafe.svg'; +const iconChoice = 'assets/icons/Choice.svg'; diff --git a/lib/ui/screen/filters_screen/filters_screen.dart b/lib/ui/screen/filters_screen/filters_screen.dart new file mode 100644 index 0000000..dc5d6a3 --- /dev/null +++ b/lib/ui/screen/filters_screen/filters_screen.dart @@ -0,0 +1,231 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:places/domain/sight.dart'; +import 'package:places/domain/sight_type.dart'; +import 'package:places/mocks.dart'; +import 'package:places/constants.dart'; +import 'package:places/theme/colors.dart'; +import 'package:places/theme/text_styles.dart'; +import 'package:places/ui/screen/filters_screen/type_filter_item_widget.dart'; +import 'package:places/ui/widgets/custom_bottom_navigation_bar.dart'; +import 'dart:math'; + +/// Screen with filters +class FiltersScreen extends StatefulWidget { + @override + _FiltersScreenState createState() => _FiltersScreenState(); +} + +class _FiltersScreenState extends State { + static const double _minRange = 100.0; + static const double _maxRange = 10000.0; + + /// Slider's values + RangeValues _rangeValues; + + /// Current location + double _currentLat = 51.699799; + double _currentLon = 39.205946; + + /// List of places + final List _sights = mocks; + + /// List of categories + final List _types = typeMocks; + + /// Selected categories to filter + final List _selectedTypes = []; + + /// Number of filtered items + int _filteredCount; + + @override + void initState() { + super.initState(); + _initFilters(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + backgroundColor: AppColors.transparent, + elevation: 0, + leading: IconButton( + icon: Icon( + Icons.arrow_back_ios_rounded, + color: Theme.of(context).buttonColor, + size: 20, + ), + onPressed: () {}), + actions: [ + TextButton( + onPressed: _clearFilters, + child: Text( + AppTexts.filtersClear, + style: subtitle2TextStyle.copyWith( + color: Theme.of(context).accentColor), + ), + ), + ], + ), + bottomNavigationBar: CustomBottomNavigationBar(), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppTexts.filtersCategories.toUpperCase(), + style: Theme.of(context) + .textTheme + .bodyText2 + .copyWith(color: AppColors.inactiveBlack), + ), + const SizedBox( + height: 24.0, + ), + GridView.builder( + physics: NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: _types.length, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3, + mainAxisSpacing: 40, + crossAxisSpacing: 12, + ), + itemBuilder: (BuildContext context, int index) { + return TypeFilterItemWidget( + sightType: _types[index], + onTap: () { + _onTypeSelect(_types[index].name); + _filteredCount = _calculateFilteredCount(); + }, + isSelected: _isTypeSelected(_types[index].name), + ); + }, + ), + SizedBox(height: 60), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + AppTexts.filtersDistance, + style: Theme.of(context).textTheme.subtitle1.copyWith( + fontWeight: FontWeight.normal, + color: Theme.of(context).buttonColor), + ), + Text( + _getDistanceLabel(), + style: TextStyle(color: Theme.of(context).disabledColor), + ), + ], + ), + RangeSlider( + values: _rangeValues, + min: _minRange, + max: _maxRange, + onChangeEnd: (_) { + setState( + () => _filteredCount = _calculateFilteredCount(), + ); + }, + onChanged: (newValues) { + setState(() => _rangeValues = newValues); + }, + ), + Spacer(), + ElevatedButton( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty.all(Theme.of(context).accentColor), + ), + onPressed: _onShowTap, + child: Text( + _getShowButtonLabel(), + style: Theme.of(context).textTheme.subtitle2, + ), + ), + ], + ), + ), + ); + } + + /// showing range above slider + String _getDistanceLabel() { + String distLabel(double distanceMeters) { + if (distanceMeters >= 1000) { + return '${(distanceMeters / 1000).toStringAsFixed(0)} ${AppTexts.filtersDistanceKilometers}'; + } + return '${(distanceMeters).toStringAsFixed(0)} ${AppTexts.filtersDistanceMeters}'; + } + + return '${AppTexts.filtersFrom} ${distLabel(_rangeValues.start)} ${AppTexts.filtersTo} ${distLabel(_rangeValues.end)}'; + } + + /// filter result value on button + String _getShowButtonLabel() { + return '${AppTexts.filtersShow.toUpperCase()} ($_filteredCount)'; + } + + /// return filtered places amount if >=1 filters enabled + int _calculateFilteredCount() { + final filteredSights = _sights + .where((sight) => _selectedTypes.isNotEmpty + ? _selectedTypes.contains(sight.type) + : true) + .where( + (sight) => _isPointInsideRange( + sight.lat, + sight.lon, + _rangeValues.start, + _rangeValues.end, + ), + ) + .toList(); + + return filteredSights.length; + } + + /// clear filters + void _clearFilters() { + setState(() { + _initFilters(); + }); + } + + /// set filters to default + void _initFilters() { + _rangeValues = RangeValues(_minRange, _maxRange); + _selectedTypes.clear(); + _filteredCount = _calculateFilteredCount(); + } + + /// add or remove category by tap + void _onTypeSelect(String typeName) { + setState(() { + _isTypeSelected(typeName) + ? _selectedTypes.remove(typeName) + : _selectedTypes.add(typeName); + }); + } + + bool _isTypeSelected(String typeName) => _selectedTypes.contains(typeName); + + //TODO вернуть фильтр на экран sight list widget после задания 8.1 + void _onShowTap() { + print( + 'minDistance: ${_rangeValues.start}\nmaxDistance ${_rangeValues.end}\ntypes: $_selectedTypes'); + } + + bool _isPointInsideRange( + double lat, double lon, double minDistance, double maxDistance) { + final double ky = 40000 / 360; + final double kx = cos(pi * _currentLat / 180.0) * ky; + final double dx = (_currentLon - lon).abs() * kx; + final double dy = (_currentLat - lat).abs() * ky; + final double dis = sqrt(dx * dx + dy * dy); + return dis >= minDistance && dis <= maxDistance; + } +} diff --git a/lib/ui/screen/filters_screen/type_filter_item_widget.dart b/lib/ui/screen/filters_screen/type_filter_item_widget.dart new file mode 100644 index 0000000..2da84d4 --- /dev/null +++ b/lib/ui/screen/filters_screen/type_filter_item_widget.dart @@ -0,0 +1,87 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:places/domain/sight_type.dart'; +import 'package:places/domain/sight_type_icon_helper.dart'; +import 'package:places/theme/colors.dart'; +import 'package:places/theme/custom_icons.dart'; + +class TypeFilterItemWidget extends StatelessWidget { + final SightType sightType; + final bool isSelected; + final VoidCallback onTap; + + const TypeFilterItemWidget({ + Key key, + @required this.sightType, + @required this.onTap, + @required this.isSelected, + }) : assert(sightType != null), + assert(isSelected != null), + assert(onTap != null); + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: onTap, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + height: 64, + width: 64, + child: Container( + decoration: BoxDecoration( + color: Theme.of(context) + .accentColor + .withOpacity(isSelected ? 0.3 : 0.1), + shape: BoxShape.circle, + ), + child: Stack( + children: [ + Center( + child: LayoutBuilder( + builder: + (BuildContext context, BoxConstraints constraints) { + return SvgPicture.asset( + getIconByName(sightType.iconName), + width: 32, + height: 32, + color: Theme.of(context).accentColor, + ); + }, + ), + ), + if (isSelected) + Align( + alignment: Alignment.bottomRight, + child: SvgPicture.asset( + //ВОПРОС: Иконка на превью выглядит так, будто сама "галочка в центре не вырезана. При попытке покрасить получается просто цветной кружок. Возможно использовать только дефолтный вид. Тянуть две иконки? + iconChoice, + ), + ) + ], + ), + ), + ), + SizedBox(height: 12), + SizedBox( + height: 16, + width: 96, + child: Text( + sightType.name, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .bodyText2 + .copyWith(color: Theme.of(context).buttonColor), + textAlign: TextAlign.center, + ), + ) + ], + ), + ); + } +} diff --git a/lib/ui/screen/res/themes.dart b/lib/ui/screen/res/themes.dart index 0f5700b..2932c3c 100644 --- a/lib/ui/screen/res/themes.dart +++ b/lib/ui/screen/res/themes.dart @@ -41,11 +41,18 @@ final lightTheme = ThemeData.light().copyWith( textStyle: body2NormalTextStyle, ), ), + sliderTheme: SliderThemeData( + trackHeight: 2.0, + thumbColor: AppColors.white, + overlayColor: AppColors.transparent, + activeTrackColor: AppColors.ltAccentColor, + inactiveTrackColor: AppColors.inactiveBlack, + ), ); final darkTheme = ThemeData.dark().copyWith( primaryColor: AppColors.dtPrimaryColor, - accentColor: AppColors.dtGreen, + accentColor: AppColors.dtAccentColor, disabledColor: AppColors.inactiveBlack, scaffoldBackgroundColor: AppColors.dtPrimaryColor, cardColor: AppColors.dtPrimaryColorDark, @@ -82,4 +89,11 @@ final darkTheme = ThemeData.dark().copyWith( textStyle: body2NormalTextStyle, ), ), + sliderTheme: SliderThemeData( + trackHeight: 2.0, + thumbColor: AppColors.white, + overlayColor: AppColors.transparent, + activeTrackColor: AppColors.ltAccentColor, + inactiveTrackColor: AppColors.inactiveBlack, + ), ); diff --git a/lib/ui/screen/sight_details.dart b/lib/ui/screen/sight_details.dart index 2095f1a..2cc279d 100644 --- a/lib/ui/screen/sight_details.dart +++ b/lib/ui/screen/sight_details.dart @@ -1,5 +1,3 @@ -import 'dart:ffi'; - import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:places/domain/sight.dart'; diff --git a/lib/ui/widgets/custom_tab_bar/custom_tab.dart b/lib/ui/widgets/custom_tab_bar/custom_tab.dart index 93aaa5d..2e8d941 100644 --- a/lib/ui/widgets/custom_tab_bar/custom_tab.dart +++ b/lib/ui/widgets/custom_tab_bar/custom_tab.dart @@ -41,18 +41,14 @@ class CustomTab extends StatelessWidget { maxLines: 1, overflow: TextOverflow.ellipsis, style: isActive - ? Theme - .of(context) - .textTheme - .bodyText1 - .copyWith(color: Theme - .of(context) - .hoverColor) - : Theme - .of(context) - .textTheme - .bodyText1 - .copyWith(color: AppColors.inactiveBlack), + ? Theme.of(context) + .textTheme + .bodyText1 + .copyWith(color: Theme.of(context).hoverColor) + : Theme.of(context) + .textTheme + .bodyText1 + .copyWith(color: AppColors.inactiveBlack), ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index 6b843d7..d977119 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,6 +38,13 @@ flutter: - assets/icons/GO.svg - assets/icons/GOBig.svg - assets/icons/EmptyFavoritesPlaces.svg + - assets/icons/Hotel.svg + - assets/icons/Restaurant.svg + - assets/icons/ParticularPlace.svg + - assets/icons/Park.svg + - assets/icons/Museum.svg + - assets/icons/Cafe.svg + - assets/icons/Choice.svg From f4fc68edb6895e17b116c2fa0d29659e3d7499ce Mon Sep 17 00:00:00 2001 From: Yung Yanni Date: Wed, 14 Apr 2021 22:14:03 +0300 Subject: [PATCH 3/3] =?UTF-8?q?-=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=20=D1=8D=D0=BA=D1=80=D0=B0=D0=BD=20settings=5Fscre?= =?UTF-8?q?en.dart=20-=20=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=B0=20=D1=81=D0=BC=D0=B5=D0=BD=D0=B0=20=D1=82?= =?UTF-8?q?=D0=B5=D0=BC=D1=8B=20=D0=BF=D0=BE=20=D1=81=D0=BB=D0=B0=D0=B9?= =?UTF-8?q?=D0=B4=D0=B5=D1=80=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/constants.dart | 4 + lib/main.dart | 36 ++++++++- lib/theme/custom_icons.dart | 2 + lib/ui/screen/res/themes.dart | 39 +++++++--- lib/ui/settings_screen.dart | 73 +++++++++++++++++++ .../widgets/custom_bottom_navigation_bar.dart | 28 ------- pubspec.yaml | 1 + 7 files changed, 143 insertions(+), 40 deletions(-) create mode 100644 lib/ui/settings_screen.dart diff --git a/lib/constants.dart b/lib/constants.dart index 5e580ef..0ccc5b7 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -24,4 +24,8 @@ class AppTexts { static const String filtersTo = 'до'; static const String filtersShow = 'Показать'; static const String filtersClear = 'Очистить'; + + static const String settings = 'Настройки'; + static const String darkTheme = 'Тёмная тема'; + static const String watchTutorial = 'Смотреть туториал'; } diff --git a/lib/main.dart b/lib/main.dart index 62b46e2..00502e0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:places/ui/settings_screen.dart'; import 'package:places/ui/widgets/empty_visited_places.dart'; import 'package:places/ui/screen/sight_details.dart'; import 'package:places/ui/sight_card_widgets/sight_card.dart'; @@ -9,6 +10,9 @@ import 'mocks.dart'; import 'package:places/ui/screen/res/themes.dart'; import 'package:places/ui/screen/filters_screen/filters_screen.dart'; +/// current theme storage +final themeState = ThemeState(); + void main() { runApp(MyApp()); } @@ -19,17 +23,45 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { + @override + void initState() { + super.initState(); + themeState.addListener(_onThemeChange); + } + + @override + void dispose() { + themeState.removeListener(_onThemeChange); + super.dispose(); + } + @override Widget build(BuildContext context) { return MaterialApp( - theme: lightTheme, + theme: themeState.isDark ? darkTheme : lightTheme, debugShowCheckedModeBanner: false, // home: SightListScreen(), // home: SightDetails( // sight: mocks[6], // ), // home: VisitingScreen(), - home: FiltersScreen(), + //home: FiltersScreen(), + home: SettingsScreen(), ); } + + void _onThemeChange() => setState(() {}); +} + +// Временно лежит здесь. После прохождения темы "хранение данных" займет положенное место. +// Тема меняется парамаетром [ThemeState.isDark], подписываемся на обновления и меняем тему в MaterialApp. Смена темы происходит в [SettingsScreen] +class ThemeState extends ChangeNotifier { + bool _isDark = false; + + bool get isDark => _isDark; + + set isDark(bool newVal) { + _isDark = newVal; + notifyListeners(); + } } diff --git a/lib/theme/custom_icons.dart b/lib/theme/custom_icons.dart index 5577e1a..b137c99 100644 --- a/lib/theme/custom_icons.dart +++ b/lib/theme/custom_icons.dart @@ -25,3 +25,5 @@ const iconPark = 'assets/icons/Park.svg'; const iconMuseum = 'assets/icons/Museum.svg'; const iconCafe = 'assets/icons/Cafe.svg'; const iconChoice = 'assets/icons/Choice.svg'; + +const iconInfo = 'assets/icons/Info.svg'; \ No newline at end of file diff --git a/lib/ui/screen/res/themes.dart b/lib/ui/screen/res/themes.dart index 2932c3c..5afcc0a 100644 --- a/lib/ui/screen/res/themes.dart +++ b/lib/ui/screen/res/themes.dart @@ -38,17 +38,26 @@ final lightTheme = ThemeData.light().copyWith( textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( primary: AppColors.ltPrimaryColor, - textStyle: body2NormalTextStyle, + textStyle: body2NormalTextStyle, + ), ), - ), - sliderTheme: SliderThemeData( - trackHeight: 2.0, - thumbColor: AppColors.white, - overlayColor: AppColors.transparent, - activeTrackColor: AppColors.ltAccentColor, - inactiveTrackColor: AppColors.inactiveBlack, - ), -); + sliderTheme: SliderThemeData( + trackHeight: 2.0, + thumbColor: AppColors.white, + overlayColor: AppColors.transparent, + activeTrackColor: AppColors.ltAccentColor, + inactiveTrackColor: AppColors.inactiveBlack, + ), + appBarTheme: AppBarTheme( + centerTitle: true, + color: AppColors.transparent, + elevation: 0, + titleTextStyle: TextStyle(color: AppColors.ltPrimaryColor), + ), + switchTheme: SwitchThemeData( + thumbColor: MaterialStateProperty.all(AppColors.white), + trackColor: MaterialStateProperty.all(AppColors.ltAccentColor), + )); final darkTheme = ThemeData.dark().copyWith( primaryColor: AppColors.dtPrimaryColor, @@ -96,4 +105,14 @@ final darkTheme = ThemeData.dark().copyWith( activeTrackColor: AppColors.ltAccentColor, inactiveTrackColor: AppColors.inactiveBlack, ), + appBarTheme: AppBarTheme( + centerTitle: true, + color: AppColors.transparent, + elevation: 0, + titleTextStyle: TextStyle(color: AppColors.dtPrimaryColor), + ), + switchTheme: SwitchThemeData( + thumbColor: MaterialStateProperty.all(AppColors.white), + trackColor: MaterialStateProperty.all(AppColors.dtAccentColor), + ), ); diff --git a/lib/ui/settings_screen.dart b/lib/ui/settings_screen.dart new file mode 100644 index 0000000..2186f38 --- /dev/null +++ b/lib/ui/settings_screen.dart @@ -0,0 +1,73 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:places/constants.dart'; +import 'package:places/theme/colors.dart'; +import 'package:places/theme/custom_icons.dart'; +import 'package:places/theme/text_styles.dart'; +import 'package:places/ui/widgets/custom_bottom_navigation_bar.dart'; +import '../main.dart'; + +/// Model of Settings Screen +class SettingsScreen extends StatefulWidget { + @override + _SettingsScreenState createState() => _SettingsScreenState(); +} + +class _SettingsScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + AppTexts.settings, + style: TextStyle(color: Theme.of(context).buttonColor), + ), + ), + bottomNavigationBar: CustomBottomNavigationBar(), + body: Column( + children: [ + SizedBox(height: 36), + SwitchListTile( + //ВОПРОС: Как спрятать параметры внешнего вида? В конструкторе темы не нашел подходящего инструмента. На SwitchTheme тоже не реагирует. + activeColor: AppColors.white, + inactiveThumbColor: AppColors.white, + inactiveTrackColor: AppColors.inactiveBlack, + activeTrackColor: Theme.of(context).accentColor, + controlAffinity: ListTileControlAffinity.platform, + title: Text( + AppTexts.darkTheme, + style: subtitle2TextStyle.copyWith( + color: Theme.of(context).buttonColor, + fontWeight: FontWeight.w400), + ), + value: themeState.isDark, + onChanged: _onChangeTheme, + ), + Divider( + indent: 16, + endIndent: 16, + ), + ListTile( + title: Text( + AppTexts.watchTutorial, + style: subtitle2TextStyle.copyWith( + color: Theme.of(context).buttonColor, + fontWeight: FontWeight.w400), + ), + trailing: Padding( + padding: EdgeInsets.only(right: 16.0), + child: SvgPicture.asset(iconInfo), + ), + ), + Divider( + indent: 16, + endIndent: 16, + ), + ], + ), + ); + } + + void _onChangeTheme(bool newValue) => themeState.isDark = newValue; +} diff --git a/lib/ui/widgets/custom_bottom_navigation_bar.dart b/lib/ui/widgets/custom_bottom_navigation_bar.dart index 1e86c11..5992102 100644 --- a/lib/ui/widgets/custom_bottom_navigation_bar.dart +++ b/lib/ui/widgets/custom_bottom_navigation_bar.dart @@ -68,35 +68,7 @@ class CustomBottomNavigationBar extends StatelessWidget { .buttonColor, ), ), - // _BottomBarItem(iconName: iconHeart, activeIconName: iconHeartFull), ], ); } } -// Пытаюсь сделать переиспользуемый элемент, но не очень получается:( -// class _BottomBarItem extends BottomNavigationBarItem { -// String iconName; -// String activeIconName; -// _BottomBarItem({ -// Key key, -// @required this.iconName, -// @required this.activeIconName, -// }) : assert( -// iconName != null, -// activeIconName != null, -// ); -// -// BottomNavigationBarItem build(BuildContext context) { -// return BottomNavigationBarItem( -// icon: SvgPicture.asset( -// iconHeart, -// // color: Theme.of(context).buttonColor, -// ), -// label: '', -// activeIcon: SvgPicture.asset( -// activeIconName, -// // color: Theme.of(context).buttonColor, -// ), -// ); -// } -// } diff --git a/pubspec.yaml b/pubspec.yaml index d977119..a826c5e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,6 +45,7 @@ flutter: - assets/icons/Museum.svg - assets/icons/Cafe.svg - assets/icons/Choice.svg + - assets/icons/Info.svg