diff --git a/lib/common/utils/colors_utils.dart b/lib/common/utils/colors_utils.dart new file mode 100644 index 0000000..ed5ef7b --- /dev/null +++ b/lib/common/utils/colors_utils.dart @@ -0,0 +1,62 @@ +import 'package:flutter/material.dart'; + +MaterialColor getColorFromString(String color) { + switch (color) { + case 'blue': + return Colors.blue; + case 'red': + return Colors.red; + case 'green': + return Colors.green; + case 'yellow': + return Colors.yellow; + case 'orange': + return Colors.orange; + case 'pink': + return Colors.pink; + case 'purple': + return Colors.purple; + case 'indigo': + return Colors.indigo; + case 'teal': + return Colors.teal; + case 'cyan': + return Colors.cyan; + case 'brown': + return Colors.brown; + case 'grey': + return Colors.grey; + default: + return Colors.blue; + } +} + +String getStringFromColor(MaterialColor color) { + if (color == Colors.blue) { + return 'blue'; + } else if (color == Colors.red) { + return 'red'; + } else if (color == Colors.green) { + return 'green'; + } else if (color == Colors.yellow) { + return 'yellow'; + } else if (color == Colors.orange) { + return 'orange'; + } else if (color == Colors.pink) { + return 'pink'; + } else if (color == Colors.purple) { + return 'purple'; + } else if (color == Colors.indigo) { + return 'indigo'; + } else if (color == Colors.teal) { + return 'teal'; + } else if (color == Colors.cyan) { + return 'cyan'; + } else if (color == Colors.brown) { + return 'brown'; + } else if (color == Colors.grey) { + return 'grey'; + } else { + return 'blue'; + } +} diff --git a/lib/feature/settings/data/settings_local_data_provider.dart b/lib/feature/settings/data/settings_local_data_provider.dart index a246cd3..5deed2d 100644 --- a/lib/feature/settings/data/settings_local_data_provider.dart +++ b/lib/feature/settings/data/settings_local_data_provider.dart @@ -6,6 +6,8 @@ abstract class ISettingsLocalDataProvider { Future getGroup(); Future saveLanguage(String language); Future getLanguage(); + Future saveTheme(String theme); + Future getTheme(); } class SettingsLocalDataProvider implements ISettingsLocalDataProvider { @@ -16,6 +18,7 @@ class SettingsLocalDataProvider implements ISettingsLocalDataProvider { static const String _groupCourseKey = 'groupCourse'; static const String _groupFacultyIdKey = 'groupFacultyId'; static const String _languageKey = 'language'; + static const String _themeKey = 'theme'; SettingsLocalDataProvider({required SharedPreferences prefs}) : _prefs = prefs; @@ -59,4 +62,14 @@ class SettingsLocalDataProvider implements ISettingsLocalDataProvider { Future saveLanguage(String language) { return _prefs.setString(_languageKey, language); } + + @override + Future getTheme() async { + return _prefs.getString(_themeKey); + } + + @override + Future saveTheme(String theme) { + return _prefs.setString(_themeKey, theme); + } } diff --git a/lib/feature/settings/data/settings_repository.dart b/lib/feature/settings/data/settings_repository.dart index eeedc6b..92d1ff5 100644 --- a/lib/feature/settings/data/settings_repository.dart +++ b/lib/feature/settings/data/settings_repository.dart @@ -9,6 +9,9 @@ abstract class ISettingsRepository { Future saveLanguage(String language); Future getLanguage(); Stream getLanguageChangedStream(); + Future saveTheme(String theme); + Future getTheme(); + Stream getThemeChangedStream(); } class SettingsRepository implements ISettingsRepository { @@ -20,6 +23,9 @@ class SettingsRepository implements ISettingsRepository { final StreamController _languageChangedController = StreamController.broadcast(); + final StreamController _themeChangedController = + StreamController.broadcast(); + @override Future getGroup() { return _localDataProvider.getGroup(); @@ -44,4 +50,20 @@ class SettingsRepository implements ISettingsRepository { @override Stream getLanguageChangedStream() => _languageChangedController.stream; + + @override + Future getTheme() { + return _localDataProvider.getTheme(); + } + + @override + Stream getThemeChangedStream() { + return _themeChangedController.stream; + } + + @override + Future saveTheme(String theme) async { + await _localDataProvider.saveTheme(theme); + _themeChangedController.add(theme); + } } diff --git a/lib/feature/settings/widget/settings_page.dart b/lib/feature/settings/widget/settings_page.dart index 65ed872..03b7ce9 100644 --- a/lib/feature/settings/widget/settings_page.dart +++ b/lib/feature/settings/widget/settings_page.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:uneconly/common/dependencies/dependencies_scope.dart'; import 'package:uneconly/common/localization/localization.dart'; +import 'package:uneconly/common/utils/colors_utils.dart'; /// Flutter code sample for [CupertinoPicker]. @@ -23,6 +24,22 @@ class SettingsPage extends StatefulWidget { class _SettingsPageState extends State { int _selectedLanguage = 0; + int _selectedColor = 0; + + List colors = [ + Colors.blue, + Colors.red, + Colors.green, + Colors.yellow, + Colors.orange, + Colors.pink, + Colors.purple, + Colors.indigo, + Colors.teal, + Colors.cyan, + Colors.brown, + Colors.grey, + ]; @override void initState() { @@ -47,6 +64,27 @@ class _SettingsPageState extends State { }); } }); + + RepositoryProvider.of(context) + .settingsRepository + .getTheme() + .then((value) { + if (value == null) { + setState(() { + _selectedColor = 0; + }); + } else { + final materialColor = getColorFromString(value); + + final selectedColor = colors.indexWhere( + (element) => element == materialColor, + ); + + setState(() { + _selectedColor = selectedColor; + }); + } + }); } // This shows a CupertinoModalPopup with a reasonable fixed height which hosts CupertinoPicker. @@ -85,53 +123,113 @@ class _SettingsPageState extends State { fontSize: 22.0, ), child: Center( - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('${AppLocalizations.of(context)!.language}: '), - CupertinoButton( - padding: EdgeInsets.zero, - // Display a CupertinoPicker with list of fruits. - onPressed: () => _showDialog( - CupertinoPicker( - magnification: 1.22, - squeeze: 1.2, - useMagnifier: true, - itemExtent: _kItemExtent, - // This sets the initial item. - scrollController: FixedExtentScrollController( - initialItem: _selectedLanguage, - ), - // This is called when selected item is changed. - onSelectedItemChanged: (int selectedItem) async { - setState(() { - _selectedLanguage = selectedItem; - }); + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text('${AppLocalizations.of(context)!.language}: '), + CupertinoButton( + padding: EdgeInsets.zero, + // Display a CupertinoPicker with list of fruits. + onPressed: () => _showDialog( + CupertinoPicker( + magnification: 1.22, + squeeze: 1.2, + useMagnifier: true, + itemExtent: _kItemExtent, + // This sets the initial item. + scrollController: FixedExtentScrollController( + initialItem: _selectedLanguage, + ), + // This is called when selected item is changed. + onSelectedItemChanged: (int selectedItem) async { + setState(() { + _selectedLanguage = selectedItem; + }); - if (_selectedLanguage == 0) { - await RepositoryProvider.of(context) - .settingsRepository - .saveLanguage('ru'); - } else { - await RepositoryProvider.of(context) - .settingsRepository - .saveLanguage('en'); - } - }, - children: List.generate( - _languageNames.length, - (int index) { - return Center(child: Text(_languageNames[index])); - }, + if (_selectedLanguage == 0) { + await RepositoryProvider.of( + context, + ).settingsRepository.saveLanguage('ru'); + } else { + await RepositoryProvider.of( + context, + ).settingsRepository.saveLanguage('en'); + } + }, + children: List.generate( + _languageNames.length, + (int index) { + return Center(child: Text(_languageNames[index])); + }, + ), + ), + ), + // This displays the selected fruit name. + child: Text( + _languageNames[_selectedLanguage], + style: const TextStyle( + fontSize: 22.0, + ), ), ), + ], + ), + // horizontal list of themes + Container( + height: 40, + padding: const EdgeInsets.symmetric( + horizontal: 10, ), - // This displays the selected fruit name. - child: Text( - _languageNames[_selectedLanguage], - style: const TextStyle( - fontSize: 22.0, - ), + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: colors.length, + itemBuilder: (context, index) { + return GestureDetector( + onTap: () async { + await context + .read() + .settingsRepository + .saveTheme( + getStringFromColor(colors[index]), + ); + setState(() { + _selectedColor = index; + }); + }, + child: Container( + width: 40, + height: 40, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10), + color: Colors.white, + border: _selectedColor == index + ? Border.all( + color: Colors.blue, + width: 2, + ) + : null, + ), + child: Center( + child: Container( + width: 32, + height: 32, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(10 / 1.25), + color: colors[index], + ), + ), + ), + ), + ); + }, + separatorBuilder: (context, index) { + return const SizedBox( + width: 10, + ); + }, ), ), ], diff --git a/lib/main.dart b/lib/main.dart index cd747d7..158d0e6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,6 +10,7 @@ import 'package:uneconly/common/database/database.dart'; import 'package:uneconly/common/dependencies/dependencies_scope.dart'; import 'package:uneconly/common/routing/app_route_information_parser.dart'; import 'package:uneconly/common/routing/app_router_delegate.dart'; +import 'package:uneconly/common/utils/colors_utils.dart'; import 'package:uneconly/constants.dart'; import 'package:uneconly/feature/settings/data/settings_local_data_provider.dart'; import 'package:uneconly/feature/settings/data/settings_repository.dart'; @@ -46,6 +47,7 @@ class _MyAppState extends State { late AppRouteInformationParser _routeInformationParser; late String locale; + late String theme; @override void initState() { @@ -57,6 +59,9 @@ class _MyAppState extends State { final defaultLocale = Platform.localeName; locale = defaultLocale.split('_')[0]; + const defaultTheme = 'blue'; + theme = defaultTheme; + widget.settingsRepository.getLanguage().then( (value) { if (value != null) { @@ -72,6 +77,22 @@ class _MyAppState extends State { locale = newLanguage; }); }); + + widget.settingsRepository.getTheme().then( + (value) { + if (value != null) { + setState(() { + theme = value; + }); + } + }, + ); + + widget.settingsRepository.getThemeChangedStream().listen((newTheme) { + setState(() { + theme = newTheme; + }); + }); } @override @@ -92,7 +113,9 @@ class _MyAppState extends State { localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, theme: ThemeData( - primarySwatch: Colors.blue, + primarySwatch: getColorFromString( + theme, + ), ), scrollBehavior: AppScrollBehavior(), routerDelegate: _routerDelegate,