Skip to content

Commit

Permalink
💄 migrate DebugFloatingThemeButton to Material 3
Browse files Browse the repository at this point in the history
  • Loading branch information
Pavel-Sulimau committed Jan 27, 2024
1 parent 391e4e8 commit af6268c
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 78 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# 3.6.0
- Migrate `DebugFloatingThemeButton` to Material 3.

# 3.5.0

- Add support for dynamically changing `debugShowFloatingThemeButton` state using `AdaptiveTheme.of(context).setDebugShowFloatingThemeButton(bool)` method.
Expand Down
64 changes: 34 additions & 30 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class MyApp extends StatelessWidget {
darkTheme: darkTheme,
home: const MyHomePage(),
),
debugShowFloatingThemeButton: true,
);
}
}
Expand All @@ -50,36 +51,39 @@ class MyHomePage extends StatelessWidget {
title: const Text('Adaptive Theme Demo'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'This is a sample app to demonstrate the usage of adaptive theme.',
),
const Text(
'You can switch between light and dark theme using the switch below.',
),
const SizedBox(height: 20),
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Light'),
const SizedBox(width: 10),
Switch(
value: AdaptiveTheme.of(context).mode.isDark,
onChanged: (value) {
if (value) {
AdaptiveTheme.of(context).setDark();
} else {
AdaptiveTheme.of(context).setLight();
}
},
),
const SizedBox(width: 10),
const Text('Dark'),
],
),
],
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'This is a sample app to demonstrate the usage of adaptive theme.',
),
const Text(
'You can switch between light and dark theme using the switch below.',
),
const SizedBox(height: 20),
Row(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Light'),
const SizedBox(width: 10),
Switch(
value: AdaptiveTheme.of(context).mode.isDark,
onChanged: (value) {
if (value) {
AdaptiveTheme.of(context).setDark();
} else {
AdaptiveTheme.of(context).setLight();
}
},
),
const SizedBox(width: 10),
const Text('Dark'),
],
),
],
),
),
),
);
Expand Down
102 changes: 64 additions & 38 deletions lib/src/debug_floating_theme_buttons.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,6 @@ class _DebugFloatingThemeButtonState extends State<DebugFloatingThemeButton> {
}
}

void onTap() {
animate = true;
final width = MediaQuery.of(context).size.width;
final left = !hidden ? width - kHandleWidth : width - 180;
hidden = !hidden;
setState(() => position = Offset(left, position.dy));
}

@override
Widget build(BuildContext context) {
// don't show in release mode
Expand Down Expand Up @@ -170,7 +162,7 @@ class _DebugFloatingThemeButtonState extends State<DebugFloatingThemeButton> {
initialLocalPosition = Offset.zero;
initialPosition = Offset.zero;
},
onTap: onTap,
onTap: _handleTap,
child: SizedBox(
width: kHandleWidth,
height: double.infinity,
Expand All @@ -184,35 +176,11 @@ class _DebugFloatingThemeButtonState extends State<DebugFloatingThemeButton> {
),
),
),
ToggleButtons(
borderRadius: BorderRadius.circular(6),
constraints: const BoxConstraints.tightFor(
width: 40, height: 40),
onPressed: (index) {
final mode = AdaptiveThemeMode.values[index];
widget.manager.setThemeMode(mode);
},
isSelected: [
widget.manager.mode == AdaptiveThemeMode.light,
widget.manager.mode == AdaptiveThemeMode.dark,
widget.manager.mode == AdaptiveThemeMode.system,
],
children: [
const Center(
child: Icon(Icons.sunny, size: 18),
),
Center(
child: Transform.rotate(
angle: pi * -30 / 180,
child:
const Icon(Icons.nightlight, size: 18),
),
),
const Center(
child: Icon(Icons.brightness_auto_outlined,
size: 18),
)
],
_ThemeModeSelector(
selectedThemeMode: widget.manager.mode,
onThemeModeChanged: (newThemeMode) => setState(
() => widget.manager.setThemeMode(newThemeMode),
),
),
],
),
Expand All @@ -227,4 +195,62 @@ class _DebugFloatingThemeButtonState extends State<DebugFloatingThemeButton> {
),
);
}

