diff --git a/lib/assets/images/categories/business-dark.svg b/lib/assets/images/categories/business-dark.svg new file mode 100644 index 0000000..9e09cda --- /dev/null +++ b/lib/assets/images/categories/business-dark.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/lib/assets/images/categories/business-light.svg b/lib/assets/images/categories/business-light.svg new file mode 100644 index 0000000..4b64c42 --- /dev/null +++ b/lib/assets/images/categories/business-light.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/lib/assets/images/categories/business.png b/lib/assets/images/categories/business.png deleted file mode 100644 index 0470a94..0000000 Binary files a/lib/assets/images/categories/business.png and /dev/null differ diff --git a/lib/assets/images/categories/economic-dark.svg b/lib/assets/images/categories/economic-dark.svg new file mode 100644 index 0000000..3700deb --- /dev/null +++ b/lib/assets/images/categories/economic-dark.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/lib/assets/images/categories/economic-light.svg b/lib/assets/images/categories/economic-light.svg new file mode 100644 index 0000000..0513930 --- /dev/null +++ b/lib/assets/images/categories/economic-light.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/lib/assets/images/categories/economic.png b/lib/assets/images/categories/economic.png deleted file mode 100644 index 44f67fb..0000000 Binary files a/lib/assets/images/categories/economic.png and /dev/null differ diff --git a/lib/assets/images/categories/enviromental-dark.svg b/lib/assets/images/categories/enviromental-dark.svg new file mode 100644 index 0000000..b59ba84 --- /dev/null +++ b/lib/assets/images/categories/enviromental-dark.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/assets/images/categories/enviromental-light.svg b/lib/assets/images/categories/enviromental-light.svg new file mode 100644 index 0000000..ce38dd6 --- /dev/null +++ b/lib/assets/images/categories/enviromental-light.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/assets/images/categories/enviromental.png b/lib/assets/images/categories/enviromental.png deleted file mode 100644 index f56f648..0000000 Binary files a/lib/assets/images/categories/enviromental.png and /dev/null differ diff --git a/lib/assets/images/categories/political-dark.svg b/lib/assets/images/categories/political-dark.svg new file mode 100644 index 0000000..ae8f3ad --- /dev/null +++ b/lib/assets/images/categories/political-dark.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/lib/assets/images/categories/political-light.svg b/lib/assets/images/categories/political-light.svg new file mode 100644 index 0000000..c8f71b4 --- /dev/null +++ b/lib/assets/images/categories/political-light.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/lib/assets/images/categories/political.png b/lib/assets/images/categories/political.png deleted file mode 100644 index 06ea42a..0000000 Binary files a/lib/assets/images/categories/political.png and /dev/null differ diff --git a/lib/assets/images/categories/social-dark.svg b/lib/assets/images/categories/social-dark.svg new file mode 100644 index 0000000..c4a0fb8 --- /dev/null +++ b/lib/assets/images/categories/social-dark.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/lib/assets/images/categories/social-light.svg b/lib/assets/images/categories/social-light.svg new file mode 100644 index 0000000..2b28dac --- /dev/null +++ b/lib/assets/images/categories/social-light.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/lib/assets/images/categories/social.png b/lib/assets/images/categories/social.png deleted file mode 100644 index 2b4e4ea..0000000 Binary files a/lib/assets/images/categories/social.png and /dev/null differ diff --git a/lib/assets/images/categories/tech-dark.svg b/lib/assets/images/categories/tech-dark.svg new file mode 100644 index 0000000..3fa3e80 --- /dev/null +++ b/lib/assets/images/categories/tech-dark.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/lib/assets/images/categories/tech-light.svg b/lib/assets/images/categories/tech-light.svg new file mode 100644 index 0000000..ec8743a --- /dev/null +++ b/lib/assets/images/categories/tech-light.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/lib/assets/images/categories/tech.png b/lib/assets/images/categories/tech.png deleted file mode 100644 index 76bdc49..0000000 Binary files a/lib/assets/images/categories/tech.png and /dev/null differ diff --git a/lib/assets/images/logos/logo-h-t.png b/lib/assets/images/logos/logo-h-t.png deleted file mode 100644 index 1d259cc..0000000 Binary files a/lib/assets/images/logos/logo-h-t.png and /dev/null differ diff --git a/lib/assets/images/logos/logo-horizontal-dark.svg b/lib/assets/images/logos/logo-horizontal-dark.svg new file mode 100644 index 0000000..ab9db91 --- /dev/null +++ b/lib/assets/images/logos/logo-horizontal-dark.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/assets/images/logos/logo-horizontal-light.svg b/lib/assets/images/logos/logo-horizontal-light.svg new file mode 100644 index 0000000..04e066b --- /dev/null +++ b/lib/assets/images/logos/logo-horizontal-light.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/assets/images/logos/logo-v-t.png b/lib/assets/images/logos/logo-v-t.png deleted file mode 100644 index 9d27213..0000000 Binary files a/lib/assets/images/logos/logo-v-t.png and /dev/null differ diff --git a/lib/assets/images/logos/logo-vertical-dark.svg b/lib/assets/images/logos/logo-vertical-dark.svg new file mode 100644 index 0000000..0cf46df --- /dev/null +++ b/lib/assets/images/logos/logo-vertical-dark.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/assets/images/logos/logo-vertical-light.svg b/lib/assets/images/logos/logo-vertical-light.svg new file mode 100644 index 0000000..0f4c57f --- /dev/null +++ b/lib/assets/images/logos/logo-vertical-light.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/assets/images/themes/dark-theme.svg b/lib/assets/images/themes/dark-theme.svg new file mode 100644 index 0000000..ce96210 --- /dev/null +++ b/lib/assets/images/themes/dark-theme.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/assets/images/themes/light-theme.svg b/lib/assets/images/themes/light-theme.svg new file mode 100644 index 0000000..93ac8c4 --- /dev/null +++ b/lib/assets/images/themes/light-theme.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/lib/config/design_config.dart b/lib/config/design_config.dart index 3b009c9..3c74136 100644 --- a/lib/config/design_config.dart +++ b/lib/config/design_config.dart @@ -15,10 +15,10 @@ class DesignConfig { Radius.circular(16), ); - static final Border cardBorder = Border.all( - color: Theme.of(context).colorScheme.cardBorder, - width: 0.25, - ); + static Border get cardBorder => Border.all( + color: Theme.of(context).colorScheme.cardBorder, + width: 0.5, + ); static List get defaultShadow => [ BoxShadow( @@ -28,6 +28,8 @@ class DesignConfig { ) ]; + static Brightness get brightness => Theme.of(context).brightness; + static const Duration lowAnimationDuration = Duration(milliseconds: 300); static const Duration mediumAnimationDuration = Duration(milliseconds: 600); @@ -37,12 +39,12 @@ class DesignConfig { ? Brightness.light : Brightness.dark, statusBarColor: Colors.transparent, - systemNavigationBarColor: Theme.of(context).colorScheme.surface, - systemNavigationBarDividerColor: Colors.transparent, - systemNavigationBarIconBrightness: - Theme.of(context).colorScheme.brightness == Brightness.dark - ? Brightness.light - : Brightness.dark, + // systemNavigationBarColor: Theme.of(context).colorScheme.surface, + // systemNavigationBarDividerColor: Colors.transparent, + // systemNavigationBarIconBrightness: + // Theme.of(context).colorScheme.brightness == Brightness.dark + // ? Brightness.light + // : Brightness.dark, ); } diff --git a/lib/constants/assets.dart b/lib/constants/assets.dart index 59af3fe..e22b26f 100644 --- a/lib/constants/assets.dart +++ b/lib/constants/assets.dart @@ -1,24 +1,36 @@ +import 'package:didvan/config/design_config.dart'; +import 'package:flutter/cupertino.dart'; + class Assets { static const String _basePath = 'lib/assets'; static const String _baseImagesPath = _basePath + '/images'; + static const String _baseThemesPath = _basePath + '/images/themes'; static const String _baseAnimationsPath = _basePath + '/animations'; - static const String verticalLogoWithText = - _baseImagesPath + '/logos/logo-v-t.png'; - static const String horizontalLogoWithText = - _baseImagesPath + '/logos/logo-h-t.png'; + static String get verticalLogoWithText => + _baseImagesPath + '/logos/logo-vertical-$_themeSuffix.svg'; + static String get horizontalLogoWithText => + _baseImagesPath + '/logos/logo-horizontal-$_themeSuffix.svg'; static const String logoLoadingAnimation = _baseAnimationsPath + '/indicator.riv'; - static const businessCategoryIcon = - _baseImagesPath + '/categories/business.png'; - static const economicCategoryIcon = - _baseImagesPath + '/categories/economic.png'; - static const enviromentalCategoryIcon = - _baseImagesPath + '/categories/enviromental.png'; - static const politicalCategoryIcon = - _baseImagesPath + '/categories/political.png'; - static const socialCategoryIcon = _baseImagesPath + '/categories/social.png'; - static const techCategoryIcon = _baseImagesPath + '/categories/tech.png'; + static String get businessCategoryIcon => + _baseImagesPath + '/categories/business-$_themeSuffix.svg'; + static String get economicCategoryIcon => + _baseImagesPath + '/categories/economic-$_themeSuffix.svg'; + static String get enviromentalCategoryIcon => + _baseImagesPath + '/categories/enviromental-$_themeSuffix.svg'; + static String get politicalCategoryIcon => + _baseImagesPath + '/categories/political-$_themeSuffix.svg'; + static String get socialCategoryIcon => + _baseImagesPath + '/categories/social-$_themeSuffix.svg'; + static String get techCategoryIcon => + _baseImagesPath + '/categories/tech-$_themeSuffix.svg'; + + static const String lightTheme = _baseThemesPath + '/light-theme.svg'; + static const String darkTheme = _baseThemesPath + '/dark-theme.svg'; + + static String get _themeSuffix => + DesignConfig.brightness == Brightness.dark ? 'dark' : 'light'; } diff --git a/lib/main.dart b/lib/main.dart index 878f462..87129d8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,5 @@ -import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; +import 'package:didvan/providers/theme_provider.dart'; import 'package:didvan/providers/user_provider.dart'; import 'package:didvan/routes/route_generator.dart'; import 'package:flutter/material.dart'; @@ -16,28 +16,33 @@ class Didvan extends StatelessWidget { Widget build(BuildContext context) { return MultiProvider( providers: [ - ChangeNotifierProvider( + ChangeNotifierProvider( create: (context) => UserProvider(), ), + ChangeNotifierProvider( + create: (context) => ThemeProvider(), + ), ], - child: MaterialApp( - debugShowCheckedModeBanner: false, - title: 'Didvan', - theme: LightThemeConfig.themeData, - darkTheme: DarkThemeConfig.themeData, - color: LightThemeConfig.themeData.primaryColor, - themeMode: ThemeMode.light, - onGenerateRoute: (settings) => RouteGenerator.generateRoute(settings), - initialRoute: '/', - localizationsDelegates: const [ - GlobalCupertinoLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ], - supportedLocales: const [ - Locale("fa", "IR"), - ], - locale: const Locale("fa", "IR"), + child: Consumer( + builder: (context, themeProvider, child) => MaterialApp( + debugShowCheckedModeBanner: false, + title: 'Didvan', + theme: LightThemeConfig.themeData, + darkTheme: DarkThemeConfig.themeData, + color: LightThemeConfig.themeData.primaryColor, + themeMode: themeProvider.themeMode, + onGenerateRoute: (settings) => RouteGenerator.generateRoute(settings), + initialRoute: '/', + localizationsDelegates: const [ + GlobalCupertinoLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: const [ + Locale("fa", "IR"), + ], + locale: const Locale("fa", "IR"), + ), ), ); } diff --git a/lib/models/settings_data.dart b/lib/models/settings_data.dart new file mode 100644 index 0000000..e8404a9 --- /dev/null +++ b/lib/models/settings_data.dart @@ -0,0 +1,27 @@ +class SettingsData { + final String brightness; + final List notificationTimeRange; + final String fontFamily; + final double fontSizeScale; + + SettingsData( + this.brightness, + this.notificationTimeRange, + this.fontFamily, + this.fontSizeScale, + ); + + factory SettingsData.fromJson(Map json) => SettingsData( + json['brightness'], + json['notificationTimeRange'], + json['fontFamily'], + json['fontSizeScale'], + ); + + Map toJson() => { + 'brightness': brightness, + 'notificationTimeRange': notificationTimeRange, + 'fontFamily': fontFamily, + 'fontSizeScale': fontSizeScale, + }; +} diff --git a/lib/pages/home/profile/edit_profile/edit_profile.dart b/lib/pages/home/profile/edit_profile/edit_profile.dart index 1bd7628..c459622 100644 --- a/lib/pages/home/profile/edit_profile/edit_profile.dart +++ b/lib/pages/home/profile/edit_profile/edit_profile.dart @@ -16,55 +16,47 @@ class EditProfile extends StatelessWidget { return DidvanScaffold( appBarData: AppBarData(title: 'ویرایش پروفایل'), slivers: [ - const SliverPadding( - padding: EdgeInsets.only(top: 16), - sliver: SliverToBoxAdapter( - child: ProfilePhoto(), - ), - ), - SliverPadding( - padding: const EdgeInsets.all(16), - sliver: SliverToBoxAdapter( - child: DidvanCard( - child: Column( - children: [ - DidvanTextField( - title: 'نام کاربری', - hintText: 'انتخاب نام کاربری (اختیاری)', - onChanged: (value) { - //TODO: Needs implementation - }, - ), - const SizedBox(height: 16), - DidvanTextField( - title: 'ایمیل', - hintText: 'مثال: example@email.com', - onChanged: (value) { - //TODO: Meeds implementaion - }, - ), - const SizedBox(height: 16), - const DidvanTextField( - title: 'موبایل', - enabled: false, - hintText: '09123456789', //TODO: Needs api - ), - const SizedBox(height: 16), - DidvanSwitch( - value: true, //TODO: Needs api - title: 'ورود با اثر انگشت', - onChanged: (value) => { - //TODO: Needs implementaion - }, - ), - const DidvanDivider(), - MenuItem( - title: 'تغییر رمز عبور', - onTap: () {}, - ), - ], + const SizedBox(height: 16), + const ProfilePhoto(), + const SizedBox(height: 16), + DidvanCard( + child: Column( + children: [ + DidvanTextField( + title: 'نام کاربری', + hintText: 'انتخاب نام کاربری (اختیاری)', + onChanged: (value) { + //TODO: Needs implementation + }, ), - ), + const SizedBox(height: 16), + DidvanTextField( + title: 'ایمیل', + hintText: 'مثال: example@email.com', + onChanged: (value) { + //TODO: Meeds implementaion + }, + ), + const SizedBox(height: 16), + const DidvanTextField( + title: 'موبایل', + enabled: false, + hintText: '09123456789', //TODO: Needs api + ), + const SizedBox(height: 16), + DidvanSwitch( + value: true, //TODO: Needs api + title: 'ورود با اثر انگشت', + onChanged: (value) => { + //TODO: Needs implementaion + }, + ), + const DidvanDivider(), + MenuItem( + title: 'تغییر رمز عبور', + onTap: () {}, + ), + ], ), ), ], diff --git a/lib/pages/home/profile/profile.dart b/lib/pages/home/profile/profile.dart index d6fc48c..805099b 100644 --- a/lib/pages/home/profile/profile.dart +++ b/lib/pages/home/profile/profile.dart @@ -1,12 +1,14 @@ import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/pages/home/profile/widgets/menu_item.dart'; import 'package:didvan/pages/home/widgets/logo_app_bar.dart'; +import 'package:didvan/providers/theme_provider.dart'; import 'package:didvan/routes/routes.dart'; import 'package:didvan/widgets/didvan/card.dart'; import 'package:didvan/widgets/didvan/divider.dart'; import 'package:didvan/widgets/didvan/text.dart'; import 'package:didvan/widgets/item_title.dart'; import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; class Profile extends StatelessWidget { const Profile({Key? key}) : super(key: key); @@ -18,7 +20,12 @@ class Profile extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const LogoAppBar(hasExtraPadding: false), + Consumer( + builder: (context, themeProvider, child) => LogoAppBar( + hasExtraPadding: false, + key: UniqueKey(), + ), + ), const SizedBox(height: 16), DidvanCard( child: Column( diff --git a/lib/pages/home/profile/settings/settings.dart b/lib/pages/home/profile/settings/settings.dart index e445f8a..c73bf6b 100644 --- a/lib/pages/home/profile/settings/settings.dart +++ b/lib/pages/home/profile/settings/settings.dart @@ -1,23 +1,124 @@ +import 'package:day_night_time_picker/lib/daynight_timepicker.dart'; +import 'package:didvan/config/design_config.dart'; +import 'package:didvan/constants/app_icons.dart'; +import 'package:didvan/constants/assets.dart'; import 'package:didvan/models/view/app_bar_data.dart'; +import 'package:didvan/pages/home/profile/settings/settings_state.dart'; import 'package:didvan/pages/home/profile/widgets/menu_item.dart'; +import 'package:didvan/providers/theme_provider.dart'; import 'package:didvan/widgets/didvan/card.dart'; +import 'package:didvan/widgets/didvan/divider.dart'; import 'package:didvan/widgets/didvan/scaffold.dart'; +import 'package:didvan/widgets/didvan/text.dart'; +import 'package:didvan/widgets/item_title.dart'; +import 'package:didvan/widgets/state_handler.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:provider/provider.dart'; class Settings extends StatelessWidget { const Settings({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return DidvanScaffold( - appBarData: AppBarData(hasBack: true, title: 'تنظیمات'), - slivers: [ - SliverToBoxAdapter( - child: DidvanCard( - child: MenuItem(title: 'زمان دریافت اعلان', onTap: () {}), - ), + return Consumer( + builder: (context, state, child) => StateHandler( + state: context.read(), + builder: (context, state) => DidvanScaffold( + appBarData: AppBarData(hasBack: true, title: 'تنظیمات'), + slivers: [ + DidvanCard( + child: MenuItem( + title: 'زمان دریافت اعلان', + onTap: () => _pickTimeRange(context), + icon: DidvanIcons.notification_regular, + suffix: state.notificationTimeRange[0], + ), + ), + const ItemTitle( + title: 'انتخاب قلم', + icon: DidvanIcons.font_solid, + ), + DidvanCard( + child: Column( + children: [ + MenuItem( + suffix: 'نیاز به پیاده‌سازی', + title: 'فونت برنامه', + onTap: () {}, + ), + const DidvanDivider(), + MenuItem( + suffix: 'نیاز به پیاده‌سازی', + title: 'اندازه متن', + onTap: () {}, + ), + ], + ), + ), + const ItemTitle( + title: 'ظاهر برنامه', + icon: DidvanIcons.theme_solid, + ), + DidvanCard( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + _themeItem(context, state, 'light'), + _themeItem(context, state, 'dark'), + ], + ), + ), + ], ), - ], + ), + ); + } + + Future _pickTimeRange(context) async { + final SettingsState state = context.read(); + Navigator.of(context).push( + showPicker( + context: context, + value: const TimeOfDay(hour: 0, minute: 0), + onChange: (time) { + String hour = time.toString().split(':')[0]; + state.notificationTimeRange = []; + }, + ), + ); + } + + Widget _themeItem( + BuildContext context, SettingsState state, String brightness) { + final bool isDarkTheme = brightness == 'dark'; + return GestureDetector( + onTap: () { + state.brightness = brightness; + if (isDarkTheme && Theme.of(context).brightness == Brightness.dark) { + return; + } else if (!isDarkTheme && + Theme.of(context).brightness == Brightness.light) { + return; + } else { + if (isDarkTheme) { + context.read().themeMode = ThemeMode.dark; + DesignConfig.updateSystemUiOverlayStyle(); + } else { + context.read().themeMode = ThemeMode.light; + DesignConfig.updateSystemUiOverlayStyle(); + } + } + }, + child: Column( + children: [ + SvgPicture.asset(isDarkTheme ? Assets.darkTheme : Assets.lightTheme), + const SizedBox(height: 8), + DidvanText( + isDarkTheme ? 'تیره' : 'روشن', + ), + ], + ), ); } } diff --git a/lib/pages/home/profile/settings/settings_state.dart b/lib/pages/home/profile/settings/settings_state.dart new file mode 100644 index 0000000..0f0e973 --- /dev/null +++ b/lib/pages/home/profile/settings/settings_state.dart @@ -0,0 +1,73 @@ +import 'package:didvan/models/enums.dart'; +import 'package:didvan/providers/core_provider.dart'; +import 'package:didvan/services/storage/storage.dart'; + +class SettingsState extends CoreProvier { + SettingsState() { + getSettingsFromStorage(); + } + + List _notificationTimeRange = []; + String _fontFamily = 'Dana-FA'; + double _fontSizeScale = 1; + String _brightness = 'light'; + + set notificationTimeRange(List value) { + _notificationTimeRange = value; + StorageService.setValue( + key: 'notificationTimeRange', + value: value, + boxName: 'settings', + ); + } + + List get notificationTimeRange => _notificationTimeRange; + + set fontFamily(String value) { + _fontFamily = value; + StorageService.setValue( + key: 'fontFamily', + value: value, + boxName: 'settings', + ); + notifyListeners(); + } + + String get fontFamily => _fontFamily; + + set fontSizeScale(double value) { + _fontSizeScale = value; + StorageService.setValue( + key: 'fontSizeScale', + value: value, + boxName: 'settings', + ); + notifyListeners(); + } + + double get fontSizeScale => _fontSizeScale; + + set brightness(String value) { + _brightness = value; + StorageService.setValue( + key: 'brightness', + value: value, + boxName: 'settings', + ); + notifyListeners(); + } + + String get brightness => _brightness; + + Future getSettingsFromStorage() async { + appState = AppState.busy; + final Map settingsData = await StorageService.getBoxData( + boxName: 'settings', + ); + _notificationTimeRange = settingsData['notificationTimeRange']; + _fontFamily = settingsData['fontFamily']; + _fontSizeScale = settingsData['fontSizeScale']; + _brightness = settingsData['brightness']; + appState = AppState.idle; + } +} diff --git a/lib/pages/home/profile/widgets/menu_item.dart b/lib/pages/home/profile/widgets/menu_item.dart index 5f4e855..f6956a3 100644 --- a/lib/pages/home/profile/widgets/menu_item.dart +++ b/lib/pages/home/profile/widgets/menu_item.dart @@ -33,6 +33,15 @@ class MenuItem extends StatelessWidget { color: color ?? Theme.of(context).colorScheme.title, ), const Spacer(), + if (suffix != null) + DidvanText( + suffix!, + style: Theme.of(context) + .textTheme + .subtitle2! + .copyWith(fontWeight: FontWeight.w400), + color: Theme.of(context).colorScheme.primary, + ), trailing ?? Icon( DidvanIcons.angle_left_regular, diff --git a/lib/pages/home/radar/widgets/category_item.dart b/lib/pages/home/radar/widgets/category_item.dart index bfbdbbb..b9bec99 100644 --- a/lib/pages/home/radar/widgets/category_item.dart +++ b/lib/pages/home/radar/widgets/category_item.dart @@ -3,6 +3,7 @@ import 'package:didvan/config/theme_data.dart'; import 'package:didvan/widgets/animated_visibility.dart'; import 'package:didvan/widgets/didvan/text.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/flutter_svg.dart'; class CategoryItem extends StatelessWidget { final String title; @@ -45,9 +46,7 @@ class CategoryItem extends StatelessWidget { borderRadius: DesignConfig.mediumBorderRadius, ), padding: const EdgeInsets.all(8), - child: Image.asset( - asset, - ), + child: SvgPicture.asset(asset), ), ), const SizedBox( diff --git a/lib/pages/splash/splash.dart b/lib/pages/splash/splash.dart index 7e2645b..5d7de57 100644 --- a/lib/pages/splash/splash.dart +++ b/lib/pages/splash/splash.dart @@ -1,10 +1,14 @@ import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/main.dart'; +import 'package:didvan/pages/splash/splash_state.dart'; +import 'package:didvan/providers/theme_provider.dart'; import 'package:didvan/routes/routes.dart'; +import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/widgets/logos/didvan_horizontal_logo.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:provider/provider.dart'; class Splash extends StatefulWidget { const Splash({Key? key}) : super(key: key); @@ -14,31 +18,47 @@ class Splash extends StatefulWidget { } class _SplashState extends State { + bool _isGettingThemeData = true; + @override void initState() { - Future.delayed( - const Duration(seconds: 2), - () { - Navigator.of(context).pushReplacementNamed( - Routes.home, - ); - }, - ); + _initialize(); super.initState(); } + Future _initialize() async { + await AppInitializer.setupServices(); + final ThemeMode themeMode = await AppInitializer.initilizeSettings(); + context.read().themeMode = themeMode; + await Future.delayed( + const Duration(milliseconds: 200), + () => setState(() { + _isGettingThemeData = false; + }), + ); + await Future.delayed(const Duration(seconds: 2)); + Navigator.of(context).pushReplacementNamed(Routes.home); + } + @override Widget build(BuildContext context) { DesignConfig.context = context; + if (_isGettingThemeData) { + return Container( + color: Colors.black, + height: double.infinity, + width: double.infinity, + ); + } return AnnotatedRegion( value: DesignConfig.systemUiOverlayStyle.copyWith( - systemNavigationBarColor: Theme.of(context).colorScheme.focused, - ), + // systemNavigationBarColor: Theme.of(context).colorScheme.focused, + ), child: Scaffold( body: Container( alignment: Alignment.center, padding: const EdgeInsets.all(60), - color: Theme.of(context).colorScheme.focused, + color: Theme.of(context).colorScheme.background, child: const DidvanVerticalLogo(), ), ), diff --git a/lib/providers/core_provider.dart b/lib/providers/core_provider.dart index 3238711..45ef78c 100644 --- a/lib/providers/core_provider.dart +++ b/lib/providers/core_provider.dart @@ -9,5 +9,7 @@ class CoreProvier with ChangeNotifier { notifyListeners(); } + void update() => notifyListeners(); + AppState get appState => _appState; } diff --git a/lib/providers/theme_provider.dart b/lib/providers/theme_provider.dart index 638c1c7..ab095b2 100644 --- a/lib/providers/theme_provider.dart +++ b/lib/providers/theme_provider.dart @@ -2,5 +2,12 @@ import 'package:didvan/providers/core_provider.dart'; import 'package:flutter/material.dart'; class ThemeProvider extends CoreProvier { - late ThemeMode themeMode; + ThemeMode _themeMode = ThemeMode.system; + + set themeMode(ThemeMode value) { + _themeMode = value; + notifyListeners(); + } + + ThemeMode get themeMode => _themeMode; } diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart index 4fdba0a..7b05f54 100644 --- a/lib/routes/route_generator.dart +++ b/lib/routes/route_generator.dart @@ -4,6 +4,7 @@ import 'package:didvan/pages/home/home.dart'; import 'package:didvan/pages/home/home_state.dart'; import 'package:didvan/pages/home/profile/edit_profile/edit_profile.dart'; import 'package:didvan/pages/home/profile/settings/settings.dart'; +import 'package:didvan/pages/home/profile/settings/settings_state.dart'; import 'package:didvan/pages/splash/splash.dart'; import 'package:didvan/pages/splash/splash_state.dart'; import 'package:didvan/routes/routes.dart'; @@ -40,7 +41,10 @@ class RouteGenerator { ); case Routes.settings: return _createRoute( - const Settings(), + ChangeNotifierProvider( + create: (context) => SettingsState(), + child: const Settings(), + ), ); default: return _errorRoute(); diff --git a/lib/services/app_initalizer.dart b/lib/services/app_initalizer.dart new file mode 100644 index 0000000..ac76df2 --- /dev/null +++ b/lib/services/app_initalizer.dart @@ -0,0 +1,37 @@ +import 'dart:io'; + +import 'package:didvan/models/settings_data.dart'; +import 'package:didvan/services/storage/storage.dart'; +import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; +import 'package:path_provider/path_provider.dart'; + +class AppInitializer { + static Future setupServices() async { + final Directory appDir = await getApplicationDocumentsDirectory(); + Hive.init(appDir.path); + } + + static Future initilizeSettings() async { + final bool settingsBoxExists = await StorageService.boxExists( + boxName: 'settings', + ); + if (settingsBoxExists) { + final String brightness = + await StorageService.getValue(key: 'brightness', boxName: 'settings'); + return brightness == 'dark' ? ThemeMode.dark : ThemeMode.light; + } else { + final SettingsData settingsData = SettingsData( + 'light', + ['00:00', '23:59'], + 'Dana-FA', + 1, + ); + await StorageService.setBoxData( + data: settingsData.toJson(), + boxName: 'settings', + ); + return ThemeMode.light; + } + } +} diff --git a/lib/services/request_handler.dart b/lib/services/network/request.dart similarity index 99% rename from lib/services/request_handler.dart rename to lib/services/network/request.dart index 462ee4d..fb2c7b7 100644 --- a/lib/services/request_handler.dart +++ b/lib/services/network/request.dart @@ -3,7 +3,7 @@ import 'dart:developer'; import 'dart:io'; import 'package:http/http.dart' as http; -class RequestHandler { +class RequestService { static late String token; String? errorMessage; @@ -18,7 +18,7 @@ class RequestHandler { bool isSuccess = false; - RequestHandler( + RequestService( this.url, { Map? requestHeaders, body, diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart new file mode 100644 index 0000000..4e25ff3 --- /dev/null +++ b/lib/services/network/request_helper.dart @@ -0,0 +1 @@ +class RequestHelper {} diff --git a/lib/services/storage/storage.dart b/lib/services/storage/storage.dart new file mode 100644 index 0000000..ec01ccf --- /dev/null +++ b/lib/services/storage/storage.dart @@ -0,0 +1,40 @@ +import 'package:hive/hive.dart'; + +class StorageService { + static Future setValue({ + required String key, + required dynamic value, + required String boxName, + }) async { + final Box box = await Hive.openBox(boxName); + await box.put(key, value); + await box.close(); + } + + static Future getValue({required String key, required String boxName}) async { + final Box box = await Hive.openBox(boxName); + final value = await box.get(key); + await box.close(); + return value; + } + + static Future setBoxData({ + required Map data, + required String boxName, + }) async { + final Box box = await Hive.openBox(boxName); + await box.putAll(data); + await box.close(); + } + + static Future getBoxData({required String boxName}) async { + final Box box = await Hive.openBox(boxName); + await box.close(); + return box.toMap(); + } + + static Future boxExists({required String boxName}) async { + final bool result = await Hive.boxExists(boxName); + return result; + } +} diff --git a/lib/utils/actions_sheet.dart b/lib/utils/actions_sheet.dart index 50b2a2a..ee324d4 100644 --- a/lib/utils/actions_sheet.dart +++ b/lib/utils/actions_sheet.dart @@ -24,8 +24,8 @@ class ActionSheetUtils { static AnnotatedRegion _customSystemOverlayStyle({required Widget child}) { return AnnotatedRegion( value: DesignConfig.systemUiOverlayStyle.copyWith( - systemNavigationBarColor: Colors.black45, - ), + // systemNavigationBarColor: Colors.black45, + ), child: child, ); } diff --git a/lib/utils/date_time.dart b/lib/utils/date_time.dart new file mode 100644 index 0000000..f36466d --- /dev/null +++ b/lib/utils/date_time.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +class DateTimeUtils { + static TimeOfDay stringToTimeOfDay(String input) => TimeOfDay( + hour: int.parse(input.split(':')[0]), + minute: int.parse(input.split(':')[1]), + ); +} diff --git a/lib/widgets/didvan/app_bar.dart b/lib/widgets/didvan/app_bar.dart index c14902d..d9d244c 100644 --- a/lib/widgets/didvan/app_bar.dart +++ b/lib/widgets/didvan/app_bar.dart @@ -1,4 +1,3 @@ -import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/theme_data.dart'; import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/widgets/didvan/text.dart'; diff --git a/lib/widgets/didvan/scaffold.dart b/lib/widgets/didvan/scaffold.dart index 450b1d2..69fa6d7 100644 --- a/lib/widgets/didvan/scaffold.dart +++ b/lib/widgets/didvan/scaffold.dart @@ -2,7 +2,7 @@ import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/widgets/didvan/app_bar.dart'; import 'package:flutter/material.dart'; -class DidvanScaffold extends StatefulWidget { +class DidvanScaffold extends StatelessWidget { final List? slivers; final AppBarData appBarData; final bool hasPadding; @@ -13,20 +13,6 @@ class DidvanScaffold extends StatefulWidget { this.hasPadding = true, }) : super(key: key); - @override - State createState() => _DidvanScaffoldState(); -} - -class _DidvanScaffoldState extends State { - bool _isElevated = false; - final ScrollController _scrollController = ScrollController(); - - @override - void initState() { - _scrollController.addListener(() => _handleAppBarElevation()); - super.initState(); - } - @override Widget build(BuildContext context) { return Scaffold( @@ -36,24 +22,23 @@ class _DidvanScaffoldState extends State { backgroundColor: Theme.of(context).backgroundColor, automaticallyImplyLeading: false, pinned: true, - flexibleSpace: DidvanAppBar(appBarData: widget.appBarData), + flexibleSpace: DidvanAppBar(appBarData: appBarData), ), - if (widget.slivers != null) ...widget.slivers! + const SliverToBoxAdapter( + child: SizedBox(height: 16), + ), + if (slivers != null) + SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 16), + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (_, index) => slivers![index], + childCount: slivers!.length, + ), + ), + ), ], ), ); } - - void _handleAppBarElevation() { - if (_scrollController.position.pixels > kToolbarHeight && !_isElevated) { - setState(() { - _isElevated = true; - }); - } - if (_scrollController.position.pixels < kToolbarHeight && _isElevated) { - setState(() { - _isElevated = false; - }); - } - } } diff --git a/lib/widgets/loading_indicators.dart b/lib/widgets/loading_indicators.dart new file mode 100644 index 0000000..a47f241 --- /dev/null +++ b/lib/widgets/loading_indicators.dart @@ -0,0 +1 @@ +import 'package:flutter/material.dart'; diff --git a/lib/widgets/logos/didvan_horizontal_logo.dart b/lib/widgets/logos/didvan_horizontal_logo.dart index 19c2aaf..e9631d6 100644 --- a/lib/widgets/logos/didvan_horizontal_logo.dart +++ b/lib/widgets/logos/didvan_horizontal_logo.dart @@ -1,11 +1,12 @@ import 'package:didvan/constants/assets.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; class DidvanVerticalLogo extends StatelessWidget { const DidvanVerticalLogo({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return Image.asset(Assets.verticalLogoWithText); + return SvgPicture.asset(Assets.verticalLogoWithText); } } diff --git a/lib/widgets/logos/didvan_vertical_logo.dart b/lib/widgets/logos/didvan_vertical_logo.dart index 19e640e..4f5637a 100644 --- a/lib/widgets/logos/didvan_vertical_logo.dart +++ b/lib/widgets/logos/didvan_vertical_logo.dart @@ -1,11 +1,12 @@ import 'package:didvan/constants/assets.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; class DidvanHorizontalLogo extends StatelessWidget { const DidvanHorizontalLogo({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - return Image.asset(Assets.horizontalLogoWithText); + return SvgPicture.asset(Assets.horizontalLogoWithText); } } diff --git a/lib/widgets/state_handler.dart b/lib/widgets/state_handler.dart new file mode 100644 index 0000000..0fc9a6f --- /dev/null +++ b/lib/widgets/state_handler.dart @@ -0,0 +1,48 @@ +import 'package:didvan/models/enums.dart'; +import 'package:didvan/providers/core_provider.dart'; +import 'package:flutter/material.dart'; + +class StateHandler extends StatelessWidget { + final T state; + final Widget Function(BuildContext context, T state) builder; + final VoidCallback? onRefresh; + final bool enableCustomLoadingIndicator; + final Color? customLoadingIndicatorColor; + final bool enableEmptyState; + final Widget? emptyState; + final double topPadding; + const StateHandler({ + Key? key, + required this.builder, + this.emptyState, + this.enableCustomLoadingIndicator = false, + this.enableEmptyState = false, + this.onRefresh, + this.topPadding = 0, + this.customLoadingIndicatorColor, + required this.state, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + if (enableEmptyState && state.appState == AppState.idle) { + return Container( + padding: EdgeInsets.only(top: topPadding), + alignment: Alignment.center, + child: SingleChildScrollView( + child: emptyState ?? const Text('Custom Widget Required!'), + ), + ); + } + switch (state.appState) { + case AppState.idle: + return builder(context, state); + case AppState.busy: + return const CircularProgressIndicator(); + case AppState.failed: + return enableCustomLoadingIndicator ? emptyState! : Container(); + default: + return Container(); + } + } +} diff --git a/pubspec.lock b/pubspec.lock index e771f17..cecec2c 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -64,6 +64,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.4" + day_night_time_picker: + dependency: "direct main" + description: + name: day_night_time_picker + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.3+1" fake_async: dependency: transitive description: @@ -71,6 +78,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.2.0" + ffi: + dependency: transitive + description: + name: ffi + url: "https://pub.dartlang.org" + source: hosted + version: "1.1.2" + file: + dependency: transitive + description: + name: file + url: "https://pub.dartlang.org" + source: hosted + version: "6.1.2" flutter: dependency: "direct main" description: flutter @@ -95,6 +116,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.5" + flutter_spinkit: + dependency: "direct main" + description: + name: flutter_spinkit + url: "https://pub.dartlang.org" + source: hosted + version: "5.1.0" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -203,6 +238,76 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.0" + path_drawing: + dependency: transitive + description: + name: path_drawing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + path_parsing: + dependency: transitive + description: + name: path_parsing + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.8" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.9" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.7" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + url: "https://pub.dartlang.org" + source: hosted + version: "2.1.4" + path_provider_macos: + dependency: transitive + description: + name: path_provider_macos + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.1" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.4" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "4.4.0" pin_code_fields: dependency: "direct main" description: @@ -210,6 +315,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "7.3.0" + platform: + dependency: transitive + description: + name: platform + url: "https://pub.dartlang.org" + source: hosted + version: "3.1.0" plugin_platform_interface: dependency: transitive description: @@ -217,6 +329,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.2" + process: + dependency: transitive + description: + name: process + url: "https://pub.dartlang.org" + source: hosted + version: "4.2.4" provider: dependency: "direct main" description: @@ -292,6 +411,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.1" + win32: + dependency: transitive + description: + name: win32 + url: "https://pub.dartlang.org" + source: hosted + version: "2.3.2" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.0" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "5.3.1" sdks: dart: ">=2.14.0 <3.0.0" flutter: ">=2.5.0" diff --git a/pubspec.yaml b/pubspec.yaml index aac10b9..e070249 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,10 @@ dependencies: rive: ^0.7.33 hive: ^2.0.5 image_picker: ^0.8.4+4 + day_night_time_picker: ^1.0.3+1 + path_provider: ^2.0.8 + flutter_spinkit: ^5.1.0 + flutter_svg: ^1.0.0 dev_dependencies: flutter_test: @@ -66,15 +70,24 @@ flutter: # To add assets to your application, add an assets section, like this: assets: - - lib/assets/images/logos/logo-v-t.png - - lib/assets/images/logos/logo-h-t.png - - lib/assets/images/categories/business.png - - lib/assets/images/categories/economic.png - - lib/assets/images/categories/enviromental.png - - lib/assets/images/categories/political.png - - lib/assets/images/categories/social.png - - lib/assets/images/categories/tech.png - - lib/assets/animations/indicator.riv + - lib/assets/images/logos/logo-vertical-dark.svg + - lib/assets/images/logos/logo-vertical-light.svg + - lib/assets/images/logos/logo-horizontal-dark.svg + - lib/assets/images/logos/logo-horizontal-light.svg + - lib/assets/images/categories/business-light.svg + - lib/assets/images/categories/economic-light.svg + - lib/assets/images/categories/enviromental-light.svg + - lib/assets/images/categories/political-light.svg + - lib/assets/images/categories/social-light.svg + - lib/assets/images/categories/tech-light.svg + - lib/assets/images/categories/business-dark.svg + - lib/assets/images/categories/economic-dark.svg + - lib/assets/images/categories/enviromental-dark.svg + - lib/assets/images/categories/political-dark.svg + - lib/assets/images/categories/social-dark.svg + - lib/assets/images/categories/tech-dark.svg + - lib/assets/images/themes/light-theme.svg + - lib/assets/images/themes/dark-theme.svg - lib/assets/animations/indicator.riv