Browse Source

Merge pull request #94 from jld3103/feature/m3-theme

Implement Material 3 theme
pull/96/head
jld3103 2 years ago committed by GitHub
parent
commit
f1eaba5376
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 19
      packages/neon/integration_test/screenshot_test.dart
  2. 1
      packages/neon/lib/l10n/en.arb
  3. 6
      packages/neon/lib/l10n/localizations.dart
  4. 3
      packages/neon/lib/l10n/localizations_en.dart
  5. 23
      packages/neon/lib/src/app.dart
  6. 20
      packages/neon/lib/src/apps/notes/pages/note.dart
  7. 1
      packages/neon/lib/src/apps/notifications/pages/main.dart
  8. 6
      packages/neon/lib/src/pages/home/home.dart
  9. 3
      packages/neon/lib/src/pages/settings/settings.dart
  10. 2
      packages/neon/lib/src/utils/confirmation_dialog.dart
  11. 8
      packages/neon/lib/src/utils/global_options.dart
  12. 113
      packages/neon/lib/src/utils/theme.dart
  13. BIN
      packages/neon/screenshots/files_actions.png
  14. BIN
      packages/neon/screenshots/files_create.png
  15. BIN
      packages/neon/screenshots/files_details.png
  16. BIN
      packages/neon/screenshots/files_photos.png
  17. BIN
      packages/neon/screenshots/files_root.png
  18. BIN
      packages/neon/screenshots/home_drawer.png
  19. BIN
      packages/neon/screenshots/login_form.png
  20. BIN
      packages/neon/screenshots/login_server_selection.png
  21. BIN
      packages/neon/screenshots/news_articles_starred_list.png
  22. BIN
      packages/neon/screenshots/news_articles_unread_list.png
  23. BIN
      packages/neon/screenshots/news_feed_add.png
  24. BIN
      packages/neon/screenshots/news_feed_articles_list.png
  25. BIN
      packages/neon/screenshots/news_feeds_list.png
  26. BIN
      packages/neon/screenshots/news_folders_list.png
  27. BIN
      packages/neon/screenshots/notes_categories_list.png
  28. BIN
      packages/neon/screenshots/notes_note_create.png
  29. BIN
      packages/neon/screenshots/notes_note_edit.png
  30. BIN
      packages/neon/screenshots/notes_note_preview.png
  31. BIN
      packages/neon/screenshots/notes_notes_list.png
  32. BIN
      packages/neon/screenshots/notifications_list.png
  33. BIN
      packages/neon/screenshots/settings_account.png
  34. BIN
      packages/neon/screenshots/settings_accounts.png
  35. BIN
      packages/neon/screenshots/settings_app_files.png
  36. BIN
      packages/neon/screenshots/settings_app_news.png
  37. BIN
      packages/neon/screenshots/settings_app_notes.png
  38. BIN
      packages/neon/screenshots/settings_dark.png
  39. BIN
      packages/neon/screenshots/settings_light.png
  40. BIN
      packages/neon/screenshots/settings_oled.png

19
packages/neon/integration_test/screenshot_test.dart