void _handleTap() {
animate = true;
final width = MediaQuery.of(context).size.width;
final left = !hidden ? width - kHandleWidth : width - 180;
hidden = !hidden;
setState(() => position = Offset(left, position.dy));
}
}

typedef _ThemeModeChangedCallback = void Function(
AdaptiveThemeMode newThemeModeSet);

class _ThemeModeSelector extends StatelessWidget {
const _ThemeModeSelector({
required this.selectedThemeMode,
required this.onThemeModeChanged,
});

final AdaptiveThemeMode selectedThemeMode;
final _ThemeModeChangedCallback onThemeModeChanged;

@override
Widget build(BuildContext context) {
return SegmentedButton<AdaptiveThemeMode>(
style: const ButtonStyle(visualDensity: VisualDensity.compact),
showSelectedIcon: false,
segments: const <ButtonSegment<AdaptiveThemeMode>>[
ButtonSegment<AdaptiveThemeMode>(
value: AdaptiveThemeMode.light,
icon: Icon(Icons.sunny),
),
ButtonSegment<AdaptiveThemeMode>(
value: AdaptiveThemeMode.dark,
icon: _RotatedNightlightIcon(),
),
ButtonSegment<AdaptiveThemeMode>(
value: AdaptiveThemeMode.system,
icon: Icon(Icons.brightness_auto_outlined),
),
],
selected: {selectedThemeMode},
onSelectionChanged: (Set<AdaptiveThemeMode> newThemeModeSet) =>
onThemeModeChanged(newThemeModeSet.first),
);
}
}

class _RotatedNightlightIcon extends StatelessWidget {
const _RotatedNightlightIcon();

@override
Widget build(BuildContext context) {
return Transform.rotate(
angle: pi * -30 / 180,
child: const Icon(Icons.nightlight, size: 18),
);
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: adaptive_theme
description: Allows to change between light and dark theme dynamically and add system adaptive theme support.
version: 3.5.0
version: 3.6.0
homepage: https://github.com/birjuvachhani/adaptive_theme

screenshots:
Expand Down
25 changes: 16 additions & 9 deletions test/debug_floating_theme_buttons_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -215,29 +215,36 @@ void main() {
await tester.tapAt(rect.center);
await tester.pumpAndSettle();

final toggleButtonFinder =
find.descendant(of: widgetFinder, matching: find.byType(ToggleButtons));
ToggleButtons buttonsWidget() =>
tester.widget<ToggleButtons>(toggleButtonFinder);
final segmentedButtonFinder = find.descendant(
of: widgetFinder,
matching: find.byType(SegmentedButton<AdaptiveThemeMode>));
SegmentedButton<AdaptiveThemeMode> buttonsWidget() => tester
.widget<SegmentedButton<AdaptiveThemeMode>>(segmentedButtonFinder);

// check if light theme is selected
expect(
buttonsWidget().isSelected, containsAllInOrder([true, false, false]));
buttonsWidget().selected,
containsAllInOrder({AdaptiveThemeMode.light}),
);

// select dark theme
buttonsWidget().onPressed?.call(1);
buttonsWidget().onSelectionChanged?.call({AdaptiveThemeMode.dark});
await tester.pumpAndSettle();

expect(manager.mode, AdaptiveThemeMode.dark);
expect(
buttonsWidget().isSelected, containsAllInOrder([false, true, false]));
buttonsWidget().selected,
containsAllInOrder({AdaptiveThemeMode.dark}),
);

// select system theme
buttonsWidget().onPressed?.call(2);
buttonsWidget().onSelectionChanged?.call({AdaptiveThemeMode.system});
await tester.pumpAndSettle();

expect(manager.mode, AdaptiveThemeMode.system);
expect(
buttonsWidget().isSelected, containsAllInOrder([false, false, true]));
buttonsWidget().selected,
containsAllInOrder({AdaptiveThemeMode.system}),
);
});
}

0 comments on commit af6268c

Please sign in to comment.