Customize SliderTheme Flutter: Happiness App – Step 2

We have been building a “Happiness Calculator App” in Flutter. However, we are building it step-by-step. In this section, we will customize the theme of Slider Widget. And, to do that we will use “SliderTheme” in Flutter.

Firstly, we have discussed enum before. Secondly, we have reduced the code size by using the ternary operator. And finally, we have discussed Slider in Flutter.

Actually, these are steps that define how we can build the App.

During building the App, the last stage was as follows.


Slider proceeds to the maximum value
Slider proceeds to the maximum value

However, we want to give it a professional look. And to do that, we must have a common theme that applies color and font across the entire app.

As a result, it will look as follows.


Customized Slider Theme that runs across the entire Flutter App
Customized Slider Theme that runs across the entire Flutter App

Have you not read the previous sections where we have discussed how to customize color and fonts across the App? In that case, please read how to customize color, and how to customize font and design.

Firstly, when we try to calculate Happiness, we need some inputs. Say, it is greed. A greedy person suffers from eternal unhappiness.

Therefore, as we raise the Slider bar that means we raise the amount of Greed.

Secondly, the amount of happiness differs from male to female. So we have used two icons that represent gender.

As a whole, up to now, we have designed this part. And in this section, we will learn how we have achieved that.

How to use the custom theme

Although we have discussed this part before, still recapitulation will not harm.

To customize a theme that will work across the entire Flutter App, we need a custom theme class.

Let us see how this custom theme class look like.

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

/// In a custom theme page we have described color and fonts
/// We may add more custom theme-features later
///

class HappyTheme {
  static const Color primaryColor = Color(0xFF409B25);
  static const Color scaffoldBackgroundColor = Color(0xFF2C6F2E);
  static const Color appBarBackgroundColor = Color(0xFF2C6F2E);
  static const Color boxDecorationColor = Color(0xFFC5DA28);
  static const Color elevatedButtonPrimaryColor = Color(0xFF3C9415);
  static const Color dividerColor = Color(0xFFD9DB26);
  static const correctAnswerColor = Color(0xFFFACAFA);
  static const questionTextColor = Color(0xFFF8E1F8);
  static const answerColor = Color(0xFFFFFFFF);

  static TextStyle answerStyle = GoogleFonts.langar(
    textStyle: const TextStyle(
      color: HappyTheme.answerColor,
      fontSize: 20.0,
      fontWeight: FontWeight.bold,
    ),
  );

  static TextStyle greedStyle = GoogleFonts.laila(
    textStyle: const TextStyle(
      color: HappyTheme.shrinePink100,
      fontSize: 60.0,
      fontWeight: FontWeight.bold,
    ),
  );

  static TextStyle genderStyle = GoogleFonts.antic(
    textStyle: const TextStyle(
      color: HappyTheme.shrineSurfaceWhite,
      fontSize: 15.0,
      fontWeight: FontWeight.bold,
    ),
  );

  static TextStyle questionStyle = GoogleFonts.laila(
    textStyle: const TextStyle(
      color: HappyTheme.shrineBrown600,
      fontSize: 30.0,
      fontWeight: FontWeight.bold,
    ),
  );

  static TextStyle appbarStyle = GoogleFonts.salsa(
    textStyle: const TextStyle(
      color: HappyTheme.shrineBrown600,
      fontSize: 20.0,
      fontWeight: FontWeight.bold,
    ),
  );

  ThemeData _buildShrineTheme() {
    final ThemeData base = ThemeData.light();
    return base.copyWith(
      colorScheme: _shrineColorScheme,
      toggleableActiveColor: shrinePink400,
      primaryColor: shrinePink100,
      primaryColorLight: shrinePink100,
      scaffoldBackgroundColor: shrineBackgroundWhite,
      cardColor: shrineBackgroundWhite,
      textSelectionTheme:
          const TextSelectionThemeData(selectionColor: shrinePink100),
      errorColor: shrineErrorRed,
      buttonTheme: ButtonThemeData(
        colorScheme: _shrineColorScheme.copyWith(primary: shrinePink400),
        textTheme: ButtonTextTheme.normal,
      ),
      primaryIconTheme: _customIconTheme(base.iconTheme),
      textTheme: _buildShrineTextTheme(base.textTheme),
      primaryTextTheme: _buildShrineTextTheme(base.primaryTextTheme),
      iconTheme: _customIconTheme(base.iconTheme),
    );
  }

