Browse Source

neon: allow apps to define custom themes

pull/449/head
Nikolas Rimikis 1 year ago
parent
commit
1bca03c4d5
No known key found for this signature in database
GPG Key ID: 85ED1DE9786A4FF2
  1. 22
      packages/neon/neon/lib/src/app.dart
  2. 5
      packages/neon/neon/lib/src/models/app_implementation.dart
  3. 101
      packages/neon/neon/lib/src/utils/theme.dart

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

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:neon/l10n/localizations.dart';
@ -285,7 +286,13 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
? _accountsBloc.getCapabilitiesBlocFor(activeAccountSnapshot.data!).capabilities
: null,
builder: (final context, final capabilitiesSnapshot) {
final nextcloudTheme = capabilitiesSnapshot.data?.capabilities.theming;
final appTheme = AppTheme(
capabilitiesSnapshot.data?.capabilities.theming,
keepOriginalAccentColor: themeKeepOriginalAccentColor,
oledAsDark: themeOLEDAsDark,
appThemes: _appImplementations.map((final a) => a.theme).whereNotNull(),
);
return MaterialApp.router(
localizationsDelegates: [
..._appImplementations.map((final app) => app.localizationsDelegate),
@ -298,17 +305,8 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
...AppLocalizations.supportedLocales,
},
themeMode: themeMode,
theme: getThemeFromNextcloudTheme(
nextcloudTheme,
Brightness.light,
keepOriginalAccentColor: nextcloudTheme == null || themeKeepOriginalAccentColor,
),
darkTheme: getThemeFromNextcloudTheme(
nextcloudTheme,
Brightness.dark,
keepOriginalAccentColor: nextcloudTheme == null || themeKeepOriginalAccentColor,
oledAsDark: themeOLEDAsDark,
),
theme: appTheme.lightTheme,
darkTheme: appTheme.darkTheme,
routerConfig: _routerDelegate,
);
},

5
packages/neon/neon/lib/src/models/app_implementation.dart

@ -86,6 +86,11 @@ abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions>
void dispose() {
options.dispose();
}
/// A custom theme that will be injected into the widget tree.
///
/// You can later acess it through `Theme.of(context).extension<ThemeName>()`.
ThemeExtension? theme;
}
extension AppImplementationFind on Iterable<AppImplementation> {

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

@ -7,40 +7,79 @@ import 'package:nextcloud/nextcloud.dart';
const themePrimaryColor = Color(0xFFF37736);
@internal
ThemeData getThemeFromNextcloudTheme(
final CoreServerCapabilities_Ocs_Data_Capabilities_Theming? nextcloudTheme,
final Brightness brightness, {
required final bool keepOriginalAccentColor,
final bool oledAsDark = false,
}) {
if (oledAsDark) {
assert(brightness == Brightness.dark, 'Brightness.dark is required for oledAsDark.');
@immutable
class AppTheme {
AppTheme(
this.nextcloudTheme, {
final bool keepOriginalAccentColor = false,
this.oledAsDark = false,
this.appThemes,
}) : keepOriginalAccentColor = nextcloudTheme == null || keepOriginalAccentColor;
final CoreServerCapabilities_Ocs_Data_Capabilities_Theming? nextcloudTheme;
final bool keepOriginalAccentColor;
final bool oledAsDark;
final Iterable<ThemeExtension>? appThemes;
late final _primaryColor = nextcloudTheme?.color != null ? HexColor(nextcloudTheme!.color!) : themePrimaryColor;
late final _keepOriginalAccentColorOverride = keepOriginalAccentColor ? _primaryColor : null;
ColorScheme _buildColorScheme(final Brightness brightness) {
final oledBackgroundOverride = oledAsDark && brightness == Brightness.dark ? Colors.black : null;
return ColorScheme.fromSeed(
seedColor: _primaryColor,
brightness: brightness,
).copyWith(
background: oledBackgroundOverride,
primary: _keepOriginalAccentColorOverride,
secondary: _keepOriginalAccentColorOverride,
);
}
ThemeData _getTheme(final Brightness brightness) {
final colorScheme = _buildColorScheme(brightness);
return ThemeData(
useMaterial3: true,
colorScheme: colorScheme,
scaffoldBackgroundColor: colorScheme.background,
cardColor: colorScheme.background, // For LicensePage
snackBarTheme: _snackBarTheme,
dividerTheme: _dividerTheme,
extensions: [
const NeonTheme(),
...?appThemes,
],
);
}
final primaryColor = nextcloudTheme?.color != null ? HexColor(nextcloudTheme!.color!) : themePrimaryColor;
final oledBackgroundOverride = oledAsDark ? Colors.black : null;
final keepOriginalAccentColorOverride = keepOriginalAccentColor ? primaryColor : null;
final colorScheme = ColorScheme.fromSeed(
seedColor: primaryColor,
brightness: brightness,
).copyWith(
background: oledBackgroundOverride,
primary: keepOriginalAccentColorOverride,
secondary: keepOriginalAccentColorOverride,
ThemeData get lightTheme => _getTheme(Brightness.light);
ThemeData get darkTheme => _getTheme(Brightness.dark);
static const _snackBarTheme = SnackBarThemeData(
behavior: SnackBarBehavior.floating,
);
return ThemeData(
useMaterial3: true,
colorScheme: colorScheme,
scaffoldBackgroundColor: colorScheme.background,
cardColor: colorScheme.background, // For LicensePage
snackBarTheme: const SnackBarThemeData(
behavior: SnackBarBehavior.floating,
),
dividerTheme: const DividerThemeData(
thickness: 1.5,
space: 30,
),
static const _dividerTheme = DividerThemeData(
thickness: 1.5,
space: 30,
);
}
@internal
@immutable
class NeonTheme extends ThemeExtension<NeonTheme> {
const NeonTheme();
@override
NeonTheme copyWith() => const NeonTheme();
@override
NeonTheme lerp(final NeonTheme? other, final double t) {
if (other is! NeonTheme) {
return this;
}
return const NeonTheme();
}
}

Loading…
Cancel
Save