diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index f9dc4c2fa4..ee9469167e 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -24,6 +24,7 @@ - Fixed `StreamMessageComposer` polling the audio recorder ten times a second while no recording is in progress, including when voice recording is disabled. - Fixed `StreamMessageComposer`'s send button looking enabled on an empty message when voice recording is off, and showing the microphone instead of send for a staged poll. - Fixed the attachment picker showing an empty commands tab in channels without commands. +- Fixed dialogs and bottom sheets opened below `StreamChat` keeping the old colors when the app switched between light and dark mode. ## 10.5.0 diff --git a/packages/stream_chat_flutter/lib/src/stream_chat.dart b/packages/stream_chat_flutter/lib/src/stream_chat.dart index 75162c2155..2a2584a3cb 100644 --- a/packages/stream_chat_flutter/lib/src/stream_chat.dart +++ b/packages/stream_chat_flutter/lib/src/stream_chat.dart @@ -217,8 +217,18 @@ class StreamChatState extends State { final theme = widget.themeData ?? StreamChatThemeData(); child = StreamChatTheme(data: theme, child: child); - final streamTheme = StreamTheme.of(context); - child = Theme(data: Theme.of(context).withExtension(streamTheme), child: child); + // Only when the app has not registered a StreamTheme itself: dialogs and + // sheets opened below this point copy the Theme once and would stop + // following the app's brightness (flutter/flutter#106642). + // + // The key keeps the subtree's state when the Theme comes or goes, such as + // when only one of the app's light and dark themes registers a StreamTheme. + child = KeyedSubtree(key: GlobalObjectKey(this), child: child); + final materialTheme = Theme.of(context); + if (materialTheme.extension() == null) { + final streamTheme = StreamTheme.of(context); + child = Theme(data: materialTheme.withExtension(streamTheme), child: child); + } if (widget.componentBuilders case final builders?) { child = StreamComponentFactory(builders: builders, child: child); diff --git a/packages/stream_chat_flutter/test/src/stream_chat_test.dart b/packages/stream_chat_flutter/test/src/stream_chat_test.dart index 9e8db8ae5e..6e998c1bd9 100644 --- a/packages/stream_chat_flutter/test/src/stream_chat_test.dart +++ b/packages/stream_chat_flutter/test/src/stream_chat_test.dart @@ -218,6 +218,79 @@ void main() { expect(captured, same(sibling)); }, ); + + testWidgets( + 'a dialog opened below StreamChat follows the app theme when the ' + 'brightness changes', + (tester) async { + tester.platformDispatcher.platformBrightnessTestValue = Brightness.light; + addTearDown(tester.platformDispatcher.clearPlatformBrightnessTestValue); + + final mockClient = MockClient(); + final light = StreamTheme.light(); + final dark = StreamTheme.dark(); + StreamTheme? captured; + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(extensions: [light]), + darkTheme: ThemeData(brightness: Brightness.dark, extensions: [dark]), + home: StreamChat( + client: mockClient, + child: Builder( + builder: (context) => TextButton( + onPressed: () => showDialog( + context: context, + builder: (context) { + captured = Theme.of(context).extension(); + return const Text('Dialog'); + }, + ), + child: const Text('Open'), + ), + ), + ), + ), + ); + + await tester.tap(find.text('Open')); + await tester.pumpAndSettle(); + expect(captured, same(light)); + + tester.platformDispatcher.platformBrightnessTestValue = Brightness.dark; + await tester.pumpAndSettle(); + expect(captured, same(dark)); + }, + ); + + testWidgets( + 'keeps the state below StreamChat when only one of the app themes ' + 'registers a StreamTheme', + (tester) async { + tester.platformDispatcher.platformBrightnessTestValue = Brightness.light; + addTearDown(tester.platformDispatcher.clearPlatformBrightnessTestValue); + + final mockClient = MockClient(); + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(extensions: [StreamTheme.light()]), + darkTheme: ThemeData(brightness: Brightness.dark), + home: StreamChat( + client: mockClient, + child: const Material(child: TextField()), + ), + ), + ); + + await tester.enterText(find.byType(TextField), 'draft'); + + tester.platformDispatcher.platformBrightnessTestValue = Brightness.dark; + await tester.pumpAndSettle(); + + expect(find.text('draft'), findsOneWidget); + }, + ); }); }