diff --git a/assets/background/chatDark.png b/assets/background/chatDark.png new file mode 100644 index 0000000..d51c578 Binary files /dev/null and b/assets/background/chatDark.png differ diff --git a/lib/app.dart b/lib/app.dart index a9c02ab..d2e9dd9 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -44,6 +44,7 @@ class _AppState extends State { context, controller: tabController, navBarStyle: NavBarStyle.style6, + backgroundColor: Theme.of(context).colorScheme.onSecondary, decoration: const NavBarDecoration( border: Border.symmetric(vertical: BorderSide.none, horizontal: BorderSide(color: Colors.grey, width: 1)) ), @@ -57,13 +58,13 @@ class _AppState extends State { items: [ PersistentBottomNavBarItem( activeColorPrimary: Theme.of(context).primaryColor, - inactiveColorPrimary: Theme.of(context).disabledColor, + inactiveColorPrimary: Theme.of(context).colorScheme.secondary, icon: const Icon(Icons.calendar_month), title: "Vertretung" ), PersistentBottomNavBarItem( activeColorPrimary: Theme.of(context).primaryColor, - inactiveColorPrimary: Theme.of(context).disabledColor, + inactiveColorPrimary: Theme.of(context).colorScheme.secondary, icon: Consumer( builder: (context, value, child) { if(value.primaryLoading()) return const Icon(Icons.chat); @@ -86,13 +87,13 @@ class _AppState extends State { ), PersistentBottomNavBarItem( activeColorPrimary: Theme.of(context).primaryColor, - inactiveColorPrimary: Theme.of(context).disabledColor, + inactiveColorPrimary: Theme.of(context).colorScheme.secondary, icon: const Icon(Icons.folder), title: "Dateien" ), PersistentBottomNavBarItem( activeColorPrimary: Theme.of(context).primaryColor, - inactiveColorPrimary: Theme.of(context).disabledColor, + inactiveColorPrimary: Theme.of(context).colorScheme.secondary, icon: const Icon(Icons.more_horiz), title: "Mehr" ), diff --git a/lib/data/appTheme.dart b/lib/data/appTheme.dart new file mode 100644 index 0000000..559449f --- /dev/null +++ b/lib/data/appTheme.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; + +class AppTheme extends ChangeNotifier { + ThemeMode _mode = ThemeMode.system; + ThemeMode get getMode => _mode; + + void setTheme(ThemeMode newMode) { + _mode = newMode; + notifyListeners(); + } + + static ThemeModeDisplay getDisplayOptions(ThemeMode theme) { + switch(theme) { + case ThemeMode.system: + return ThemeModeDisplay(icon: Icons.auto_awesome, displayName: "Systemvorgabe"); + + case ThemeMode.light: + return ThemeModeDisplay(icon: Icons.dark_mode_outlined, displayName: "Hell"); + + case ThemeMode.dark: + return ThemeModeDisplay(icon: Icons.dark_mode, displayName: "Dunkel"); + + } + } + + static bool isDarkMode(BuildContext context) { + return Theme.of(context).brightness == Brightness.dark; + } +} + +class ThemeModeDisplay { + final IconData icon; + final String displayName; + + ThemeModeDisplay({required this.icon, required this.displayName}); +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 82df1b4..d3472f2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,8 +3,11 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:jiffy/jiffy.dart'; +import 'package:marianum_mobile/data/appTheme.dart'; import 'package:marianum_mobile/data/timetable/timetableProps.dart'; import 'package:marianum_mobile/screen/login/login.dart'; +import 'package:marianum_mobile/theming/darkAppTheme.dart'; +import 'package:marianum_mobile/theming/lightAppTheme.dart'; import 'package:marianum_mobile/widget/errorView.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -30,6 +33,7 @@ Future main() async { MultiProvider( providers: [ ChangeNotifierProvider(create: (context) => AccountModel()), + ChangeNotifierProvider(create: (context) => AppTheme()), ChangeNotifierProvider(create: (context) => TimetableProps()), ChangeNotifierProvider(create: (context) => ChatListProps()), ChangeNotifierProvider(create: (context) => ChatProps()), @@ -48,7 +52,6 @@ class Main extends StatefulWidget { } class _MainState extends State
{ - static const Color marianumRed = Color.fromARGB(255, 153, 51, 51); final Future _storage = SharedPreferences.getInstance(); @@ -70,63 +73,48 @@ class _MainState extends State
{ Widget build(BuildContext context) { SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - return MaterialApp( - debugShowCheckedModeBanner: false, - localizationsDelegates: const [ - ...GlobalMaterialLocalizations.delegates, - GlobalWidgetsLocalizations.delegate, - ], - supportedLocales: const [ - Locale('de'), - Locale('en'), - ], - locale: const Locale('de'), + return Directionality( + textDirection: TextDirection.ltr, + child: Consumer( + builder: (context, value, child) { + return MaterialApp( + debugShowCheckedModeBanner: false, + localizationsDelegates: const [ + ...GlobalMaterialLocalizations.delegates, + GlobalWidgetsLocalizations.delegate, + ], + supportedLocales: const [ + Locale('de'), + Locale('en'), + ], + locale: const Locale('de'), - title: 'Marianum Fulda', - theme: ThemeData( - brightness: Brightness.light, - primaryColor: marianumRed, - colorScheme: const ColorScheme( - brightness: Brightness.light, - surface: Colors.white, - onSurface: Colors.black, - onSecondary: Colors.white, - onPrimary: Colors.white, - onError: marianumRed, - onBackground: Colors.black, - error: marianumRed, - background: Colors.white, - secondary: marianumRed, - primary: marianumRed, - ), - hintColor: marianumRed, - inputDecorationTheme: const InputDecorationTheme( - border: UnderlineInputBorder(borderSide: BorderSide(color: marianumRed)), - ), - appBarTheme: const AppBarTheme( - backgroundColor: marianumRed, - ), - progressIndicatorTheme: const ProgressIndicatorThemeData( - color: marianumRed, - ), - ), + title: 'Marianum Fulda', - home: FutureBuilder( - future: _storage, - builder: (BuildContext context, AsyncSnapshot snapshot) { + themeMode: value.getMode, + theme: LightAppTheme.theme, + darkTheme: DarkAppTheme.theme, - if(snapshot.hasData) { - return Consumer( - builder: (context, accountModel, child) { - return accountModel.isLoggedIn ? const App() : const Login(); - }, - ); - } else { - return const Center(child: CircularProgressIndicator()); - } + + home: FutureBuilder( + future: _storage, + builder: (BuildContext context, AsyncSnapshot snapshot) { + + if(snapshot.hasData) { + return Consumer( + builder: (context, accountModel, child) { + return accountModel.isLoggedIn ? const App() : const Login(); + }, + ); + } else { + return const Center(child: CircularProgressIndicator()); + } + }, + ) + ); }, - ) + ), ); } } diff --git a/lib/screen/pages/more/debug/ThemeColors.dart b/lib/screen/pages/more/debug/ThemeColors.dart new file mode 100644 index 0000000..9dd56be --- /dev/null +++ b/lib/screen/pages/more/debug/ThemeColors.dart @@ -0,0 +1,128 @@ +import 'package:flutter/material.dart'; + +class ColorPreviewWidget extends StatelessWidget { + @override + Widget build(BuildContext context) { + final themeData = Theme.of(context); + + return ListView( + children: [ + ListTile( + leading: Icon(Icons.color_lens_outlined), + title: Text('Farbtest'), + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ColorPreviewPage(), + ), + ); + }, + ), + ], + ); + } +} + +class ColorPreviewPage extends StatelessWidget { + @override + Widget build(BuildContext context) { + final themeData = Theme.of(context); + final colorScheme = themeData.colorScheme; + + return Scaffold( + appBar: AppBar( + title: Text('Farbtest'), + ), + body: ListView( + children: [ + for (var entry in [ + 'Primary', + 'Primary Variant', + 'Secondary', + 'Secondary Variant', + 'Background', + 'Surface', + 'Error', + 'On Primary', + 'On Secondary', + 'On Background', + 'On Surface', + 'On Error', + ]) + ColorItem(name: entry, color: _getColor(colorScheme, entry)), + ], + ), + ); + } + + Color _getColor(ColorScheme colorScheme, String name) { + switch (name) { + case 'Primary': + return colorScheme.primary; + case 'Primary Variant': + return colorScheme.primaryVariant; + case 'Secondary': + return colorScheme.secondary; + case 'Secondary Variant': + return colorScheme.secondaryVariant; + case 'Background': + return colorScheme.background; + case 'Surface': + return colorScheme.surface; + case 'Error': + return colorScheme.error; + case 'On Primary': + return colorScheme.onPrimary; + case 'On Secondary': + return colorScheme.onSecondary; + case 'On Background': + return colorScheme.onBackground; + case 'On Surface': + return colorScheme.onSurface; + case 'On Error': + return colorScheme.onError; + default: + return Colors.transparent; + } + } +} + +class ColorItem extends StatelessWidget { + final String name; + final Color color; + + const ColorItem({Key? key, required this.name, required this.color}) + : super(key: key); + + @override + Widget build(BuildContext context) { + final themeData = Theme.of(context); + + return Container( + padding: EdgeInsets.all(16.0), + color: color, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + name, + style: TextStyle( + color: themeData.brightness == Brightness.light + ? Colors.black + : Colors.white, + ), + ), + Text( + '#${color.value.toRadixString(16).padLeft(8, '0').toUpperCase()}', + style: TextStyle( + color: themeData.brightness == Brightness.light + ? Colors.black + : Colors.white, + ), + ), + ], + ), + ); + } +} diff --git a/lib/screen/pages/more/overhang.dart b/lib/screen/pages/more/overhang.dart index cf6f846..9652d9c 100644 --- a/lib/screen/pages/more/overhang.dart +++ b/lib/screen/pages/more/overhang.dart @@ -5,6 +5,7 @@ import 'package:marianum_mobile/screen/settings/settings.dart'; import 'package:persistent_bottom_nav_bar/persistent_tab_view.dart'; import '../../../widget/ListItem.dart'; +import 'debug/ThemeColors.dart'; import 'message/message.dart'; class Overhang extends StatelessWidget { @@ -20,11 +21,12 @@ class Overhang extends StatelessWidget { ], ), body: ListView( - children: const [ + children: [ ListItemNavigator(icon: Icons.newspaper, text: "Marianum Message", target: Message()), ListItemNavigator(icon: Icons.room, text: "Raumplan", target: Roomplan()), ListItemNavigator(icon: Icons.calendar_month, text: "Schulferien", target: Roomplan()), - ListItemNavigator(icon: Icons.calculate, text: "Notendurschnitts rechner", target: Roomplan()) + ListItemNavigator(icon: Icons.calculate, text: "Notendurschnitts rechner", target: Roomplan()), + ListItemNavigator(icon: Icons.color_lens_outlined, text: "Farbtest", target: ColorPreviewPage()) ], ), ); diff --git a/lib/screen/pages/talk/chatBubble.dart b/lib/screen/pages/talk/chatBubble.dart index 231e45d..ba94475 100644 --- a/lib/screen/pages/talk/chatBubble.dart +++ b/lib/screen/pages/talk/chatBubble.dart @@ -5,6 +5,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:jiffy/jiffy.dart'; +import 'package:marianum_mobile/data/appTheme.dart'; import '../../../api/marianumcloud/talk/chat/getChatResponse.dart'; import '../../../api/marianumcloud/talk/room/getRoomResponse.dart'; @@ -30,18 +31,26 @@ class ChatBubble extends StatefulWidget { } class _ChatBubbleState extends State { - static const styleSystem = BubbleStyle( - color: Color(0xffd4eaf4), - borderWidth: 1, - elevation: 2, - margin: BubbleEdges.only(bottom: 20, top: 10), - alignment: Alignment.center, - ); + // late BubbleStyle styleSystem; + // late BubbleStyle Function(bool) styleRemote; + // late BubbleStyle Function(bool) styleSelf; - static BubbleStyle getStyleOther(bool seamless) { + + BubbleStyle getSystemStyle() { + return BubbleStyle( + color: AppTheme.isDarkMode(context) ? Color(0xff182229) : Colors.white, + borderWidth: 1, + elevation: 2, + margin: const BubbleEdges.only(bottom: 20, top: 10), + alignment: Alignment.center, + ); + } + + BubbleStyle getRemoteStyle(bool seamless) { + var color = AppTheme.isDarkMode(context) ? Color(0xff202c33) : Colors.white; return BubbleStyle( nip: BubbleNip.leftTop, - color: seamless ? Colors.transparent : Colors.white, + color: seamless ? Colors.transparent : color, borderWidth: seamless ? 0 : 1, elevation: seamless ? 0 : 1, margin: const BubbleEdges.only(bottom: 10, left: 10, right: 50), @@ -49,10 +58,10 @@ class _ChatBubbleState extends State { ); } - static BubbleStyle getStyleSelf(bool seamless) { + BubbleStyle getSelfStyle(bool seamless) { return BubbleStyle( nip: BubbleNip.rightBottom, - color: seamless ? Colors.transparent : const Color(0xffd9fdd3), + color: seamless ? Colors.transparent : Color(0xff005c4b), borderWidth: seamless ? 0 : 1, elevation: seamless ? 0 : 1, margin: const BubbleEdges.only(bottom: 10, right: 10, left: 50), @@ -76,12 +85,12 @@ class _ChatBubbleState extends State { BubbleStyle getStyle() { if(widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment) { if(widget.isSender) { - return getStyleSelf(message.containsFile); + return getSelfStyle(message.containsFile); } else { - return getStyleOther(message.containsFile); + return getRemoteStyle(message.containsFile); } } else { - return styleSystem; + return getSystemStyle(); } } diff --git a/lib/screen/pages/talk/chatTextfield.dart b/lib/screen/pages/talk/chatTextfield.dart index 80820d1..29f7d36 100644 --- a/lib/screen/pages/talk/chatTextfield.dart +++ b/lib/screen/pages/talk/chatTextfield.dart @@ -56,7 +56,7 @@ class _ChatTextfieldState extends State { padding: const EdgeInsets.only(left: 10, bottom: 10, top: 10), height: 60, width: double.infinity, - color: Colors.white, + color: Theme.of(context).colorScheme.secondary, child: Row( children: [ GestureDetector( @@ -108,9 +108,9 @@ class _ChatTextfieldState extends State { controller: _textBoxController, readOnly: sending, maxLines: 10, - decoration: const InputDecoration( + decoration: InputDecoration( hintText: "Nachricht schreiben...", - hintStyle: TextStyle(color: Colors.black54), + hintStyle: TextStyle(color: Theme.of(context).colorScheme.onSecondary), border: InputBorder.none ), ), diff --git a/lib/screen/pages/talk/chatView.dart b/lib/screen/pages/talk/chatView.dart index 89066b7..7886ad7 100644 --- a/lib/screen/pages/talk/chatView.dart +++ b/lib/screen/pages/talk/chatView.dart @@ -4,6 +4,7 @@ import 'package:jiffy/jiffy.dart'; import 'package:loader_overlay/loader_overlay.dart'; import 'package:marianum_mobile/api/marianumcloud/talk/chat/getChatResponse.dart'; import 'package:marianum_mobile/api/marianumcloud/talk/room/getRoomResponse.dart'; +import 'package:marianum_mobile/data/appTheme.dart'; import 'package:marianum_mobile/data/chatList/chatProps.dart'; import 'package:marianum_mobile/screen/pages/talk/chatBubble.dart'; import 'package:marianum_mobile/screen/pages/talk/chatTextfield.dart'; @@ -84,13 +85,13 @@ class _ChatViewState extends State { ), ), body: Container( - decoration: const BoxDecoration( + decoration: BoxDecoration( image: DecorationImage( - image: AssetImage("assets/background/chat.png"), + image: AppTheme.isDarkMode(context) ? const AssetImage("assets/background/chatDark.png") : const AssetImage("assets/background/chat.png"), scale: 1.5, opacity: 0.5, repeat: ImageRepeat.repeat, - colorFilter: ColorFilter.linearToSrgbGamma() + colorFilter: const ColorFilter.linearToSrgbGamma() ) ), child: LoaderOverlay( diff --git a/lib/screen/settings/settings.dart b/lib/screen/settings/settings.dart index e2e0300..cd64dec 100644 --- a/lib/screen/settings/settings.dart +++ b/lib/screen/settings/settings.dart @@ -6,6 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../data/accountModel.dart'; +import '../../data/appTheme.dart'; import 'debug/debugOverview.dart'; class Settings extends StatefulWidget { @@ -72,6 +73,35 @@ class _SettingsState extends State { const Divider(), + Consumer( + builder: (context, value, child) { + return ListTile( + leading: const Icon(Icons.dark_mode), + title: const Text("Farbgebung"), + trailing: DropdownButton( + value: value.getMode, + icon: const Icon(Icons.arrow_drop_down), + items: ThemeMode.values.map((e) => DropdownMenuItem( + value: e, + enabled: e != value.getMode, + child: Row( + children: [ + Icon(AppTheme.getDisplayOptions(e).icon), + const SizedBox(width: 10), + Text(AppTheme.getDisplayOptions(e).displayName), + ], + ), + )).toList(), + onChanged: (e) { + Provider.of(context, listen: false).setTheme(e ?? ThemeMode.system); + }, + ), + ); + }, + ), + + const Divider(), + ListTile( leading: const Icon(Icons.info), title: const Text("Informationen und Lizenzen"), diff --git a/lib/theming/darkAppTheme.dart b/lib/theming/darkAppTheme.dart new file mode 100644 index 0000000..0191f20 --- /dev/null +++ b/lib/theming/darkAppTheme.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +class DarkAppTheme { + static const Color marianumRed = Color.fromARGB(255, 153, 51, 51); + + static final theme = ThemeData( + brightness: Brightness.dark, + primaryColor: marianumRed, + hintColor: marianumRed, + colorScheme: const ColorScheme( + brightness: Brightness.dark, + + surface: Colors.black, + onSurface: Colors.white, + + primary: Colors.black, + onPrimary: Colors.white, + + secondary: Colors.grey, + onSecondary: Colors.white, + + background: Colors.black26, + onBackground: Colors.white, + + error: marianumRed, + onError: marianumRed, + ), + inputDecorationTheme: const InputDecorationTheme( + border: UnderlineInputBorder(borderSide: BorderSide(color: marianumRed)), + ), + appBarTheme: const AppBarTheme( + backgroundColor: marianumRed, + ), + progressIndicatorTheme: const ProgressIndicatorThemeData( + color: marianumRed, + ), + + ); +} \ No newline at end of file diff --git a/lib/theming/lightAppTheme.dart b/lib/theming/lightAppTheme.dart new file mode 100644 index 0000000..18831b6 --- /dev/null +++ b/lib/theming/lightAppTheme.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; + +class LightAppTheme { + static const Color marianumRed = Color.fromARGB(255, 153, 51, 51); + + static final theme = ThemeData( + brightness: Brightness.light, + primaryColor: marianumRed, + + colorScheme: const ColorScheme( + brightness: Brightness.light, + + surface: Colors.white, + onSurface: Colors.black, + + secondary: Colors.grey, + onSecondary: Colors.white, + + primary: marianumRed, + onPrimary: Colors.white, + + background: Colors.white, + onBackground: Colors.black, + + error: marianumRed, + onError: marianumRed, + ), + inputDecorationTheme: const InputDecorationTheme( + border: UnderlineInputBorder(borderSide: BorderSide(color: marianumRed)), + ), + appBarTheme: const AppBarTheme( + backgroundColor: marianumRed, + ), + progressIndicatorTheme: const ProgressIndicatorThemeData( + color: marianumRed, + ), + + ); +} \ No newline at end of file