Build beautiful settings screens by using the settings_ui package

Flutter Aug 13, 2023

Most mobile applications require a settings screen. In this post, we are going to have a look at the settings_ui package which simplifies this process. This package makes it much easier to create good-looking settings screens for your Flutter applications. Whether you want to let users pick a language, manage their profile, or turn on dark mode, the settings_ui package can help you do all that without a lot of effort.

Installing

To get started with implementing, we need to install the settings_ui package into our project. The installation process is simple. Just execute the following command: flutter pub add settings_ui.

Once the command is executed, make sure to check your pubspec.yaml file for the added dependencies. You should see the settings_ui package included in the dependencies section, like this:

dependencies:
  flutter:
    sdk: flutter
  settings_ui: ^2.0.2

Creating a Settings screen

Designing a settings screen using the settings_ui package is quite easy. The package has a SettingsList widget. This widget can hold different sections of settings, and each section can contain various types of settings, see the code down below:

import 'package:flutter/material.dart';
import 'package:settings_ui/settings_ui.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SettingsScreen(),
    );
  }
}

class SettingsScreen extends StatefulWidget {
  const SettingsScreen({super.key});

  @override
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Settings', textAlign: TextAlign.center),
      ),
      body: SettingsList(
        sections: [
          SettingsSection(
            title: Text('General', style: TextStyle(color: Colors.black)),
            tiles: <SettingsTile>[
              SettingsTile(
                leading: Icon(Icons.language),
                title: Text('Language'),
              ),
              SettingsTile.navigation(
                leading: Icon(Icons.person),
                title: Text('Profile'),
              ),
              SettingsTile.switchTile(
                leading: Icon(Icons.dark_mode),
                title: Text('Dark mode'),
                initialValue: false,
                onToggle: (bool value) {},
              ),
            ],
          ),
        ],
      ),
    );
  }
}

In this code snippet, we created two widgets: MyApp and the SettingsScreen. The MyApp widget sets up our application and the SettingsScreen widget is where we create our settings page.

Inside the SettingsScreen widget, we have a Scaffold widget to create our page layout. On the body property of the Scaffold widget, we have a SettingsList. Inside the SettingsList we created one section by providing the SettingsSection inside the sections property. Inside this section, we added one of each available tiles: SettingsTile, SettingsTile.navigation and SettingsTile.switchTile with their required properties.

Within the SettingsScreen widget, a Scaffold widget helps structure the page layout. The body of the Scaffold contains a SettingsList widget. Within this SettingsList widget, we created a section by providing a SettingsSection widget into the sections property. Inside this section, we added one of each available settings tile: SettingsTile, SettingsTile.navigation and SettingsTile.switchTile with their required properties.

When we build the application we will end up with the following settings screen:

flutter_settings_screen_containing_three_settings

Changing style

To change the style of our settings screen we can set the platform property of the SettingsList widget:

SettingsList(
  platform: DevicePlatform.iOS,
  sections: [
    SettingsSection( ... ),
  ],
),

In this code snippet, we set the platfrom property to DevicePlatform.iOS, to change the overall style of our SettingsList:

flutter_settings_screen_with_ios_styling
As of now the settings_ui package only has different styling for iOS. The other platform all have the same style.

Adding Functionality to the settings

Now that we have built the layout of our settings screen, we can move on to add the functionality. The first code snippet below is the complete implementation of the functionality, however, underneath this code snippet, we will go over every setting implementation individually:

import 'package:flutter/material.dart';
import 'package:settings_ui/settings_ui.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: SettingsScreen(),
    );
  }
}

class SettingsScreen extends StatefulWidget {
  const SettingsScreen({super.key});

  @override
  State<SettingsScreen> createState() => _SettingsScreenState();
}

class _SettingsScreenState extends State<SettingsScreen> {
  bool _darkMode = false;
  String _language = 'English';
  List<String> _languages = <String>['English', 'German', 'Spanish'];
  final GlobalKey _dropdownButtonKey = GlobalKey();

  void _openDropdown() {
    _dropdownButtonKey.currentContext?.visitChildElements((element) {
      if (element.widget is Semantics) element.visitChildElements((element) {
          if (element.widget is Actions) element.visitChildElements(
              (element) => Actions.invoke(element, ActivateIntent())
            );
        });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Settings', textAlign: TextAlign.center),
      ),
      body: SettingsList(
        sections: [
          SettingsSection(
            title: Text('General', style: TextStyle(color: Colors.black)),
            tiles: <SettingsTile>[
              SettingsTile(
                leading: Icon(Icons.language),
                title: Text('Language'),
                onPressed: (_) => _openDropdown(),
                trailing: DropdownButton(
                  key: _dropdownButtonKey,
                  value: _language,
                  onChanged: (String? value) => setState(
                    () => _language = value!,
                  ),
                  items: _languages
                      .map<DropdownMenuItem<String>>(
                        (String value) => DropdownMenuItem(
                          value: value,
                          child: Text(value),
                        ),
                      )
                      .toList(),
                ),
              ),
              SettingsTile.navigation(
                leading: Icon(Icons.person),
                title: Text('Profile'),
                onPressed: (context) => Navigator.of(context).push(
                  MaterialPageRoute(
                    builder: (context) => const ProfileScreen(),
                  ),
                ),
              ),
              SettingsTile.switchTile(
                leading: Icon(Icons.dark_mode),
                title: Text('Dark mode'),
                onToggle: (bool value) => setState(() => _darkMode = value),
                initialValue: _darkMode,
              ),
            ],
          ),
        ],
      ),
    );
  }
}