  ThemeData buildTheme() {
    return _buildShrineTheme();
  }

  IconThemeData _customIconTheme(IconThemeData original) {
    return original.copyWith(color: shrineBrown900);
  }

  TextTheme _buildShrineTextTheme(TextTheme base) {
    return base
        .copyWith(
          caption: base.caption!.copyWith(
            fontWeight: FontWeight.w400,
            fontSize: 14,
            letterSpacing: defaultLetterSpacing,
          ),
          button: base.button!.copyWith(
            fontWeight: FontWeight.w500,
            fontSize: 14,
            letterSpacing: defaultLetterSpacing,
          ),
        )
        .apply(
          fontFamily: 'Rubik',
          displayColor: shrineBrown900,
          bodyColor: shrineBrown900,
        );
  }

  static const ColorScheme _shrineColorScheme = ColorScheme(
    primary: shrinePink100,
    secondary: shrinePink50,
    surface: shrineSurfaceWhite,
    background: shrineBackgroundWhite,
    error: shrineErrorRed,
    onPrimary: shrineBrown900,
    onSecondary: shrineBrown900,
    onSurface: shrineBrown900,
    onBackground: shrineBrown900,
    onError: shrineSurfaceWhite,
    brightness: Brightness.light,
  );

  static const Color activeCoor = Color(0xFFaa1111);
  static const Color inactiveCoor = Color(0xFF893131);

  static const Color shrinePink50 = Color(0xFFFEEAE6);
  static const Color shrinePink100 = Color(0xFFFEDBD0);
  static const Color shrinePink300 = Color(0xFFFBB8AC);
  static const Color shrinePink400 = Color(0xFFEAA4A4);

  static const Color shrineBrown900 = Color(0xFF4f0808);
  static const Color shrineBrown600 = Color(0xFF893131);

  static const Color shrineErrorRed = Color(0xFFC5032B);

  static const Color shrineSurfaceWhite = Color(0xFFFFFBFA);
  static const Color shrineBackgroundWhite = Colors.white;

  static const defaultLetterSpacing = 0.03;
}

We can give this file any name, however, the name should be meaningful.

Next, we will use this custom theme object where we need to reflect this theme design.

It could be any color or style property of the Scaffold, AppBar, or the Text Widget.

Consequently, we can take a look at the full code.

import 'package:flutter/material.dart';

import '../model/happ_theme.dart';
import '../model/constants.dart';
import '../model/container_color.dart';

class HappinessHomePage extends StatefulWidget {
  const HappinessHomePage({
    Key? key,
    required this.title,
  }) : super(key: key);

  final String title;

  @override
  State<HappinessHomePage> createState() => _HappinessHomePageState();
}

