From 26ca8ba4add87cd4964c26f01aad7bd7818e418a Mon Sep 17 00:00:00 2001 From: Marcel Date: Sun, 16 Feb 2020 14:57:50 +0000 Subject: [PATCH] [Theme] Add initial dark theme Took 1 hour 10 minutes --- lib/components/ThemeSwitcher.dart | 285 ++++++++++++++++++ lib/components/list_items/chat_list_item.dart | 19 +- lib/components/list_items/message.dart | 6 +- lib/components/matrix.dart | 18 +- lib/i18n/i18n.dart | 10 + lib/i18n/intl_de.arb | 25 ++ lib/i18n/intl_messages.arb | 25 ++ lib/i18n/messages_de.dart | 5 + lib/i18n/messages_messages.dart | 5 + lib/main.dart | 96 +++--- lib/views/chat_details.dart | 2 +- lib/views/chat_list.dart | 33 +- lib/views/login.dart | 5 +- lib/views/settings.dart | 32 +- lib/views/settings_themes.dart | 105 +++++++ lib/views/sign_up.dart | 17 +- 16 files changed, 577 insertions(+), 111 deletions(-) create mode 100644 lib/components/ThemeSwitcher.dart create mode 100644 lib/views/settings_themes.dart diff --git a/lib/components/ThemeSwitcher.dart b/lib/components/ThemeSwitcher.dart new file mode 100644 index 0000000..3b0ed15 --- /dev/null +++ b/lib/components/ThemeSwitcher.dart @@ -0,0 +1,285 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import '../utils/famedlysdk_store.dart'; +import 'matrix.dart'; + +enum Themes { + light, + dark, + system, +} + +final ThemeData lightTheme = ThemeData( + primaryColorDark: Colors.white, + primaryColorLight: Color(0xff121212), + brightness: Brightness.light, + primaryColor: Color(0xFF5625BA), + backgroundColor: Colors.white, + secondaryHeaderColor: Color(0xFFECECF2), + scaffoldBackgroundColor: Colors.white, + dialogTheme: DialogTheme( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + popupMenuTheme: PopupMenuThemeData( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + ), + appBarTheme: AppBarTheme( + brightness: Brightness.light, + color: Colors.white, + textTheme: TextTheme( + title: TextStyle( + color: Colors.black, + fontSize: 20, + ), + ), + iconTheme: IconThemeData(color: Colors.black), + ), +); + +final ThemeData darkTheme = ThemeData.dark().copyWith( + primaryColorDark: Color(0xff121212), + primaryColorLight: Colors.white, + primaryColor: Color(0xFF5625BA), + backgroundColor: Color(0xff121212), + scaffoldBackgroundColor: Color(0xff121212), + accentColor: Color(0xFFF5B4D2), + secondaryHeaderColor: Color(0xff1D1D1D), + appBarTheme: AppBarTheme( + brightness: Brightness.dark, + color: Color(0xff1D1D1D), + textTheme: TextTheme( + title: TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + iconTheme: IconThemeData(color: Colors.white), + ), +); + +final ThemeData amoledTheme = ThemeData.dark().copyWith( + primaryColorDark: Color(0xff121212), + primaryColorLight: Colors.white, + primaryColor: Color(0xFF5625BA), + backgroundColor: Colors.black, + scaffoldBackgroundColor: Colors.black, + accentColor: Color(0xFFF5B4D2), + secondaryHeaderColor: Color(0xff1D1D1D), + appBarTheme: AppBarTheme( + brightness: Brightness.dark, + color: Color(0xff1D1D1D), + textTheme: TextTheme( + title: TextStyle( + color: Colors.white, + fontSize: 20, + ), + ), + iconTheme: IconThemeData(color: Colors.white), + ), +); + +Color chatListItemColor(BuildContext context, bool activeChat) => + Theme.of(context).brightness == Brightness.light + ? activeChat ? Color(0xFFE8E8E8) : Colors.white + : activeChat + ? ThemeSwitcherWidget.of(context).amoledEnabled + ? Color(0xff121212) + : Colors.black + : ThemeSwitcherWidget.of(context).amoledEnabled + ? Colors.black + : Color(0xff121212); + +Color blackWhiteColor(BuildContext context) => + Theme.of(context).brightness == Brightness.light + ? Colors.white + : Colors.black; + +class ThemeSwitcher extends InheritedWidget { + final ThemeSwitcherWidgetState data; + + const ThemeSwitcher({ + Key key, + @required this.data, + @required Widget child, + }) : assert(child != null), + super(key: key, child: child); + + @override + bool updateShouldNotify(ThemeSwitcher old) { + return this != old; + } +} + +class ThemeSwitcherWidget extends StatefulWidget { + final Widget child; + + ThemeSwitcherWidget({Key key, this.child}) + : assert(child != null), + super(key: key); + + @override + ThemeSwitcherWidgetState createState() => ThemeSwitcherWidgetState(); + + /// Returns the (nearest) Client instance of your application. + static ThemeSwitcherWidgetState of(BuildContext context) { + ThemeSwitcherWidgetState newState = + (context.dependOnInheritedWidgetOfExactType()).data; + newState.context = context; + return newState; + } +} + +class ThemeSwitcherWidgetState extends State { + ThemeData themeData; + Themes selectedTheme; + bool amoledEnabled; + BuildContext context; + + Future loadSelection(MatrixState matrix) async { + if (kIsWeb) { + Store store = matrix.client.storeAPI; + String item = await store.getItem("theme") ?? "light"; + selectedTheme = + Themes.values.firstWhere((e) => e.toString() == 'Themes.' + item); + + amoledEnabled = + (await store.getItem("amoled_enabled") ?? "false").toLowerCase() == + 'true'; + } else { + ExtendedStore store = matrix.client.storeAPI; + String item = await store.getItem("theme") ?? "light"; + selectedTheme = + Themes.values.firstWhere((e) => e.toString() == 'Themes.' + item); + + amoledEnabled = + (await store.getItem("amoled_enabled") ?? "false").toLowerCase() == + 'true'; + } + switchTheme(matrix, selectedTheme, amoledEnabled); + return; + } + + void switchTheme( + MatrixState matrix, Themes newTheme, bool amoled_enabled) async { + ThemeData theme; + switch (newTheme) { + case Themes.light: + theme = lightTheme; + break; + case Themes.dark: + if (amoled_enabled) { + theme = amoledTheme; + } else { + theme = darkTheme; + } + break; + case Themes.system: + // This needs to be a low level call as we don't have a MaterialApp yet + Brightness brightness = + MediaQueryData.fromWindow(WidgetsBinding.instance.window) + .platformBrightness; + if (brightness == Brightness.dark) { + if (amoled_enabled) { + theme = amoledTheme; + } else { + theme = darkTheme; + } + } else { + theme = lightTheme; + } + break; + } + + await saveThemeValue(matrix, newTheme); + await saveAmoledEnabledValue(matrix, amoled_enabled); + setState(() { + amoledEnabled = amoled_enabled; + selectedTheme = newTheme; + themeData = theme; + }); + } + + Future saveThemeValue(MatrixState matrix, Themes value) async { + if (kIsWeb) { + Store store = matrix.client.storeAPI; + await store.setItem("theme", value.toString().split('.').last); + } else { + ExtendedStore store = matrix.client.storeAPI; + await store.setItem("theme", value.toString().split('.').last); + } + } + + Future saveAmoledEnabledValue(MatrixState matrix, bool value) async { + if (kIsWeb) { + Store store = matrix.client.storeAPI; + await store.setItem("amoled_enabled", value.toString()); + } else { + ExtendedStore store = matrix.client.storeAPI; + await store.setItem("amoled_enabled", value.toString()); + } + } + + void setup() async { + final MatrixState matrix = Matrix.of(context); + await loadSelection(matrix); + + if (selectedTheme == null) { + switchTheme(matrix, Themes.light, false); + } else { + switch (selectedTheme) { + case Themes.light: + switchTheme(matrix, Themes.light, false); + break; + case Themes.dark: + if (amoledEnabled) { + switchTheme(matrix, Themes.dark, true); + } else { + switchTheme(matrix, Themes.dark, false); + } + break; + case Themes.system: + switchTheme(matrix, Themes.system, false); + break; + } + } + } + + @override + void initState() { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (amoledEnabled == null || selectedTheme == null) { + setup(); + } + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + if (themeData == null) { + // This needs to be a low level call as we don't have a MaterialApp yet + Brightness brightness = + MediaQueryData.fromWindow(WidgetsBinding.instance.window) + .platformBrightness; + if (brightness == Brightness.dark) { + themeData = darkTheme; + } else { + themeData = lightTheme; + } + return ThemeSwitcher( + data: this, + child: widget.child, + ); + } else { + return ThemeSwitcher( + data: this, + child: widget.child, + ); + } + } +} diff --git a/lib/components/list_items/chat_list_item.dart b/lib/components/list_items/chat_list_item.dart index 15443db..09bb54f 100644 --- a/lib/components/list_items/chat_list_item.dart +++ b/lib/components/list_items/chat_list_item.dart @@ -1,18 +1,19 @@ import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/components/dialogs/simple_dialogs.dart'; -import 'package:fluffychat/i18n/i18n.dart'; -import 'package:fluffychat/utils/event_extension.dart'; -import 'package:fluffychat/utils/date_time_extension.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/utils/room_extension.dart'; import 'package:fluffychat/views/chat.dart'; -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; -import 'package:toast/toast.dart'; import 'package:pedantic/pedantic.dart'; +import 'package:toast/toast.dart'; +import '../../i18n/i18n.dart'; +import '../../utils/app_route.dart'; +import '../../utils/date_time_extension.dart'; +import '../../utils/event_extension.dart'; +import '../../utils/room_extension.dart'; +import '../../views/chat.dart'; +import '../ThemeSwitcher.dart'; import '../avatar.dart'; +import '../dialogs/simple_dialogs.dart'; import '../matrix.dart'; class ChatListItem extends StatelessWidget { @@ -133,7 +134,7 @@ class ChatListItem extends StatelessWidget { onWillDismiss: (actionType) => archiveAction(context), ), child: Material( - color: activeChat ? Color(0xFFE8E8E8) : Colors.white, + color: chatListItemColor(context, activeChat), child: ListTile( leading: Avatar(room.avatar, room.displayname), title: Row( diff --git a/lib/components/list_items/message.dart b/lib/components/list_items/message.dart index 758cb67..f73f213 100644 --- a/lib/components/list_items/message.dart +++ b/lib/components/list_items/message.dart @@ -44,7 +44,11 @@ class Message extends StatelessWidget { BubbleNip nip = sameSender ? BubbleNip.no : ownMessage ? BubbleNip.rightBottom : BubbleNip.leftBottom; - final Color textColor = ownMessage ? Colors.white : Colors.black; + final Color textColor = ownMessage + ? Colors.white + : Theme.of(context).brightness == Brightness.dark + ? Colors.white + : Colors.black; MainAxisAlignment rowMainAxisAlignment = ownMessage ? MainAxisAlignment.end : MainAxisAlignment.start; diff --git a/lib/components/matrix.dart b/lib/components/matrix.dart index ad3ca9a..b084ce2 100644 --- a/lib/components/matrix.dart +++ b/lib/components/matrix.dart @@ -1,14 +1,9 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:firebase_messaging/firebase_messaging.dart'; + import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/i18n/i18n.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/utils/event_extension.dart'; -import 'package:fluffychat/utils/famedlysdk_store.dart'; -import 'package:fluffychat/utils/room_extension.dart'; -import 'package:fluffychat/views/chat.dart'; +import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; @@ -16,6 +11,13 @@ import 'package:localstorage/localstorage.dart'; import 'package:path_provider/path_provider.dart'; import 'package:toast/toast.dart'; +import '../i18n/i18n.dart'; +import '../utils/app_route.dart'; +import '../utils/event_extension.dart'; +import '../utils/famedlysdk_store.dart'; +import '../utils/room_extension.dart'; +import '../views/chat.dart'; + class Matrix extends StatefulWidget { final Widget child; @@ -295,7 +297,7 @@ class MatrixState extends State { client.storeAPI = kIsWeb ? Store(client) : ExtendedStore(client); debugPrint( "[Store] Store is extended: ${client.storeAPI.extended.toString()}"); - if (await initLoginState == LoginState.logged) { + if (await initLoginState == LoginState.logged && !kIsWeb) { await setupFirebase(); } } diff --git a/lib/i18n/i18n.dart b/lib/i18n/i18n.dart index 60fb7c6..29c4548 100644 --- a/lib/i18n/i18n.dart +++ b/lib/i18n/i18n.dart @@ -569,6 +569,16 @@ class I18n { String get signUp => Intl.message("Sign up"); + String get changeTheme => Intl.message("Change your style"); + + String get systemTheme => Intl.message("System"); + + String get lightTheme => Intl.message("Light"); + + String get darkTheme => Intl.message("Dark"); + + String get useAmoledTheme => Intl.message("Use Amoled compatible colors?"); + String get sourceCode => Intl.message("Source code"); String get startYourFirstChat => Intl.message("Start your first chat :-)"); diff --git a/lib/i18n/intl_de.arb b/lib/i18n/intl_de.arb index 512d4fc..fe3423b 100644 --- a/lib/i18n/intl_de.arb +++ b/lib/i18n/intl_de.arb @@ -892,6 +892,31 @@ "type": "text", "placeholders": {} }, + "Change your style": "Change your style", + "@Change your style": { + "type": "text", + "placeholders": {} + }, + "System": "System", + "@System": { + "type": "text", + "placeholders": {} + }, + "Light": "Hell", + "@Light": { + "type": "text", + "placeholders": {} + }, + "Dark": "Dunkel", + "@Dark": { + "type": "text", + "placeholders": {} + }, + "Use Amoled compatible colors?": "Amoled optimierte Farben verwenden?", + "@Use Amoled compatible colors?": { + "type": "text", + "placeholders": {} + }, "Source code": "Quellcode", "@Source code": { "type": "text", diff --git a/lib/i18n/intl_messages.arb b/lib/i18n/intl_messages.arb index 7f10c36..6039028 100644 --- a/lib/i18n/intl_messages.arb +++ b/lib/i18n/intl_messages.arb @@ -892,6 +892,31 @@ "type": "text", "placeholders": {} }, + "Change your style": "Change your style", + "@Change your style": { + "type": "text", + "placeholders": {} + }, + "System": "System", + "@System": { + "type": "text", + "placeholders": {} + }, + "Light": "Light", + "@Light": { + "type": "text", + "placeholders": {} + }, + "Dark": "Dark", + "@Dark": { + "type": "text", + "placeholders": {} + }, + "Use Amoled compatible colors?": "Use Amoled compatible colors?", + "@Use Amoled compatible colors?": { + "type": "text", + "placeholders": {} + }, "Source code": "Source code", "@Source code": { "type": "text", diff --git a/lib/i18n/messages_de.dart b/lib/i18n/messages_de.dart index 3b67ba6..c6e64d4 100644 --- a/lib/i18n/messages_de.dart +++ b/lib/i18n/messages_de.dart @@ -150,6 +150,7 @@ class MessageLookup extends MessageLookupByLibrary { "Ban from chat" : MessageLookupByLibrary.simpleMessage("Aus dem Chat verbannen"), "Banned" : MessageLookupByLibrary.simpleMessage("Banned"), "Change the name of the group" : MessageLookupByLibrary.simpleMessage("Gruppenname ändern"), + "Change your style" : MessageLookupByLibrary.simpleMessage("Change your style"), "Changelog" : MessageLookupByLibrary.simpleMessage("Changelog"), "Chat details" : MessageLookupByLibrary.simpleMessage("Gruppeninfo"), "Choose a username" : MessageLookupByLibrary.simpleMessage("Wähle einen Benutzernamen"), @@ -166,6 +167,7 @@ class MessageLookup extends MessageLookupByLibrary { "Create" : MessageLookupByLibrary.simpleMessage("Create"), "Create account now" : MessageLookupByLibrary.simpleMessage("Account jetzt erstellen"), "Create new group" : MessageLookupByLibrary.simpleMessage("Neue Gruppe"), + "Dark" : MessageLookupByLibrary.simpleMessage("Dunkel"), "Delete" : MessageLookupByLibrary.simpleMessage("Löschen"), "Delete message" : MessageLookupByLibrary.simpleMessage("Nachricht löschen"), "Discard picture" : MessageLookupByLibrary.simpleMessage("Bild verwerfen"), @@ -199,6 +201,7 @@ class MessageLookup extends MessageLookupByLibrary { "Leave" : MessageLookupByLibrary.simpleMessage("Verlassen"), "Left the chat" : MessageLookupByLibrary.simpleMessage("Hat den Chat verlassen"), "License" : MessageLookupByLibrary.simpleMessage("Lizenz"), + "Light" : MessageLookupByLibrary.simpleMessage("Hell"), "Loading... Please wait" : MessageLookupByLibrary.simpleMessage("Lade ... Bitte warten"), "Login" : MessageLookupByLibrary.simpleMessage("Login"), "Logout" : MessageLookupByLibrary.simpleMessage("Logout"), @@ -244,12 +247,14 @@ class MessageLookup extends MessageLookupByLibrary { "Source code" : MessageLookupByLibrary.simpleMessage("Quellcode"), "Start your first chat :-)" : MessageLookupByLibrary.simpleMessage("Starte deinen ersten Chat :-)"), "Sunday" : MessageLookupByLibrary.simpleMessage("Sonntag"), + "System" : MessageLookupByLibrary.simpleMessage("System"), "Tap to show menu" : MessageLookupByLibrary.simpleMessage("Tippen, um das Menü anzuzeigen"), "This room has been archived." : MessageLookupByLibrary.simpleMessage("Dieser Raum wurde archiviert."), "Thursday" : MessageLookupByLibrary.simpleMessage("Donnerstag"), "Try to send again" : MessageLookupByLibrary.simpleMessage("Nochmal versuchen zu senden"), "Tuesday" : MessageLookupByLibrary.simpleMessage("Tuesday"), "Unmute chat" : MessageLookupByLibrary.simpleMessage("Stumm aus"), + "Use Amoled compatible colors?" : MessageLookupByLibrary.simpleMessage("Amoled optimierte Farben verwenden?"), "Username" : MessageLookupByLibrary.simpleMessage("Benutzername"), "Visibility of the chat history" : MessageLookupByLibrary.simpleMessage("Sichtbarkeit des Chat-Verlaufs"), "Visible for all participants" : MessageLookupByLibrary.simpleMessage("Sichtbar für alle Teilnehmer"), diff --git a/lib/i18n/messages_messages.dart b/lib/i18n/messages_messages.dart index 3c79ece..7529315 100644 --- a/lib/i18n/messages_messages.dart +++ b/lib/i18n/messages_messages.dart @@ -150,6 +150,7 @@ class MessageLookup extends MessageLookupByLibrary { "Ban from chat" : MessageLookupByLibrary.simpleMessage("Ban from chat"), "Banned" : MessageLookupByLibrary.simpleMessage("Banned"), "Change the name of the group" : MessageLookupByLibrary.simpleMessage("Change the name of the group"), + "Change your style" : MessageLookupByLibrary.simpleMessage("Change your style"), "Changelog" : MessageLookupByLibrary.simpleMessage("Changelog"), "Chat details" : MessageLookupByLibrary.simpleMessage("Chat details"), "Choose a username" : MessageLookupByLibrary.simpleMessage("Choose a username"), @@ -166,6 +167,7 @@ class MessageLookup extends MessageLookupByLibrary { "Create" : MessageLookupByLibrary.simpleMessage("Create"), "Create account now" : MessageLookupByLibrary.simpleMessage("Create account now"), "Create new group" : MessageLookupByLibrary.simpleMessage("Create new group"), + "Dark" : MessageLookupByLibrary.simpleMessage("Dark"), "Delete" : MessageLookupByLibrary.simpleMessage("Delete"), "Delete message" : MessageLookupByLibrary.simpleMessage("Delete message"), "Discard picture" : MessageLookupByLibrary.simpleMessage("Discard picture"), @@ -199,6 +201,7 @@ class MessageLookup extends MessageLookupByLibrary { "Leave" : MessageLookupByLibrary.simpleMessage("Leave"), "Left the chat" : MessageLookupByLibrary.simpleMessage("Left the chat"), "License" : MessageLookupByLibrary.simpleMessage("License"), + "Light" : MessageLookupByLibrary.simpleMessage("Light"), "Loading... Please wait" : MessageLookupByLibrary.simpleMessage("Loading... Please wait"), "Login" : MessageLookupByLibrary.simpleMessage("Login"), "Logout" : MessageLookupByLibrary.simpleMessage("Logout"), @@ -244,12 +247,14 @@ class MessageLookup extends MessageLookupByLibrary { "Source code" : MessageLookupByLibrary.simpleMessage("Source code"), "Start your first chat :-)" : MessageLookupByLibrary.simpleMessage("Start your first chat :-)"), "Sunday" : MessageLookupByLibrary.simpleMessage("Sunday"), + "System" : MessageLookupByLibrary.simpleMessage("System"), "Tap to show menu" : MessageLookupByLibrary.simpleMessage("Tap to show menu"), "This room has been archived." : MessageLookupByLibrary.simpleMessage("This room has been archived."), "Thursday" : MessageLookupByLibrary.simpleMessage("Thursday"), "Try to send again" : MessageLookupByLibrary.simpleMessage("Try to send again"), "Tuesday" : MessageLookupByLibrary.simpleMessage("Tuesday"), "Unmute chat" : MessageLookupByLibrary.simpleMessage("Unmute chat"), + "Use Amoled compatible colors?" : MessageLookupByLibrary.simpleMessage("Use Amoled compatible colors?"), "Username" : MessageLookupByLibrary.simpleMessage("Username"), "Visibility of the chat history" : MessageLookupByLibrary.simpleMessage("Visibility of the chat history"), "Visible for all participants" : MessageLookupByLibrary.simpleMessage("Visible for all participants"), diff --git a/lib/main.dart b/lib/main.dart index f5e20a0..6db3d11 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,19 +1,17 @@ import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/i18n/i18n.dart'; -import 'package:fluffychat/views/sign_up.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; -import 'package:universal_html/prefer_universal/html.dart' as html; +import 'i18n/i18n.dart'; +import 'views/sign_up.dart'; +import 'components/ThemeSwitcher.dart'; import 'components/matrix.dart'; import 'views/chat_list.dart'; void main() { SystemChrome.setSystemUIOverlayStyle( - SystemUiOverlayStyle(statusBarColor: Colors.white), - ); + SystemUiOverlayStyle(statusBarColor: Colors.transparent)); runApp(App()); } @@ -23,63 +21,39 @@ class App extends StatelessWidget { return Matrix( clientName: "FluffyChat", child: Builder( - builder: (BuildContext context) => MaterialApp( - title: 'FluffyChat', - theme: ThemeData( - brightness: Brightness.light, - primaryColor: Color(0xFF5625BA), - backgroundColor: Colors.white, - secondaryHeaderColor: Color(0xFFECECF2), - scaffoldBackgroundColor: Colors.white, - dialogTheme: DialogTheme( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), + builder: (BuildContext context) => ThemeSwitcherWidget( + child: Builder( + builder: (BuildContext context) => MaterialApp( + title: 'FluffyChat', + theme: ThemeSwitcherWidget.of(context).themeData, + localizationsDelegates: [ + AppLocalizationsDelegate(), + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: [ + const Locale('en'), // English + const Locale('de'), // German + ], + home: FutureBuilder( + future: + Matrix.of(context).client.onLoginStateChanged.stream.first, + builder: (context, snapshot) { + if (!snapshot.hasData) { + return Scaffold( + body: Center( + child: CircularProgressIndicator(), + ), + ); + } + if (Matrix.of(context).client.isLogged()) { + return ChatListView(); + } + return SignUp(); + }, ), ), - popupMenuTheme: PopupMenuThemeData( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - ), - appBarTheme: AppBarTheme( - brightness: Brightness.light, - color: Colors.white, - //elevation: 1, - textTheme: TextTheme( - title: TextStyle( - color: Colors.black, - fontSize: 20, - ), - ), - iconTheme: IconThemeData(color: Colors.black), - ), - ), - localizationsDelegates: [ - AppLocalizationsDelegate(), - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - supportedLocales: [ - const Locale('en'), // English - const Locale('de'), // German - ], - locale: kIsWeb - ? Locale(html.window.navigator.language.split("-").first) - : null, - home: FutureBuilder( - future: Matrix.of(context).client.onLoginStateChanged.stream.first, - builder: (context, snapshot) { - if (!snapshot.hasData) { - return Scaffold( - body: Center( - child: CircularProgressIndicator(), - ), - ); - } - if (Matrix.of(context).client.isLogged()) return ChatListView(); - return SignUp(); - }, ), ), ), diff --git a/lib/views/chat_details.dart b/lib/views/chat_details.dart index c494d9d..72b4f45 100644 --- a/lib/views/chat_details.dart +++ b/lib/views/chat_details.dart @@ -237,7 +237,7 @@ class _ChatDetailsState extends State { linkStyle: TextStyle(color: Colors.blueAccent), textStyle: TextStyle( fontSize: 14, - color: Colors.black, + color: Theme.of(context).accentColor, ), ), onTap: widget.room.canSendEvent("m.room.topic") diff --git a/lib/views/chat_list.dart b/lib/views/chat_list.dart index cf52d8c..373e938 100644 --- a/lib/views/chat_list.dart +++ b/lib/views/chat_list.dart @@ -1,16 +1,6 @@ import 'dart:async'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; -import 'package:fluffychat/components/list_items/chat_list_item.dart'; -import 'package:fluffychat/components/matrix.dart'; -import 'package:fluffychat/i18n/i18n.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/utils/url_launcher.dart'; -import 'package:fluffychat/views/archive.dart'; -import 'package:fluffychat/views/new_group.dart'; -import 'package:fluffychat/views/new_private_chat.dart'; -import 'package:fluffychat/views/settings.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -18,6 +8,18 @@ import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:toast/toast.dart'; import 'package:uni_links/uni_links.dart'; +import '../components/ThemeSwitcher.dart'; +import '../components/adaptive_page_layout.dart'; +import '../components/list_items/chat_list_item.dart'; +import '../components/matrix.dart'; +import '../i18n/i18n.dart'; +import '../utils/app_route.dart'; +import '../utils/url_launcher.dart'; +import 'archive.dart'; +import 'new_group.dart'; +import 'new_private_chat.dart'; +import 'settings.dart'; + enum SelectMode { normal, share } class ChatListView extends StatelessWidget { @@ -39,6 +41,7 @@ class ChatList extends StatefulWidget { final String activeChat; const ChatList({this.activeChat, Key key}) : super(key: key); + @override _ChatListState createState() => _ChatListState(); } @@ -189,13 +192,15 @@ class _ChatListState extends State { ), floatingActionButton: SpeedDial( child: Icon(Icons.add), + overlayColor: blackWhiteColor(context), backgroundColor: Theme.of(context).primaryColor, children: [ SpeedDialChild( child: Icon(Icons.people_outline), backgroundColor: Colors.blue, label: I18n.of(context).createNewGroup, - labelStyle: TextStyle(fontSize: 18.0), + labelStyle: + TextStyle(fontSize: 18.0, color: blackWhiteColor(context)), onTap: () => Navigator.of(context).pushAndRemoveUntil( AppRoute.defaultRoute(context, NewGroupView()), (r) => r.isFirst), @@ -204,7 +209,11 @@ class _ChatListState extends State { child: Icon(Icons.person_add), backgroundColor: Colors.green, label: I18n.of(context).newPrivateChat, - labelStyle: TextStyle(fontSize: 18.0), + labelStyle: TextStyle( + fontSize: 18.0, + color: Theme.of(context).brightness == Brightness.light + ? Colors.white + : Colors.black), onTap: () => Navigator.of(context).pushAndRemoveUntil( AppRoute.defaultRoute(context, NewPrivateChatView()), (r) => r.isFirst), diff --git a/lib/views/login.dart b/lib/views/login.dart index f9146d5..19bf3cc 100644 --- a/lib/views/login.dart +++ b/lib/views/login.dart @@ -120,7 +120,6 @@ class _LoginState extends State { ), ListTile( leading: CircleAvatar( - backgroundColor: Colors.white, child: Icon(Icons.account_box, color: Theme.of(context).primaryColor), ), @@ -137,7 +136,9 @@ class _LoginState extends State { ), ListTile( leading: CircleAvatar( - backgroundColor: Colors.white, + backgroundColor: Theme.of(context).brightness == Brightness.dark + ? Color(0xff121212) + : Colors.white, child: Icon(Icons.lock, color: Theme.of(context).primaryColor), ), title: TextField( diff --git a/lib/views/settings.dart b/lib/views/settings.dart index ec62fb6..8abe59c 100644 --- a/lib/views/settings.dart +++ b/lib/views/settings.dart @@ -1,20 +1,22 @@ import 'dart:io'; import 'package:famedlysdk/famedlysdk.dart'; -import 'package:fluffychat/components/adaptive_page_layout.dart'; -import 'package:fluffychat/components/content_banner.dart'; -import 'package:fluffychat/components/dialogs/simple_dialogs.dart'; -import 'package:fluffychat/components/matrix.dart'; -import 'package:fluffychat/i18n/i18n.dart'; -import 'package:fluffychat/utils/app_route.dart'; -import 'package:fluffychat/views/app_info.dart'; -import 'package:fluffychat/views/chat_list.dart'; -import 'package:fluffychat/views/sign_up.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:toast/toast.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'app_info.dart'; +import 'chat_list.dart'; +import 'settings_themes.dart'; +import 'sign_up.dart'; +import '../components/dialogs/simple_dialogs.dart'; +import '../components/adaptive_page_layout.dart'; +import '../components/content_banner.dart'; +import '../components/matrix.dart'; +import '../i18n/i18n.dart'; +import '../utils/app_route.dart'; + class SettingsView extends StatelessWidget { @override Widget build(BuildContext context) { @@ -34,6 +36,7 @@ class Settings extends StatefulWidget { class _SettingsState extends State { Future profileFuture; dynamic profile; + void logoutAction(BuildContext context) async { if (await SimpleDialogs(context).askConfirmation() == false) { return; @@ -140,6 +143,17 @@ class _SettingsState extends State { subtitle: Text(profile?.displayname ?? client.userID.localpart), onTap: () => setDisplaynameAction(context), ), + ListTile( + trailing: Icon(Icons.color_lens), + title: Text(I18n.of(context).changeTheme), + onTap: () async => await Navigator.of(context).push( + AppRoute.defaultRoute( + context, + ThemesSettingsView(), + ), + ), + ), + Divider(thickness: 1), ListTile( trailing: Icon(Icons.exit_to_app), title: Text(I18n.of(context).logout), diff --git a/lib/views/settings_themes.dart b/lib/views/settings_themes.dart new file mode 100644 index 0000000..0e20faa --- /dev/null +++ b/lib/views/settings_themes.dart @@ -0,0 +1,105 @@ +import 'package:flutter/material.dart'; + +import '../components/ThemeSwitcher.dart'; +import '../components/adaptive_page_layout.dart'; +import '../components/matrix.dart'; +import '../i18n/i18n.dart'; +import 'chat_list.dart'; + +class ThemesSettingsView extends StatelessWidget { + @override + Widget build(BuildContext context) { + return AdaptivePageLayout( + primaryPage: FocusPage.SECOND, + firstScaffold: ChatList(), + secondScaffold: ThemesSettings(), + ); + } +} + +class ThemesSettings extends StatefulWidget { + @override + ThemesSettingsState createState() => ThemesSettingsState(); +} + +class ThemesSettingsState extends State { + Themes _selectedTheme; + bool _amoledEnabled; + + @override + Widget build(BuildContext context) { + final MatrixState matrix = Matrix.of(context); + final ThemeSwitcherWidgetState themeEngine = + ThemeSwitcherWidget.of(context); + _selectedTheme = themeEngine.selectedTheme; + _amoledEnabled = themeEngine.amoledEnabled; + + return Scaffold( + appBar: AppBar( + title: Text(I18n.of(context).changeTheme), + ), + body: Column( + children: [ + RadioListTile( + title: Text( + I18n.of(context).systemTheme, + ), + value: Themes.system, + groupValue: _selectedTheme, + activeColor: Theme.of(context).primaryColor, + onChanged: (Themes value) { + setState(() { + _selectedTheme = value; + themeEngine.switchTheme(matrix, value, _amoledEnabled); + }); + }, + ), + RadioListTile( + title: Text( + I18n.of(context).lightTheme, + ), + value: Themes.light, + groupValue: _selectedTheme, + activeColor: Theme.of(context).primaryColor, + onChanged: (Themes value) { + setState(() { + _selectedTheme = value; + themeEngine.switchTheme(matrix, value, _amoledEnabled); + }); + }, + ), + RadioListTile( + title: Text( + I18n.of(context).darkTheme, + ), + value: Themes.dark, + groupValue: _selectedTheme, + activeColor: Theme.of(context).primaryColor, + onChanged: (Themes value) { + setState(() { + _selectedTheme = value; + themeEngine.switchTheme(matrix, value, _amoledEnabled); + }); + }, + ), + Divider(thickness: 8), + ListTile( + title: Text( + I18n.of(context).useAmoledTheme, + ), + trailing: Switch( + value: _amoledEnabled, + activeColor: Theme.of(context).primaryColor, + onChanged: (bool value) { + setState(() { + _amoledEnabled = value; + themeEngine.switchTheme(matrix, _selectedTheme, value); + }); + }, + ), + ), + ], + ), + ); + } +} diff --git a/lib/views/sign_up.dart b/lib/views/sign_up.dart index f59da37..52fb382 100644 --- a/lib/views/sign_up.dart +++ b/lib/views/sign_up.dart @@ -97,12 +97,13 @@ class _SignUpState extends State { autocorrect: false, controller: serverController, decoration: InputDecoration( - icon: Icon(Icons.domain), - hintText: "matrix-client.matrix.org", - errorText: serverError, - errorMaxLines: 1, - prefixText: "https://", - labelText: serverError == null ? "Homeserver" : serverError), + icon: Icon(Icons.domain), + hintText: "matrix-client.matrix.org", + errorText: serverError, + errorMaxLines: 1, + prefixText: "https://", + labelText: serverError == null ? "Homeserver" : serverError, + ), ), ), body: ListView( @@ -115,7 +116,7 @@ class _SignUpState extends State { leading: CircleAvatar( backgroundImage: avatar == null ? null : FileImage(avatar), backgroundColor: avatar == null - ? Colors.white + ? Theme.of(context).brightness == Brightness.dark ? Color(0xff121212) : Colors.white : Theme.of(context).secondaryHeaderColor, child: avatar == null ? Icon(Icons.camera_alt, @@ -137,7 +138,7 @@ class _SignUpState extends State { ), ListTile( leading: CircleAvatar( - backgroundColor: Colors.white, + backgroundColor: Theme.of(context).brightness == Brightness.dark ? Color(0xff121212) : Colors.white, child: Icon( Icons.account_circle, color: Theme.of(context).primaryColor,