class ProfileScreen extends StatelessWidget {
  const ProfileScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Profile'),
      ),
    );
  }
}

1. SettingsTile

The regular SettingsTile offers a lot of customization, in this example, we will be creating a language selector.

To start off we have to make some changes inside our _SettingsScreenState state:

class _SettingsScreenState extends State {
  String _language = 'English';
  List _languages = ['English', 'German', 'Spanish'];
  final GlobalKey _dropdownButtonKey = GlobalKey();

  void _openDropdown() {
    _dropdownButtonKey.currentContext?.visitChildElements((element) {
      if (element.widget is Semantics) element.visitChildElements((element) {
        if (element.widget is Actions) element.visitChildElements(
                (element) => Actions.invoke(element, ActivateIntent())
        );
      });
    });
  }

  @override
  Widget build(BuildContext context) { ... }
}

In this code snippet, we added three variables: _language, _languages and _dropdownButtonKey. The _language variable hold the currently selected language, the _languages variable holds a list of all the available languages and _dropdownButtonKey variable is needed for the _openDropdown function.

The _openDropdown function is used to open the DropdownMenuItem widget when tapping the SettingTile. The DropdownMenuItem widget will be used to make the languages selectable.

SettingsTile(
  leading: Icon(Icons.language),
  title: Text('Language'),
  onPressed: (_) => _openDropdown(),
  trailing: DropdownButton(
    key: _dropdownButtonKey,
    value: _language,
    onChanged: (String? value) => setState(
          () => _language = value!,
    ),
    items: _languages
        .map<DropdownMenuItem<String>>(
          (String value) => DropdownMenuItem(
        value: value,
        child: Text(value),
      ),
    )
        .toList(),
  ),
),

In this code snippet, we updated the SettingsTile widget with the onPressed and trailing properties. The onPressed property calls the openDropdown function when the setting is tapped. The trailing property takes a Widget and will be displayed at the end of the setting. In this case, we added the DropDownButton widget which holds a list of the available languages in its items property. When another language is selected we change the value of the _language variable calling setState inside the onChanged property.

flutter_settings_screen_showcasing_language_setting

2. SettingsTile.navigation

The SettingsTile.navigation can be used to navigate to different screens. In this case, we will navigate to a profile screen.

class ProfileScreen extends StatelessWidget {
  const ProfileScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Profile'),
      ),
    );
  }
}

In this code snippet, we create the ProfileScreen widget. This is a simple widget that serves as our profile screen. It returns a Scaffold widget with an AppBar widget that has the title "Profile".

SettingsTile.navigation(
  leading: Icon(Icons.person),
  title: Text('Profile'),
  onPressed: (context) => Navigator.of(context).push(
    MaterialPageRoute(
      builder: (context) => const ProfileScreen(),
    ),
  ),
),

In this code snippet, we updated the SettingsTile.navigation widget with the onPressed property. Now when we tap the profile setting we will navigate to the ProfileScreen using Navigator.of(context).push.

flutter_settings_screen_showcasing_profile_setting

3. SettingsTile.switch

To create a simple switch you can use the SettingsTile.switch widget. In this case, we used the widget to create a dark mode switch.

class _SettingsScreenState extends State<SettingsScreen> {
  bool _darkMode = false;

  @override
  Widget build(BuildContext context) { ... }
}

In this code snippet, we added a _darkMode variable with the default value of false to our _SettingsScreenState state.

SettingsTile.switchTile(
  leading: Icon(Icons.dark_mode),
  title: Text('Dark mode'),
  onToggle: (bool value) => setState(() => _darkMode = value),
  initialValue: _darkMode,
),

In this code snippet, we added the onToggle and initialValue properties. The onToggle property changes our _darkMode variable by using the setState function when the setting is tapped. The initialValue is set to the _darkMode variable which means that the initial value will be false.

flutter_settings_screen_showcasing_dark_mode_setting

Conclusion

In this post, we have explored the settings_ui package, a helpful tool for designing good-looking settings screens in Flutter applications. It is designed to be simple, so you can spend more time improving how the application works and how users enjoy it. It is a great choice for making mobile applications more interesting and user-friendly.

Tags