diff --git a/lib/assets/animations/indicator.riv b/lib/assets/animations/indicator.riv index 6bc6ab8..0190c3e 100644 Binary files a/lib/assets/animations/indicator.riv and b/lib/assets/animations/indicator.riv differ diff --git a/lib/config/design_config.dart b/lib/config/design_config.dart index 493aba0..b609d7c 100644 --- a/lib/config/design_config.dart +++ b/lib/config/design_config.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; class DesignConfig { + static const Color lightPrimaryColor2 = Color(0XFFE6F3FA); static const Color lightPrimaryColor3 = Color(0XFFF5FAFC); static const Color primaryColor = Color(0XFF007EA7); static const Color darkPrimaryColor2 = Color(0XFF1B3C59); @@ -40,7 +41,7 @@ class DesignConfig { brightness: Brightness.light, ); - static const currentColorScheme = lightColorScheme; + static const ColorScheme currentColorScheme = lightColorScheme; static const TextStyle subtitle1Text = TextStyle( fontSize: 17, @@ -74,14 +75,14 @@ class DesignConfig { ); static final List defaultShadow = [ BoxShadow( - color: const Color(0XFF4D4D4D).withOpacity(0.25), - offset: const Offset(0, 4), - blurRadius: 8, + color: currentColorScheme.primaryVariant.withOpacity(0.25), + blurRadius: 16, spreadRadius: 0, ) ]; - static const Duration defaultAnimationDuration = Duration(milliseconds: 600); + static const Duration lowAnimationDuration = Duration(milliseconds: 300); + static const Duration mediumAnimationDuration = Duration(milliseconds: 600); static final SystemUiOverlayStyle systemUIOverlayStyle = SystemUiOverlayStyle( statusBarBrightness: Brightness.dark, diff --git a/lib/pages/authentication/authentication.dart b/lib/pages/authentication/authentication.dart index ade5c92..0f8209e 100644 --- a/lib/pages/authentication/authentication.dart +++ b/lib/pages/authentication/authentication.dart @@ -27,7 +27,7 @@ class _AuthenticationState extends State { return Scaffold( body: Consumer( builder: (context, state, child) => AnimatedSwitcher( - duration: DesignConfig.defaultAnimationDuration, + duration: DesignConfig.mediumAnimationDuration, child: _pages[state.currentPageIndex], ), ), diff --git a/lib/pages/home/home.dart b/lib/pages/home/home.dart index aeed46a..7436f2e 100644 --- a/lib/pages/home/home.dart +++ b/lib/pages/home/home.dart @@ -1,9 +1,11 @@ +import 'package:didvan/pages/home/home_state.dart'; import 'package:didvan/pages/home/news/news.dart'; import 'package:didvan/pages/home/profile/profile.dart'; import 'package:didvan/pages/home/radar/radar.dart'; import 'package:didvan/pages/home/radar/radar_state.dart'; import 'package:didvan/pages/home/statistics/statistics.dart'; import 'package:didvan/pages/home/studio/studio..dart'; +import 'package:didvan/pages/home/widgets/didvan_bnb.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; @@ -20,7 +22,9 @@ class _HomeState extends State with SingleTickerProviderStateMixin { @override void initState() { _tabController = TabController(length: 5, vsync: this); - _tabController.addListener(() {}); + _tabController.addListener(() { + context.read().currentPageIndex = _tabController.index; + }); super.initState(); } @@ -44,6 +48,15 @@ class _HomeState extends State with SingleTickerProviderStateMixin { ], ), ), + bottomNavigationBar: Consumer( + builder: (context, state, child) => DidvanBNB( + currentTabIndex: state.currentPageIndex, + onTabChanged: (index) { + state.currentPageIndex = index; + _tabController.animateTo(index); + }, + ), + ), ); } } diff --git a/lib/pages/home/home_state.dart b/lib/pages/home/home_state.dart new file mode 100644 index 0000000..4d20216 --- /dev/null +++ b/lib/pages/home/home_state.dart @@ -0,0 +1,12 @@ +import 'package:didvan/providers/core_provider.dart'; + +class HomeState extends CoreProvier { + int _currentPageIndex = 0; + + set currentPageIndex(int value) { + _currentPageIndex = value; + notifyListeners(); + } + + int get currentPageIndex => _currentPageIndex; +} diff --git a/lib/pages/home/radar/radar.dart b/lib/pages/home/radar/radar.dart index 161f03e..7b27a89 100644 --- a/lib/pages/home/radar/radar.dart +++ b/lib/pages/home/radar/radar.dart @@ -30,7 +30,7 @@ class _RadarState extends State { _isAnimating = true; await _scrollController.animateTo( 380, - duration: DesignConfig.defaultAnimationDuration, + duration: DesignConfig.mediumAnimationDuration, curve: Curves.ease, ); _isAnimating = false; @@ -40,7 +40,7 @@ class _RadarState extends State { _isAnimating = true; await _scrollController.animateTo( 0, - duration: DesignConfig.defaultAnimationDuration, + duration: DesignConfig.mediumAnimationDuration, curve: Curves.ease, ); _isAnimating = false; diff --git a/lib/pages/home/radar/widgets/categories_gird.dart b/lib/pages/home/radar/widgets/categories_gird.dart index eca753b..cb51cd4 100644 --- a/lib/pages/home/radar/widgets/categories_gird.dart +++ b/lib/pages/home/radar/widgets/categories_gird.dart @@ -12,7 +12,7 @@ class CategoriesRow1 extends StatelessWidget { Widget build(BuildContext context) { final MediaQueryData d = MediaQuery.of(context); return AnimatedPositioned( - duration: DesignConfig.defaultAnimationDuration, + duration: DesignConfig.mediumAnimationDuration, top: isColapsed ? 12 : 176 + d.padding.top, left: isColapsed ? -d.size.width : 0, right: isColapsed ? d.size.width : 0, @@ -51,7 +51,7 @@ class CategoriesRow2 extends StatelessWidget { Widget build(BuildContext context) { final MediaQueryData d = MediaQuery.of(context); return AnimatedPositioned( - duration: DesignConfig.defaultAnimationDuration, + duration: DesignConfig.mediumAnimationDuration, top: isColapsed ? -60 : 300 + d.padding.top, left: isColapsed ? -80 : 0, right: isColapsed ? 124 : 0, diff --git a/lib/pages/home/radar/widgets/categories_list.dart b/lib/pages/home/radar/widgets/categories_list.dart index 9d6cb38..e9b4f3a 100644 --- a/lib/pages/home/radar/widgets/categories_list.dart +++ b/lib/pages/home/radar/widgets/categories_list.dart @@ -24,7 +24,7 @@ class CategoriesList extends StatelessWidget { child: AnimatedCrossFade( crossFadeState: isColapsed ? CrossFadeState.showSecond : CrossFadeState.showFirst, - duration: DesignConfig.defaultAnimationDuration, + duration: DesignConfig.mediumAnimationDuration, firstChild: const SizedBox(), secondChild: Container( height: 60 + d.padding.top, @@ -44,7 +44,7 @@ class CategoriesList extends StatelessWidget { children: [ AnimatedVisibility( isVisible: isColapsed, - duration: DesignConfig.defaultAnimationDuration, + duration: DesignConfig.mediumAnimationDuration, child: _itemBuilder( RadarCategory(title: 'همه', asset: '', id: 0), ), diff --git a/lib/pages/home/radar/widgets/category_item.dart b/lib/pages/home/radar/widgets/category_item.dart index 48560ae..85f516c 100644 --- a/lib/pages/home/radar/widgets/category_item.dart +++ b/lib/pages/home/radar/widgets/category_item.dart @@ -20,7 +20,7 @@ class CategoryItem extends StatelessWidget { final Size ds = MediaQuery.of(context).size; return Center( child: AnimatedContainer( - duration: DesignConfig.defaultAnimationDuration, + duration: DesignConfig.mediumAnimationDuration, padding: isColapsed ? const EdgeInsets.all(4) : EdgeInsets.zero, width: isColapsed ? 100 : ds.width / 3, alignment: Alignment.center, @@ -33,7 +33,7 @@ class CategoryItem extends StatelessWidget { child: Column( children: [ AnimatedVisibility( - duration: DesignConfig.defaultAnimationDuration, + duration: DesignConfig.mediumAnimationDuration, isVisible: !isColapsed, child: Container( width: ds.width / 5, diff --git a/lib/pages/home/widgets/didvan_bnb.dart b/lib/pages/home/widgets/didvan_bnb.dart new file mode 100644 index 0000000..d1f3d91 --- /dev/null +++ b/lib/pages/home/widgets/didvan_bnb.dart @@ -0,0 +1,129 @@ +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/widgets/didvan/text.dart'; +import 'package:flutter/material.dart'; + +class DidvanBNB extends StatelessWidget { + final int currentTabIndex; + final void Function(int index) onTabChanged; + + const DidvanBNB( + {Key? key, required this.currentTabIndex, required this.onTabChanged}) + : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + height: 64, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: const BorderRadius.vertical(top: Radius.circular(16)), + boxShadow: DesignConfig.defaultShadow, + ), + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + children: [ + _NavBarItem( + isSelected: currentTabIndex == 0, + title: 'اخبار', + selectedIcon: DidvanIcons.news_solid, + unselectedIcon: DidvanIcons.news_light, + onTap: () => onTabChanged(0), + ), + _NavBarItem( + isSelected: currentTabIndex == 1, + title: 'آمار', + selectedIcon: DidvanIcons.chart_solid, + unselectedIcon: DidvanIcons.chart_light, + onTap: () => onTabChanged(1), + ), + _NavBarItem( + isSelected: currentTabIndex == 2, + title: 'رادار', + selectedIcon: DidvanIcons.news_solid, + unselectedIcon: DidvanIcons.news_regular, + onTap: () => onTabChanged(2), + ), + _NavBarItem( + isSelected: currentTabIndex == 3, + title: 'استودیو', + selectedIcon: DidvanIcons.play_circle_solid, + unselectedIcon: DidvanIcons.play_circle_light, + onTap: () => onTabChanged(3), + ), + _NavBarItem( + isSelected: currentTabIndex == 4, + title: 'پروفایل', + selectedIcon: DidvanIcons.profile_solid, + unselectedIcon: DidvanIcons.profile_light, + onTap: () => onTabChanged(4), + ), + ], + ), + ); + } +} + +class _NavBarItem extends StatelessWidget { + final VoidCallback onTap; + final bool isSelected; + final String title; + final IconData selectedIcon; + final IconData unselectedIcon; + const _NavBarItem({ + Key? key, + required this.isSelected, + required this.title, + required this.selectedIcon, + required this.unselectedIcon, + required this.onTap, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Expanded( + child: Tooltip( + message: title, + decoration: BoxDecoration( + color: DesignConfig.darkPrimaryColor2, + borderRadius: DesignConfig.highBorderRadius, + boxShadow: DesignConfig.defaultShadow, + ), + child: GestureDetector( + onTap: onTap, + child: Container( + color: Colors.transparent, + child: Column( + children: [ + const SizedBox( + height: 4, + ), + AnimatedContainer( + padding: const EdgeInsets.all(4), + duration: DesignConfig.lowAnimationDuration, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: isSelected + ? DesignConfig.lightPrimaryColor2 + : Theme.of(context).colorScheme.surface, + ), + child: Icon( + isSelected ? selectedIcon : unselectedIcon, + size: 32, + color: isSelected ? DesignConfig.darkPrimaryColor2 : null, + ), + ), + DidvanText( + title, + style: Theme.of(context).textTheme.caption, + color: DesignConfig.darkPrimaryColor2, + ), + const Spacer(), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart index 7ea510f..f904a45 100644 --- a/lib/routes/route_generator.dart +++ b/lib/routes/route_generator.dart @@ -1,6 +1,7 @@ import 'package:didvan/pages/authentication/authentication.dart'; import 'package:didvan/pages/authentication/authentication_state.dart'; import 'package:didvan/pages/home/home.dart'; +import 'package:didvan/pages/home/home_state.dart'; import 'package:didvan/pages/home/radar/radar.dart'; import 'package:didvan/pages/home/radar/radar_state.dart'; import 'package:didvan/pages/splash/splash.dart'; @@ -28,7 +29,10 @@ class RouteGenerator { ); case Routes.home: return _materialPageRouteGenerator( - const Home(), + ChangeNotifierProvider( + create: (context) => HomeState(), + child: const Home(), + ), ); default: return _errorRoute(); diff --git a/lib/utils/actions_sheet.dart b/lib/utils/actions_sheet.dart index 29a2299..fa63022 100644 --- a/lib/utils/actions_sheet.dart +++ b/lib/utils/actions_sheet.dart @@ -10,7 +10,8 @@ class ActionSheetUtils { context: context, builder: (context) => Padding( padding: EdgeInsets.symmetric( - horizontal: MediaQuery.of(context).size.width / 3), + horizontal: MediaQuery.of(context).size.width / 3, + ), child: const RiveAnimation.asset(Assets.logoLoadingAnimation), ), );