@ -15,6 +15,7 @@ import 'package:neon/src/neon.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart';
import 'package:settings/settings.dart';
import 'package:shared_preferences/shared_preferences.dart';
class MemorySharedPreferences implements SharedPreferences {
@ -169,19 +170,22 @@ Future pumpAppPage(
],
child: StreamBuilder<NextcloudTheme>(
stream: userThemeStream,
builder: (final context, final themeSnapshot) => StreamBuilder<ThemeMode>(
stream: globalOptions.themeMode.stream,
builder: (final context, final themeModeSnapshot) => StreamBuilder<bool>(
stream: globalOptions.themeOLEDAsDark.stream,
builder: (final context, final themeOLEDAsDarkSnapshot) => MaterialApp(
builder: (final context, final themeSnapshot) => OptionBuilder(
option: globalOptions.themeMode,
builder: (final context, final themeMode) => OptionBuilder(
option: globalOptions.themeOLEDAsDark,
builder: (final context, final themeOLEDAsDark) => OptionBuilder(
option: globalOptions.themeKeepOriginalAccentColor,
builder: (final context, final themeKeepOriginalAccentColor) => MaterialApp(
debugShowCheckedModeBanner: false,
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
theme: getThemeFromNextcloudTheme(
themeSnapshot.data,
themeModeSnapshot.data ?? ThemeMode.system,
themeMode ?? ThemeMode.system,
Brightness.light,
oledAsDark: themeOLEDAsDarkSnapshot.data ?? false,
oledAsDark: themeOLEDAsDark ?? false,
keepOriginalAccentColor: themeKeepOriginalAccentColor ?? true,
),
home: builder(context, userThemeStream.add),
),
@ -189,6 +193,7 @@ Future pumpAppPage(
),
),
),
),
);
await tester.pumpAndSettle();
}

1
packages/neon/lib/l10n/en.arb

@ -80,6 +80,7 @@
"globalOptionsThemeModeDark": "Dark",
"globalOptionsThemeModeAutomatic": "Automatic",
"globalOptionsThemeOLEDAsDark": "OLED theme as dark theme",
"globalOptionsThemeKeepOriginalAccentColor": "Keep the original accent color",
"globalOptionsPushNotificationsNotice": "External services are used for delivering push notifications. While the content is encrypted and can only be read by this app, extracting metadata like the time and count of notifications is still possible.",
"globalOptionsPushNotificationsEnabled": "Enabled",
"globalOptionsPushNotificationsEnabledDisabledNotice": "No UnifiedPush distributor could be found. Please go to https://unifiedpush.org/users/distributors and setup any of the listed distributors. Then re-open this app and you should be able to enable notifications",

6
packages/neon/lib/l10n/localizations.dart

@ -401,6 +401,12 @@ abstract class AppLocalizations {
/// **'OLED theme as dark theme'**
String get globalOptionsThemeOLEDAsDark;
/// No description provided for @globalOptionsThemeKeepOriginalAccentColor.
///
/// In en, this message translates to:
/// **'Keep the original accent color'**
String get globalOptionsThemeKeepOriginalAccentColor;
/// No description provided for @globalOptionsPushNotificationsNotice.
///
/// In en, this message translates to:

3
packages/neon/lib/l10n/localizations_en.dart

@ -171,6 +171,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get globalOptionsThemeOLEDAsDark => 'OLED theme as dark theme';
@override
String get globalOptionsThemeKeepOriginalAccentColor => 'Keep the original accent color';
@override
String get globalOptionsPushNotificationsNotice =>
'External services are used for delivering push notifications. While the content is encrypted and can only be read by this app, extracting metadata like the time and count of notifications is still possible.';

23
packages/neon/lib/src/app.dart

@ -92,14 +92,17 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver {
@override
Widget build(final BuildContext context) => StreamBuilder<Brightness>(
stream: _platformBrightness,
builder: (final context, final platformBrightnessSnapshot) => StreamBuilder<ThemeMode>(
stream: widget.globalOptions.themeMode.stream,
builder: (final context, final themeModeSnapshot) => StreamBuilder<bool>(
stream: widget.globalOptions.themeOLEDAsDark.stream,
builder: (final context, final themeOLEDAsDarkSnapshot) {
builder: (final context, final platformBrightnessSnapshot) => OptionBuilder(
option: widget.globalOptions.themeMode,
builder: (final context, final themeMode) => OptionBuilder(
option: widget.globalOptions.themeOLEDAsDark,
builder: (final context, final themeOLEDAsDark) => OptionBuilder(
option: widget.globalOptions.themeKeepOriginalAccentColor,
builder: (final context, final themeKeepOriginalAccentColor) {
if (!platformBrightnessSnapshot.hasData ||
!themeOLEDAsDarkSnapshot.hasData ||
!themeModeSnapshot.hasData) {
themeMode == null ||
themeOLEDAsDark == null ||
themeKeepOriginalAccentColor == null) {
return Container();
}
return MaterialApp(
@ -108,14 +111,16 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver {
navigatorKey: _navigatorKey,
theme: getThemeFromNextcloudTheme(
_userTheme,
themeModeSnapshot.data!,
themeMode,
platformBrightnessSnapshot.data!,
oledAsDark: themeOLEDAsDarkSnapshot.data!,
oledAsDark: themeOLEDAsDark,
keepOriginalAccentColor: themeKeepOriginalAccentColor,
),
home: Container(),
);
},
),
),
),
);
}

20
packages/neon/lib/src/apps/notes/pages/note.dart

@ -85,13 +85,7 @@ class _NotesNotePageState extends State<NotesNotePage> {
}
@override
Widget build(final BuildContext context) {
final titleInputBorder = UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).colorScheme.onPrimary,
),
);
return WillPopScope(
Widget build(final BuildContext context) => WillPopScope(
onWillPop: () async {
_update();
@ -101,21 +95,20 @@ class _NotesNotePageState extends State<NotesNotePage> {
return true;
},
child: Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
titleSpacing: 0,
title: TextField(
controller: _titleController,
focusNode: _titleFocusNode,
style: TextStyle(
style: const TextStyle(
fontSize: 22,
color: Theme.of(context).colorScheme.onPrimary,
),
cursorColor: Theme.of(context).colorScheme.onPrimary,
decoration: InputDecoration(
decoration: const InputDecoration(
isDense: true,
contentPadding: EdgeInsets.zero,
border: titleInputBorder,
focusedBorder: titleInputBorder,
border: UnderlineInputBorder(),
focusedBorder: UnderlineInputBorder(),
),
),
actions: [
@ -201,4 +194,3 @@ class _NotesNotePageState extends State<NotesNotePage> {
),
);
}
}

1
packages/neon/lib/src/apps/notifications/pages/main.dart

@ -112,6 +112,7 @@ class _NotificationsMainPageState extends State<NotificationsMainPage> {
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () {
Navigator.of(context).pop();

6
packages/neon/lib/src/pages/home/home.dart

@ -280,6 +280,7 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () {
Navigator.of(context).pop();
@ -427,7 +428,7 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
}
return DrawerHeader(
decoration: BoxDecoration(
color: Theme.of(context).appBarTheme.backgroundColor,
color: Theme.of(context).colorScheme.primary,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@ -461,7 +462,7 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
child: DropdownButton<String>(
isExpanded: true,
dropdownColor: Theme.of(context).colorScheme.primary,
iconEnabledColor: Theme.of(context).appBarTheme.foregroundColor,
iconEnabledColor: Theme.of(context).colorScheme.onBackground,
value: widget.account.id,
items: accounts
.map<DropdownMenuItem<String>>(
@ -607,6 +608,7 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
);
return Scaffold(
resizeToAvoidBottomInset: false,
body: Row(
children: [
if (navigationMode == NavigationMode.drawerAlwaysVisible) ...[

3
packages/neon/lib/src/pages/settings/settings.dart

@ -105,6 +105,9 @@ class _SettingsPageState extends State<SettingsPage> {
CheckBoxSettingsTile(
option: globalOptions.themeOLEDAsDark,
),
CheckBoxSettingsTile(
option: globalOptions.themeKeepOriginalAccentColor,
),
],
),
SettingsCategory(

2
packages/neon/lib/src/utils/confirmation_dialog.dart

@ -10,6 +10,7 @@ Future<bool> showConfirmationDialog(final BuildContext context, final String tit
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () {
Navigator.of(context).pop(false);
@ -19,6 +20,7 @@ Future<bool> showConfirmationDialog(final BuildContext context, final String tit
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
),
onPressed: () {
Navigator.of(context).pop(true);

8
packages/neon/lib/src/utils/global_options.dart

@ -60,6 +60,7 @@ class GlobalOptions {
late final List<Option> options = [
themeMode,
themeOLEDAsDark,
themeKeepOriginalAccentColor,
pushNotificationsEnabled,
pushNotificationsDistributor,
startupMinimized,
@ -135,6 +136,13 @@ class GlobalOptions {
enabled: _themeOLEDAsDarkEnabledSubject,
);
late final themeKeepOriginalAccentColor = ToggleOption(
storage: _storage,
key: 'theme-keep-original-accent-color',
label: (final context) => AppLocalizations.of(context).globalOptionsThemeKeepOriginalAccentColor,
defaultValue: BehaviorSubject.seeded(false),
);
late final pushNotificationsEnabled = ToggleOption(
storage: _storage,
key: 'push-notifications-enabled',

113
packages/neon/lib/src/utils/theme.dart

@ -1,20 +1,15 @@
part of '../neon.dart';
const themePrimaryColor = Color(0xFFF37736);
const themeOnPrimaryColor = Color(0xFFFFFFFF);
ThemeData getThemeFromNextcloudTheme(
final CoreServerCapabilities_Ocs_Data_Capabilities_Theming? nextcloudTheme,
final ThemeMode themeMode,
final Brightness platformBrightness, {
required final bool oledAsDark,
required final bool keepOriginalAccentColor,
}) {
var primaryColor = themePrimaryColor;
var onPrimaryColor = themeOnPrimaryColor;
if (nextcloudTheme != null) {
primaryColor = HexColor(nextcloudTheme.color);
onPrimaryColor = HexColor(nextcloudTheme.colorText);
}
final primaryColor = nextcloudTheme != null ? HexColor(nextcloudTheme.color) : themePrimaryColor;
late final Brightness selectBrightness;
switch (themeMode) {
@ -29,62 +24,24 @@ ThemeData getThemeFromNextcloudTheme(
break;
}
final backgroundColor = selectBrightness == Brightness.dark
? oledAsDark
? Colors.black
: const Color(0xFF303030)
: Colors.white;
final onBackgroundColor = selectBrightness == Brightness.dark ? Colors.white : const Color(0xFF303030);
final canvasColor = selectBrightness == Brightness.dark
? oledAsDark
? const Color(0xFF202020)
: const Color(0xFF404040)
: const Color(0xFFEAEAEA);
final disabledColor = selectBrightness == Brightness.dark
? oledAsDark
? Colors.grey[700]
: Colors.grey[600]
: Colors.grey[500];
final colorScheme =
(selectBrightness == Brightness.dark ? const ColorScheme.dark() : const ColorScheme.light()).copyWith(
primary: primaryColor,
onPrimary: onPrimaryColor,
secondary: primaryColor,
onSecondary: onPrimaryColor,
background: backgroundColor,
onBackground: onBackgroundColor,
final oledBackgroundOverride = selectBrightness == Brightness.dark && oledAsDark ? Colors.black : null;
final colorScheme = ColorScheme.fromSeed(
seedColor: primaryColor,
brightness: selectBrightness,
).copyWith(
background: oledBackgroundOverride,
surface: oledBackgroundOverride,
primary: keepOriginalAccentColor ? primaryColor : null,
);
return ThemeData(
useMaterial3: true,
disabledColor: disabledColor,
brightness: selectBrightness,
scaffoldBackgroundColor: backgroundColor,
canvasColor: canvasColor,
cardColor: backgroundColor,
colorScheme: colorScheme,
textSelectionTheme: TextSelectionThemeData(
cursorColor: primaryColor,
selectionColor: primaryColor,
selectionHandleColor: primaryColor,
),
appBarTheme: AppBarTheme(
backgroundColor: primaryColor,
foregroundColor: selectBrightness == Brightness.dark ? Colors.white : Colors.black,
),
snackBarTheme: SnackBarThemeData(
scaffoldBackgroundColor: colorScheme.background,
canvasColor: colorScheme.background, // For Drawer
cardColor: colorScheme.background, // For LicensePage
snackBarTheme: const SnackBarThemeData(
behavior: SnackBarBehavior.floating,
actionTextColor: primaryColor,
),
progressIndicatorTheme: ProgressIndicatorThemeData(
color: primaryColor,
),
drawerTheme: DrawerThemeData(
backgroundColor: backgroundColor,
),
checkboxTheme: CheckboxThemeData(
fillColor: MaterialStateProperty.resolveWith((final states) {
@ -92,43 +49,21 @@ ThemeData getThemeFromNextcloudTheme(
return selectBrightness == Brightness.dark ? Colors.white38 : Colors.black38;
}
return primaryColor;
return colorScheme.primary;
}),
checkColor: MaterialStateProperty.resolveWith((final states) => onPrimaryColor),
checkColor: MaterialStateProperty.resolveWith((final states) => colorScheme.onPrimary),
),
dividerTheme: DividerThemeData(
color: primaryColor,
dividerTheme: const DividerThemeData(
thickness: 1.5,
space: 40,
indent: 10,
endIndent: 10,
),
scrollbarTheme: ScrollbarThemeData(
thumbColor: MaterialStateProperty.resolveWith(
(final states) => primaryColor
.withOpacity(states.contains(MaterialState.hovered) || states.contains(MaterialState.dragged) ? 1 : 0.5),
),
mainAxisMargin: 10,
crossAxisMargin: 5,
),
radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.resolveWith(
(final states) => states.contains(MaterialState.disabled) ? disabledColor : primaryColor,
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
foregroundColor: onPrimaryColor,
backgroundColor: primaryColor,
).copyWith(
elevation: ButtonStyleButton.allOrNull(0),
),
space: 30,
),
popupMenuTheme: PopupMenuThemeData(
color: canvasColor,
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: primaryColor,
// TODO: Only needed until M3 popup menus are implemented
color: selectBrightness == Brightness.dark
? oledAsDark
? const Color(0xFF202020)
: const Color(0xFF404040)
: const Color(0xFFEAEAEA),
),
);
}

BIN
packages/neon/screenshots/files_actions.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 343 KiB

After

Width:  |  Height:  |  Size: 357 KiB

BIN
packages/neon/screenshots/files_create.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

After

Width:  |  Height:  |  Size: 285 KiB

BIN
packages/neon/screenshots/files_details.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 752 KiB

After

Width:  |  Height:  |  Size: 757 KiB

BIN
packages/neon/screenshots/files_photos.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 KiB

After

Width:  |  Height:  |  Size: 339 KiB

BIN
packages/neon/screenshots/files_root.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 KiB

After

Width:  |  Height:  |  Size: 142 KiB

BIN
packages/neon/screenshots/home_drawer.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 70 KiB

BIN
packages/neon/screenshots/login_form.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 850 KiB

After

Width:  |  Height:  |  Size: 850 KiB

BIN
packages/neon/screenshots/login_server_selection.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 51 KiB

BIN
packages/neon/screenshots/news_articles_starred_list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 93 KiB

BIN
packages/neon/screenshots/news_articles_unread_list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 KiB

After

Width:  |  Height:  |  Size: 312 KiB

BIN
packages/neon/screenshots/news_feed_add.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 95 KiB

BIN
packages/neon/screenshots/news_feed_articles_list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 300 KiB

After

Width:  |  Height:  |  Size: 358 KiB

BIN
packages/neon/screenshots/news_feeds_list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 98 KiB

BIN
packages/neon/screenshots/news_folders_list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

BIN
packages/neon/screenshots/notes_categories_list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 42 KiB

BIN
packages/neon/screenshots/notes_note_create.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 78 KiB

BIN
packages/neon/screenshots/notes_note_edit.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 37 KiB

BIN
packages/neon/screenshots/notes_note_preview.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 36 KiB

BIN
packages/neon/screenshots/notes_notes_list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 61 KiB

BIN
packages/neon/screenshots/notifications_list.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 36 KiB

BIN
packages/neon/screenshots/settings_account.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 94 KiB

BIN
packages/neon/screenshots/settings_accounts.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 165 KiB

BIN
packages/neon/screenshots/settings_app_files.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 79 KiB

BIN
packages/neon/screenshots/settings_app_news.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 165 KiB

BIN
packages/neon/screenshots/settings_app_notes.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 114 KiB

BIN
packages/neon/screenshots/settings_dark.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 162 KiB

BIN
packages/neon/screenshots/settings_light.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 160 KiB

BIN
packages/neon/screenshots/settings_oled.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 147 KiB

Loading…
Cancel
Save