Skip to content

Commit 8e66fa0

Browse files
authored
React to changes in the accent color on Windows (#7)
2 parents b9896d5 + 9820213 commit 8e66fa0

7 files changed

Lines changed: 344 additions & 120 deletions

File tree

README.md

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,10 @@
2929

3030
### Supported platforms
3131

32-
| Feature | Android 10+ | iOS | Web | MacOs 10.4+ | Windows 10+ and XBox | Linux GTK 3+ |
33-
| ---------------- | :---------: | :-: | :-: | :---------: | :------------------: | :----------: |
34-
| Get accent color | ✔️ | | ✔️ | ✔️ | ✔️ | ✔️ |
32+
| Feature | Android 10+ | iOS | Web | MacOs 10.4+ | Windows 10+ and XBox | Linux GTK 3+ |
33+
| ----------------- | :---------: | :-: | :-: | :---------: | :------------------: | :----------: |
34+
| Get accent color | ✔️ | | ✔️ | ✔️ | ✔️ | ✔️ |
35+
| Listem to changes | | | | | ✔️ | |
3536

3637
## Usage
3738

@@ -80,10 +81,29 @@ void main() async {
8081
}
8182
```
8283

84+
### Listen to changes on the system accent color
85+
86+
To simply listen to changes on the system accent color, use the `SystemTheme.onChange` stream:
87+
88+
```dart
89+
SystemTheme.onChange.listen((event) {
90+
debugPrint('Accent color changed to ${event.accentColor}');
91+
});
92+
```
93+
94+
Alteratively, you can the `SystemThemeBuilder` widget to listen to changes on the system accent color:
95+
96+
```dart
97+
SystemThemeBuilder(builder: (context, accent) {
98+
return ColoredBox(color: accent.accentColor);
99+
});
100+
```
101+
83102
## Contribution
84103

85104
Feel free to [open an issue](https://github.com/bdlukaa/system_theme/issues/new) if you find an error or [make pull requests](https://github.com/bdlukaa/system_theme/pulls).
86105

87106
### Acknowlegments
88107

89108
- [@alexmercerind](https://github.com/alexmercerind) for the Windows implementation
109+
- [@pgiacomo69](https://github.com/pgiacomo69) for the accent color listener

example/lib/main.dart

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ import 'package:system_theme/system_theme.dart';
44
void main() async {
55
WidgetsFlutterBinding.ensureInitialized();
66
await SystemTheme.accentColor.load();
7+
SystemTheme.onChange.listen((color) {
8+
debugPrint('Accent color changed to ${color.accent}');
9+
});
10+
711
runApp(const MaterialApp(home: MyApp()));
812
}
913

@@ -15,51 +19,47 @@ class MyApp extends StatefulWidget {
1519
}
1620

1721
class _MyAppState extends State<MyApp> {
18-
@override
19-
void initState() {
20-
super.initState();
21-
debugPrint(SystemTheme.accentColor.accent.toString());
22-
}
23-
2422
@override
2523
Widget build(BuildContext context) {
26-
final colors = [
27-
SystemTheme.accentColor.lightest,
28-
SystemTheme.accentColor.lighter,
29-
SystemTheme.accentColor.light,
30-
SystemTheme.accentColor.accent,
31-
SystemTheme.accentColor.dark,
32-
SystemTheme.accentColor.darker,
33-
SystemTheme.accentColor.darkest,
34-
];
35-
return Scaffold(
36-
body: Column(
37-
children: colors.map((color) {
38-
return Expanded(
39-
child: Container(
40-
color: color,
41-
alignment: Alignment.bottomCenter,
42-
padding: const EdgeInsets.symmetric(vertical: 20.0),
43-
child: Text(
44-
[
45-
'Lightest',
46-
'Lighter',
47-
'Light',
48-
'Default',
49-
'Dark',
50-
'Darker',
51-
'Darkest',
52-
][colors.indexOf(color)],
53-
style: Theme.of(context).textTheme.titleLarge?.copyWith(
54-
color: color.computeLuminance() >= 0.5
55-
? Colors.black
56-
: Colors.white,
57-
),
24+
return SystemThemeBuilder(builder: (context, accent) {
25+
final colors = [
26+
accent.lightest,
27+
accent.lighter,
28+
accent.light,
29+
accent.accent,
30+
accent.dark,
31+
accent.darker,
32+
accent.darkest,
33+
];
34+
return Scaffold(
35+
body: Column(
36+
children: colors.map((color) {
37+
return Expanded(
38+
child: Container(
39+
color: color,
40+
alignment: Alignment.bottomCenter,
41+
padding: const EdgeInsets.symmetric(vertical: 20.0),
42+
child: Text(
43+
[
44+
'Lightest',
45+
'Lighter',
46+
'Light',
47+
'Default',
48+
'Dark',
49+
'Darker',
50+
'Darkest',
51+
][colors.indexOf(color)],
52+
style: Theme.of(context).textTheme.titleLarge?.copyWith(
53+
color: color.computeLuminance() >= 0.5
54+
? Colors.black
55+
: Colors.white,
56+
),
57+
),
5858
),
59-
),
60-
);
61-
}).toList(),
62-
),
63-
);
59+
);
60+
}).toList(),
61+
),
62+
);
63+
});
6464
}
6565
}

lib/system_theme.dart

Lines changed: 68 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,37 @@
1-
import 'package:flutter/foundation.dart' show debugPrint;
1+
import 'dart:async';
2+
import 'dart:io';
3+
4+
import 'package:flutter/foundation.dart' show debugPrint, kIsWeb;
25
import 'package:flutter/services.dart'
3-
show Color, MethodChannel, MissingPluginException;
6+
show Color, EventChannel, MethodChannel, MissingPluginException;
47
import 'package:flutter/widgets.dart' show WidgetsFlutterBinding;
58

9+
export 'system_theme_builder.dart';
10+
611
/// Default system accent color.
712
const kDefaultFallbackColor = Color(0xff00b7c3);
813

914
const kGetSystemAccentColorMethod = 'SystemTheme.accentColor';
1015

16+
/// Platform event channel handler for system theme changes.
17+
const _eventChannel = EventChannel('system_theme_events/switch_callback');
18+
1119
/// Platform channel handler for invoking native methods.
1220
const MethodChannel _channel = MethodChannel('system_theme');
1321

1422
/// Class to return current system theme state on Windows.
1523
///
16-
/// [accentColor] returns the current accent color as a [SystemAccentColor]. To
17-
/// configure a fallback color if [accentColor] is not available, set [fallback]
18-
/// to the desired color
24+
/// [accentColor] returns the current accent color as a [SystemAccentColor].
25+
///
26+
/// To configure a fallback color if [accentColor] is not available, set
27+
/// [fallbackColor] to the desired color
28+
///
29+
/// [onChange] returns a stream of [SystemAccentColor] that notifies when the
30+
/// system accent color changes.
1931
class SystemTheme {
2032
/// The fallback color
33+
///
34+
/// Returns [kDefaultFallbackColor] if not set
2135
static Color fallbackColor = kDefaultFallbackColor;
2236

2337
/// Get the system accent color.
@@ -30,6 +44,26 @@ class SystemTheme {
3044
/// It returns [kDefaultFallbackColor] for unsupported platforms
3145
static final SystemAccentColor accentColor = SystemAccentColor(fallbackColor)
3246
..load();
47+
48+
/// A stream of [SystemAccentColor] that notifies when the system accent color
49+
/// changes.
50+
///
51+
/// Currently only available on Windows.
52+
///
53+
/// Basica usage:
54+
///
55+
/// ```dart
56+
/// SystemTheme.onChange.listen((color) {
57+
/// debugPrint('Accent color changed to ${color.accent}');
58+
/// });
59+
/// ```
60+
static Stream<SystemAccentColor> get onChange {
61+
if (kIsWeb || !Platform.isWindows) return Stream.value(accentColor);
62+
63+
return _eventChannel.receiveBroadcastStream().map((event) {
64+
return SystemAccentColor._fromMap(event);
65+
}).distinct();
66+
}
3367
}
3468

