---
title: Toggle theme example
description: Toggle theme example using flutter_solidart
---

# Toggle theme example

A simple __toggle-theme__ feature built using `flutter_solidart`

See the code [here](https://github.com/nank1ro/solidart/tree/main/examples/toggle_theme).

This simple example uses a powerful feature of `flutter_solidart`, the `Solid` widget.
The `Solid` widget is used to provide signals to descendants without passing them as parameters.

<Warning>You are discouraged in passing signals as parameters, even inside modals you may use Solid.value</Warning>

First of all let's use the default `light` and `dark` themes by adding them to our `MaterialApp`:
```dart
MaterialApp(
  theme: ThemeData.light(),
  darkTheme: ThemeData.dark(),
)
```

`themeMode` is an `Enum` used by the `MaterialApp` to choose which theme to use: `[light, dark, system]`.

To update the theme mode we've to rebuild our `MaterialApp`.

Let's wrap the MaterialApp with a `Solid` widget:
```dart
Solid(
  child: MaterialApp(
    theme: ThemeData.light(),
    darkTheme: ThemeData.dark(),
  ),
)
```

`Solid` takes a `providers` list, let's create our `themeMode` signal:
```dart
Solid(
  providers: [
    Provider<Signal<ThemeMode>>(
      create: () => Signal(ThemeMode.light),
    ),
  ],
  child: MaterialApp(
    theme: ThemeData.light(),
    darkTheme: ThemeData.dark(),
  ),
)
```

<Warning>Don't forget to define type of `Provider` otherwise you may encounter unexpected errors</Warning>

Now we've provided the `themeMode` signal to descendants, but we've to observe the `themeMode` signal in order to rebuilt the `MaterialApp`.
Since we need a `BuildContext` that is a descendant of `Solid` we wrap the `MaterialApp` inside a `Builder`.

```dart
Builder(
  builder: (context) {
    return MaterialApp(
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
    );
  }
)
```

Now using the `context.observe` method we effectively listen to the `themeMode` signal:
```dart
Builder(
  builder: (context) {
    final themeMode = context.observe<Signal<ThemeMode>>().value;

    return MaterialApp(
      theme: ThemeData.light(),
      darkTheme: ThemeData.dark(),
      themeMode: themeMode,
    );
  }
)
```

Provide the exact type of the signal value also to the `observe()` method. 

This little line of code will subscribe the `context` we've used to the `signal` and will rebuild every time the `themeMode` signal changes.
Finally pass the `themeMode` value to the `MaterialApp`.

In this example we've a page called `MyHomePage` that displays a simple `IconButton` that toggles the theme mode.

The page retrieves the `themeMode` signal using the `context.get()` method:
```dart
 @override
  Widget build(BuildContext context) {
    final themeMode = context.get<Signal<ThemeMode>>();
    ...
}
```

<Info>Note that the type passed to the get method contains also the signal type, it can be `Signal` or `ReadSignal` if you need to access a __read-only__ signal</Info>

The `get` method obtains the signal for the given identifier (optional) without listening to it. You may use this method inside the `initState()`, `build()` methods and inside callbacks like `onTap` or `onPressed`. 

To react to the signal we've used a `SignalBuilder`:
```dart
SignalBuilder(
  builder: (_, __) {
    return IconButton(
      icon: Icon(
        themeMode.value == ThemeMode.dark ? Icons.dark_mode : Icons.light_mode,
      ),
    );
  },
)
```

and to update our `themeMode` signal we just update the signal value inside the `onPressed` callback:
```dart
SignalBuilder(
  builder: (_, __) {
    final mode = themeMode.value;
    return IconButton(
      onPressed: () {
        // toggle the theme mode
        if (mode == ThemeMode.light) {
          themeMode.value = ThemeMode.dark;
        } else {
          themeMode.value = ThemeMode.light;
        }
      },
      icon: Icon(
        mode == ThemeMode.dark ? Icons.dark_mode : Icons.light_mode,
      ),
    );
  },
)
```

## Testing

Writing a test that toggles the dark mode on and off.

```dart
testWidgets(
    'Check that when the app is in light mode the icon button shows a moon, while in dark mode it shows a sun',
    (WidgetTester tester) async {
  // Build our app and trigger a frame.
  await tester.pumpWidget(const MyApp());

  // Icon finders
  Finder lightModeIcon() => find.byIcon(Icons.light_mode);
  Finder darkModeIcon() => find.byIcon(Icons.dark_mode);

  // Given that our theme starts at light mode
  // Verify that the toggle theme icon button shows the dark mode icon
  expect(darkModeIcon(), findsOneWidget);
  expect(lightModeIcon(), findsNothing);

  // Tap the icon button to toggle the theme mode and trigger a frame.
  await tester.tap(darkModeIcon());
  await tester.pump();

  // Verify that our theme has changed to 'dark' mode and the `light_mode` icon should be shown
  expect(lightModeIcon(), findsOneWidget);
  expect(darkModeIcon(), findsNothing);

  // Tap the icon button to toggle the theme mode and trigger a frame.
  await tester.tap(lightModeIcon());
  await tester.pump();

  // Verify that our theme has changed to 'light' mode and the `dark_mode` icon should be shown
  expect(darkModeIcon(), findsOneWidget);
  expect(lightModeIcon(), findsNothing);
});
```