class _HappinessHomePageState extends State<HappinessHomePage> {
  ContainerColor? selectedContainer;
  int height = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: HappyTheme.shrineBrown900,
      appBar: AppBar(
        title: Text(
          widget.title,
          style: HappyTheme.answerStyle,
        ),
        backgroundColor: HappyTheme.shrineBrown600,
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Row(
              children: [
                expandEnum(
                  ContainerColor.first,
                  'Male',
                  Icons.male,
                ),
                expandEnum(
                  ContainerColor.second,
                  'Female',
                  Icons.female,
                ),
              ],
            ),
            Expanded(
              child: Container(
                margin: const EdgeInsets.all(15.0),
                alignment: Alignment.center,
                color: HappyTheme.shrineBrown600,
                width: double.infinity,
                child: Column(
                  children: [
                    Container(
                      padding: const EdgeInsets.only(
                        top: 5.0,
                      ),
                      child: Text(
                        'GREED',
                        style: HappyTheme.answerStyle,
                      ),
                    ),
                    Row(
                      mainAxisAlignment: MainAxisAlignment.center,
                      crossAxisAlignment: CrossAxisAlignment.baseline,
                      textBaseline: TextBaseline.alphabetic,
                      children: [
                        Text(
                          height.toString(),
                          style: HappyTheme.greedStyle,
                        ),
                      ],
                    ),
                    SliderTheme(
                      data: SliderTheme.of(context).copyWith(
                        inactiveTrackColor: HappyTheme.shrinePink50,
                        activeTrackColor: HappyTheme.shrineBackgroundWhite,
                        thumbColor: HappyTheme.shrineErrorRed,
                        overlayColor: HappyTheme.shrinePink50,
                        thumbShape: const RoundSliderThumbShape(
                          enabledThumbRadius: 15.0,
                        ),
                        overlayShape: const RoundSliderOverlayShape(
                          overlayRadius: 35.0,
                        ),
                      ),
                      child: Slider(
                        value: height.toDouble(),
                        min: 0.0,
                        max: 100.0,
                        activeColor: activeColor,
                        inactiveColor: Colors.black26,
                        onChanged: (double newValue) {
                          setState(() {
                            height = newValue.round();
                          });
                        },
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  Expanded expandEnum(
      ContainerColor? containerColor, String gender, IconData genderIcon) {
    return Expanded(
      child: Padding(
        padding: const EdgeInsets.all(18.0),
        child: GestureDetector(
          onTap: () {
            setState(() {
              selectedContainer = containerColor;
            });
          },
          child: Container(
            alignment: Alignment.center,
            color: selectedContainer == containerColor
                ? HappyTheme.activeCoor
                : HappyTheme.inactiveCoor,
            width: 150.0,
            height: 100.0,
            child: Column(
              children: [
                Icon(
                  genderIcon,
                  size: 80.0,
                ),
                Text(
                  gender,
                  style: HappyTheme.genderStyle,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

However, in the immediate parent Widget, we have defined the MaterialApp theme property also.

Where we have created the custom theme data object.

import 'package:flutter/material.dart';
import 'package:happiness_calculator/model/happ_theme.dart';

import 'happiness_home_page.dart';

HappyTheme happyTheme = HappyTheme();

class HappinessApp extends StatelessWidget {
  const HappinessApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: happyTheme.buildTheme(),
      home: const HappinessHomePage(title: 'Flutter Happiness Calculator'),
    );
  }
}

If you want to take a closer look at how code structure works, you can clone the whole project from this GitHub repository branch.

Anyway, since we have discussed the usage of custom theme class, we are not going to chew over the same topic.

Instead, we can examine how the Slider Theme Widget works.

How SliderTheme works

The SliderTheme Widget acts as a parent class to the Slider Widget. It applies a slider theme to the descendant Slider widgets.

What is a slider theme?

It describes many properties that manage the theme, such as color, and shape. However, we need to use the SliderTheme.of method that passes the context.

And, as a result, we can access the copyWith() method. Consequently the copyWith() method has many properties that expect the slider components.

Let us take a look at the code snippet.

SliderTheme(
                      data: SliderTheme.of(context).copyWith(
                        inactiveTrackColor: HappyTheme.shrinePink50,
                        activeTrackColor: HappyTheme.shrineBackgroundWhite,
                        thumbColor: HappyTheme.shrineErrorRed,
                        overlayColor: HappyTheme.shrinePink50,
                        thumbShape: const RoundSliderThumbShape(
                          enabledThumbRadius: 15.0,
                        ),
                        overlayShape: const RoundSliderOverlayShape(
                          overlayRadius: 35.0,
                        ),
                      ),
                      child: Slider(
                        value: height.toDouble(),
                        min: 0.0,
                        max: 100.0,
                        activeColor: activeColor,
                        inactiveColor: Colors.black26,
                        onChanged: (double newValue) {
                          setState(() {
                            height = newValue.round();
                          });
                        },
                      ),
                    ),

As you see, we have defined various properties that describe the theme properties in detail.

In fact, now it becomes easy to customize the Slider Widget.

In the next section we will proceed forward to complete the App.

So stay tuned.

What Next?

Books at Leanpub

Books in Apress

My books at Amazon

GitHub repository

Technical blog

Twitter

Comments

2 responses to “Customize SliderTheme Flutter: Happiness App – Step 2”

  1. […] for simple App like the Happiness Calculator App that we have been building step by step, the Navigator API is […]

  2. […] Firstly, we have learned how to use enum. Secondly, we have learned ternary operator. Thirdly, we have grasped how to customize the Slider theme. […]

Leave a Reply