3569
/// Defines accent colors & its variants.
@@ -39,6 +73,9 @@ class SystemTheme {
3973
/// It returns [SystemAccentColor.defaultAccentColor] if
4074
/// [SystemAccentColor.load] fails
4175
class SystemAccentColor {
76+
StreamSubscription<SystemAccentColor>? _subscription;
77+
78+
/// The accent color used when the others are not available.
4279
final Color defaultAccentColor;
4380

4481
/// Base accent color.
@@ -72,10 +109,31 @@ class SystemAccentColor {
72109
darkest = defaultAccentColor;
73110
}
74111

112+
SystemAccentColor._fromMap(dynamic colors)
113+
: defaultAccentColor = SystemTheme.fallbackColor {
114+
accent = _retrieve(colors['accent']) ?? defaultAccentColor;
115+
light = _retrieve(colors['light']) ?? accent;
116+
lighter = _retrieve(colors['lighter']) ?? accent;
117+
lightest = _retrieve(colors['lightest']) ?? accent;
118+
dark = _retrieve(colors['dark']) ?? accent;
119+
darker = _retrieve(colors['darker']) ?? accent;
120+
darkest = _retrieve(colors['darkest']) ?? accent;
121+
}
122+
75123
/// Updates the fetched accent colors on Windows.
76124
Future<void> load() async {
77125
WidgetsFlutterBinding.ensureInitialized();
78126

127+
_subscription = SystemTheme.onChange.listen((color) {
128+
accent = color.accent;
129+
light = color.light;
130+
lighter = color.lighter;
131+
lightest = color.lightest;
132+
dark = color.dark;
133+
darker = color.darker;
134+
darkest = color.darkest;
135+
});
136+
79137
try {
80138
final colors = await _channel.invokeMethod(kGetSystemAccentColorMethod);
81139
if (colors == null) return;
@@ -101,6 +159,11 @@ class SystemAccentColor {
101159
return Color.fromRGBO(map['R'], map['G'], map['B'], 1.0);
102160
}
103161

162+
/// Releases any used resources
163+
void dispose() {
164+
_subscription?.cancel();
165+
}
166+
104167
@override
105168
bool operator ==(Object other) {
106169
if (identical(this, other)) return true;

lib/system_theme_builder.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import 'package:flutter/widgets.dart';
2+
import 'package:system_theme/system_theme.dart';
3+
4+
typedef ThemeWidgetBuilder = Widget Function(
5+
BuildContext context,
6+
SystemAccentColor accent,
7+
);
8+
9+
/// A widget that rebuilds when the system theme changes.
10+
///
11+
/// ```dart
12+
/// SystemThemeBuilder(builder: (context, accent) {
13+
/// return ColoredBox(color: accent.accent);
14+
/// });
15+
/// ```
16+
///
17+
/// See also:
18+
///
19+
/// * [SystemTheme.onChange], the stream used to listen to system theme changes.
20+
class SystemThemeBuilder extends StatelessWidget {
21+
/// The widget builder.
22+
final ThemeWidgetBuilder builder;
23+
24+
/// Creates a system theme builder.
25+
const SystemThemeBuilder({Key? key, required this.builder}) : super(key: key);
26+
27+
@override
28+
Widget build(BuildContext context) {
29+
return StreamBuilder<SystemAccentColor>(
30+
stream: SystemTheme.onChange,
31+
builder: (context, snapshot) {
32+
return builder(context, snapshot.data ?? SystemTheme.accentColor);
33+
},
34+
);
35+
}
36+
}

system_theme.iml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,5 @@
2121
<orderEntry type="sourceFolder" forTests="false" />
2222
<orderEntry type="library" name="Dart SDK" level="project" />
2323
<orderEntry type="library" name="Flutter Plugins" level="project" />
24-
<orderEntry type="library" name="Dart Packages" level="project" />
2524
</component>
2625
</module>

system_theme_web/pubspec.lock

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ packages:
3737
dependency: transitive
3838
description:
3939
name: collection
40-
sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c"
40+
sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687
4141
url: "https://pub.dev"
4242
source: hosted
43-
version: "1.17.1"
43+
version: "1.17.2"
4444
fake_async:
4545
dependency: transitive
4646
description:
@@ -64,30 +64,22 @@ packages:
6464
description: flutter
6565
source: sdk
6666
version: "0.0.0"
67-
js:
68-
dependency: transitive
69-
description:
70-
name: js
71-
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
72-
url: "https://pub.dev"
73-
source: hosted
74-
version: "0.6.7"
7567
matcher:
7668
dependency: transitive
7769
description:
7870
name: matcher
79-
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
71+
sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e"
8072
url: "https://pub.dev"
8173
source: hosted
82-
version: "0.12.15"
74+
version: "0.12.16"
8375
material_color_utilities:
8476
dependency: transitive
8577
description:
8678
name: material_color_utilities
87-
sha256: "586678f20e112219ed0f73215f01bcdf1d769824ba2ebae45ad918a9bfde9bdb"
79+
sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41"
8880
url: "https://pub.dev"
8981
source: hosted
90-
version: "0.3.0"
82+
version: "0.5.0"
9183
meta:
9284
dependency: transitive
9385
description:
@@ -153,10 +145,10 @@ packages:
153145
dependency: transitive
154146
description:
155147
name: test_api
156-
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
148+
sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8"
157149
url: "https://pub.dev"
158150
source: hosted
159-
version: "0.5.1"
151+
version: "0.6.0"
160152
vector_math:
161153
dependency: transitive
162154
description:
@@ -165,6 +157,14 @@ packages:
165157
url: "https://pub.dev"
166158
source: hosted
167159
version: "2.1.4"
160+
web:
161+
dependency: transitive
162+
description:
163+
name: web
164+
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
165+
url: "https://pub.dev"
166+
source: hosted
167+
version: "0.1.4-beta"
168168
sdks:
169-
dart: ">=3.0.0-0 <4.0.0"
169+
dart: ">=3.1.0-185.0.dev <4.0.0"
170170
flutter: ">=1.20.0"

0 commit comments

Comments
 (0)