Custom Theme using Theme Extensions

Jamie
ITNEXT
Published in
2 min readDec 16, 2022

--

Theme Extensions in Flutter

Video Tutorial

Theme Extensions in Flutter

Theme extensions were introduced in Flutter 3.

But what are Theme Extensions?

As the name says, it helps to extend the inbuilt themes with our own extensions.

Let’s jump into an example

So when you create a flutter app, your basic root widget will look like this

 return MaterialApp(
title: 'Flutter Theme Extensions',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(),
);

Here if you want to change the theme, you can basically customize the ThemeData, but you cannot add your own properties to it, like below.

return MaterialApp(
title: 'Flutter Theme Extensions',
theme: ThemeData(
primarySwatch: Colors.blue,
'myColor': '#FFAABBCC' // won't work
),
home: const HomePage(),
);

But then how do I do it if I want it?

Here comes the extensions.

return MaterialApp(
title: 'Flutter Theme Extensions',
theme: ThemeData(
primarySwatch: Colors.blue,
extensions: <ThemeExtension<dynamic>>[
// your colors and styles
],
),
home: const HomePage(),
);

Let’s see how we can create one. Create a new class named ‘MyColors’.

import 'package:flutter/material.dart';

@immutable
class MyColors extends ThemeExtension<MyColors> {
//
const MyColors({
required this.success,
required this.failure,
});

final Color? success;
final Color? failure;

@override
ThemeExtension<MyColors> copyWith({Color? success, Color? failure}) {
return MyColors(
success: success ?? this.success,
failure: failure ?? this.failure,
);
}

@override
ThemeExtension<MyColors> lerp(ThemeExtension<MyColors>? other, double t) {
if (other is! MyColors) {
return this;
}
return MyColors(
success: Color.lerp(success, other.success, t),
failure: Color.lerp(failure, other.failure, t),
);
}

@override
String toString() {
return 'MyColors(success: $success, failure: $failure)';
}

static const light = MyColors(
success: Color(0xFF28A745),
failure: Color.fromARGB(255, 128, 7, 35),
);

static const dark = MyColors(
success: Color.fromARGB(255, 226, 234, 8),
failure: Color.fromARGB(255, 205, 127, 18),
);
}

In the above code, I am declaring just two variables success and false.

And overrride two methods copyWith and lerp to return the new MyColor.

For convenience, declared two methods ‘light’ and ‘dark’ to use in different modes and initialized with colors for each mode.

Usage

Now in the UI, you can access it like any other Theme variable.

 Widget build(BuildContext context) {
final myColors = Theme.of(context).extension<MyColors>()!;
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Theme Extensions'),
),
body: Container(
alignment: Alignment.center,
padding: const EdgeInsets.all(30),
child: Column(
children: [
Text(
'Hello Flutter',
style: Theme.of(context).textTheme.bodyLarge,
),
const SizedBox(height: 30),
Container(
width: 200,
height: 200,
color: _failure ? myColors.failure : myColors.success,
),
const SizedBox(height: 30),
TextButton(
onPressed: () {
setState(() {
_failure = !_failure;
});
},
child: Text(_failure ? 'Failure' : 'Success'),
)
],
),
),
);
}

In the above code

final myColors = Theme.of(context).extension<MyColors>()!;

helps to get the MyColors and them you can do myColors.success and myColors.failure to access our custom theme colors. By this way MyColors has become part of the Theme.of(context) widget.

Very easy and convenient.

Watch the Video tutorial to see it in action.

Please clap and share if you find this article useful.

Thanks for reading.

--

--