I am trying to write a custom ThemeData
which I can pass to the extensions
property of the ThemeData
and be able to access them easily in my project. I have attached below the minimal code of what I am trying to achieve. As in the example code below, I want to be able to apply the theme simply by assigning to the style
property as style: MyCustomAppTheme.of(context).specialTextStyle
. The code below contains all the files of my minimal project: main.dart, theme.dart and my_custom_themedata.dart .
The problem: So, far I have not been able to override the lerp()
(linear interpolation) method properly. Also there is a problem with my extending of the ThemeData class.
Codes:
main.dart:
import 'package:extending_flutter_themedata/themes/theme.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: MyCustomAppTheme.lightTheme,
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).primaryColor,
title: const Text("Extending ThemeData"),
),
body: Text(
"Hello world",
style: MyCustomAppTheme.of(context).specialTextStyle,
),
);
}
}
theme.dart:
import 'package:extending_flutter_themedata/themes/extending_themedata/my_custom_themedata.dart';
import 'package:flutter/material.dart';
class MyCustomAppTheme {
MyCustomAppTheme._();
static ThemeData lightTheme = ThemeData(
useMaterial3: true,
fontFamily: "Roboto",
brightness: Brightness.light,
extensions: <ThemeExtension<dynamic>>[
MyCustomThemeData(
customBackgroundColor: Colors.pink,
specialTextStyle: const TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
],
);
}
my_custom_themedata.dart:
import 'package:flutter/material.dart';
class MyCustomThemeData extends ThemeExtension<MyCustomThemeData> {
final Color customBackgroundColor;
final TextStyle specialTextStyle;
MyCustomThemeData({
this.customBackgroundColor = Colors.blue,
this.specialTextStyle = const TextStyle(),
});
@override
MyCustomThemeData copyWith({
Color? customBackgroundColor,
TextStyle? specialTextStyle,
}) {
return MyCustomThemeData(
customBackgroundColor:
customBackgroundColor ?? this.customBackgroundColor,
specialTextStyle: specialTextStyle ?? this.specialTextStyle,
);
}
static MyCustomThemeData of(BuildContext context) {
return Theme.of(context).extension<MyCustomThemeData>()!;
}
@override
MyCustomThemeData lerp(MyCustomThemeData? a, MyCustomThemeData? b, double t) {
if (a == null) return b!;
if (b == null) return a;
return MyCustomThemeData(
customBackgroundColor:
Color.lerp(a.customBackgroundColor, b.customBackgroundColor, t)!,
specialTextStyle:
TextStyle.lerp(a.specialTextStyle, b.specialTextStyle, t)!,
);
}
static const id = 'my_custom_theme_data';
}
extension ThemeDataExtension on ThemeData {
MyCustomThemeData? get myCustomThemeData => extension<MyCustomThemeData>();
ThemeData copyWithCustom({MyCustomThemeData? myCustomThemeData}) {
return copyWith(
extensions: <ThemeExtension<dynamic>>{
if (myCustomThemeData != null) myCustomThemeData,
},
);
}
static ThemeData lerp(ThemeData? a, ThemeData? b, double t) {
return ThemeData.lerp(a, b, t)?.copyWithCustom(
myCustomThemeData: MyCustomThemeData.lerp(
a?.extension<MyCustomThemeData>(),
b?.extension<MyCustomThemeData>(),
t,
),
);
}
}
Update 01:
I have changes the lerp
override to as follows and now some problems have resolved.
@override
MyCustomThemeData lerp(ThemeExtension<MyCustomThemeData>? other, double t) {
if (other is! MyCustomThemeData) {
return this;
}
return MyCustomThemeData(
customBackgroundColor:
Color.lerp(customBackgroundColor, other.customBackgroundColor, t)!,
specialTextStyle:
TextStyle.lerp(specialTextStyle, other.specialTextStyle, t)!,
);
}
Now, I am able to apply the theme as:
Text(
"Hello world",
style: MyCustomThemeData.of(context).specialTextStyle,
),
and the theme dos apply. But how can I access them using the following code within the build()
method? (The Theme.of(context) method of access is still not working!)
MyCustomAppTheme? mcat = Theme.of(context).extension<MyCustomAppTheme>();
TextStyle myTextStyle = mcat.specialTextStyle;
I am trying to write a custom ThemeData
which I can pass to the extensions
property of the ThemeData
and be able to access them easily in my project. I have attached below the minimal code of what I am trying to achieve. As in the example code below, I want to be able to apply the theme simply by assigning to the style
property as style: MyCustomAppTheme.of(context).specialTextStyle
. The code below contains all the files of my minimal project: main.dart, theme.dart and my_custom_themedata.dart .
The problem: So, far I have not been able to override the lerp()
(linear interpolation) method properly. Also there is a problem with my extending of the ThemeData class.
Codes:
main.dart:
import 'package:extending_flutter_themedata/themes/theme.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: MyCustomAppTheme.lightTheme,
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).primaryColor,
title: const Text("Extending ThemeData"),
),
body: Text(
"Hello world",
style: MyCustomAppTheme.of(context).specialTextStyle,
),
);
}
}
theme.dart:
import 'package:extending_flutter_themedata/themes/extending_themedata/my_custom_themedata.dart';
import 'package:flutter/material.dart';
class MyCustomAppTheme {
MyCustomAppTheme._();
static ThemeData lightTheme = ThemeData(
useMaterial3: true,
fontFamily: "Roboto",
brightness: Brightness.light,
extensions: <ThemeExtension<dynamic>>[
MyCustomThemeData(
customBackgroundColor: Colors.pink,
specialTextStyle: const TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
),
),
],
);
}
my_custom_themedata.dart:
import 'package:flutter/material.dart';
class MyCustomThemeData extends ThemeExtension<MyCustomThemeData> {
final Color customBackgroundColor;
final TextStyle specialTextStyle;
MyCustomThemeData({
this.customBackgroundColor = Colors.blue,
this.specialTextStyle = const TextStyle(),
});
@override
MyCustomThemeData copyWith({
Color? customBackgroundColor,
TextStyle? specialTextStyle,
}) {
return MyCustomThemeData(
customBackgroundColor:
customBackgroundColor ?? this.customBackgroundColor,
specialTextStyle: specialTextStyle ?? this.specialTextStyle,
);
}
static MyCustomThemeData of(BuildContext context) {
return Theme.of(context).extension<MyCustomThemeData>()!;
}
@override
MyCustomThemeData lerp(MyCustomThemeData? a, MyCustomThemeData? b, double t) {
if (a == null) return b!;
if (b == null) return a;
return MyCustomThemeData(
customBackgroundColor:
Color.lerp(a.customBackgroundColor, b.customBackgroundColor, t)!,
specialTextStyle:
TextStyle.lerp(a.specialTextStyle, b.specialTextStyle, t)!,
);
}
static const id = 'my_custom_theme_data';
}
extension ThemeDataExtension on ThemeData {
MyCustomThemeData? get myCustomThemeData => extension<MyCustomThemeData>();
ThemeData copyWithCustom({MyCustomThemeData? myCustomThemeData}) {
return copyWith(
extensions: <ThemeExtension<dynamic>>{
if (myCustomThemeData != null) myCustomThemeData,
},
);
}
static ThemeData lerp(ThemeData? a, ThemeData? b, double t) {
return ThemeData.lerp(a, b, t)?.copyWithCustom(
myCustomThemeData: MyCustomThemeData.lerp(
a?.extension<MyCustomThemeData>(),
b?.extension<MyCustomThemeData>(),
t,
),
);
}
}
Update 01:
I have changes the lerp
override to as follows and now some problems have resolved.
@override
MyCustomThemeData lerp(ThemeExtension<MyCustomThemeData>? other, double t) {
if (other is! MyCustomThemeData) {
return this;
}
return MyCustomThemeData(
customBackgroundColor:
Color.lerp(customBackgroundColor, other.customBackgroundColor, t)!,
specialTextStyle:
TextStyle.lerp(specialTextStyle, other.specialTextStyle, t)!,
);
}
Now, I am able to apply the theme as:
Text(
"Hello world",
style: MyCustomThemeData.of(context).specialTextStyle,
),
and the theme dos apply. But how can I access them using the following code within the build()
method? (The Theme.of(context) method of access is still not working!)
MyCustomAppTheme? mcat = Theme.of(context).extension<MyCustomAppTheme>();
TextStyle myTextStyle = mcat.specialTextStyle;
Share
Improve this question
edited 22 hours ago
rusty
asked 23 hours ago
rustyrusty
3681 gold badge1 silver badge10 bronze badges
2
- 1 check this sample – pskink Commented 21 hours ago
- @pskink, thank you that was helpful. – rusty Commented 21 hours ago
1 Answer
Reset to default 1Note: Improvements to this answer as separate answers or comments are welcome. I am not marking my answer as accepted because there might be better solution with others. Currently I'm not satisfied with how I'm having to null check and assign a default value as:
TextStyle mcat02 =
Theme.of(context).myCustomThemeData?.specialTextStyle ??
TextStyle().copyWith(color: Colors.green);
Update, to above, I've learned that we can type-case the right hand side as follows (which seems simpler):
TextStyle mcat03 =
Theme.of(context).myCustomThemeData?.specialTextStyle as TextStyle;
I managed to get things working. I am posting it as answer for documentation purpose, if it might help others.
In this main.dart
file, in one of the Text
widget I've directly used the class MyCustomThemeData
and consequently imported import 'package:extending_flutter_themedata/themes/extending_themedata/my_custom_themedata.dart';
which is not how we want to access the theme. The alternative method shown using the TextStyle mcat02 = ... ...
is the preferred way.
The theme for both light and dark modes apply. We may check them with the following command in the terminal:
adb shell "cmd uimode night yes" // dark mode
adb shell "cmd uimode night no" // light mode
main.dart:
import 'package:extending_flutter_themedata/themes/extending_themedata/my_custom_themedata.dart';
import 'package:extending_flutter_themedata/themes/theme.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
themeMode: ThemeMode.system,
theme: MyCustomAppTheme.lightTheme,
darkTheme: MyCustomAppTheme.darkTheme,
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
/*
// Could not get the following working:
MyCustomAppTheme? mcat = Theme.of(context).extension<MyCustomAppTheme>();
TextStyle myTextStyle = mcat.specialTextStyle;
*/
// This one works:
TextStyle mcat02 =
Theme.of(context).myCustomThemeData?.specialTextStyle ??
TextStyle().copyWith(color: Colors.green);
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).primaryColor,
title: const Text("Extending ThemeData"),
),
body: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(15.0),
child: Column(
children: [
Text(
"Hello world",
style: MyCustomThemeData.of(context).specialTextStyle,
),
Text("Hello world", style: mcat02),
Text("Hello world", style: mcat02.copyWith(color: Colors.pink)),
],
),
),
),
);
}
}
theme.dart:
import 'package:extending_flutter_themedata/themes/extending_themedata/my_custom_themedata.dart';
import 'package:flutter/material.dart';
class MyCustomAppTheme {
MyCustomAppTheme._();
static ThemeData lightTheme = ThemeData(
useMaterial3: true,
fontFamily: "Roboto",
brightness: Brightness.light,
extensions: <ThemeExtension<dynamic>>[
MyCustomThemeData(
customBackgroundColor: Colors.pink,
specialTextStyle: const TextStyle(
fontSize: 30.0,
fontWeight: FontWeight.bold,
color: Colors.amber,
),
),
],
);
static ThemeData darkTheme = ThemeData(
useMaterial3: true,
fontFamily: "Roboto",
brightness: Brightness.dark,
extensions: <ThemeExtension<dynamic>>[
MyCustomThemeData(
customBackgroundColor: Colors.pink,
specialTextStyle: const TextStyle(
fontSize: 30.0,
fontWeight: FontWeight.bold,
color: Colors.tealAccent,
),
),
],
);
}
my_custom_themedata.dart:
import 'package:flutter/material.dart';
class MyCustomThemeData extends ThemeExtension<MyCustomThemeData> {
final Color customBackgroundColor;
final TextStyle specialTextStyle;
MyCustomThemeData({
this.customBackgroundColor = Colors.blue,
this.specialTextStyle = const TextStyle(),
});
@override
MyCustomThemeData copyWith({
Color? customBackgroundColor,
TextStyle? specialTextStyle,
}) {
return MyCustomThemeData(
customBackgroundColor:
customBackgroundColor ?? this.customBackgroundColor,
specialTextStyle: specialTextStyle ?? this.specialTextStyle,
);
}
static MyCustomThemeData of(BuildContext context) {
return Theme.of(context).extension<MyCustomThemeData>()!;
}
@override
MyCustomThemeData lerp(ThemeExtension<MyCustomThemeData>? other, double t) {
if (other is! MyCustomThemeData) {
return this;
}
return MyCustomThemeData(
customBackgroundColor:
Color.lerp(customBackgroundColor, other.customBackgroundColor, t)!,
specialTextStyle:
TextStyle.lerp(specialTextStyle, other.specialTextStyle, t)!,
);
}
static const id = 'my_custom_theme_data';
}
extension ThemeDataExtension on ThemeData {
MyCustomThemeData? get myCustomThemeData => extension<MyCustomThemeData>();
ThemeData copyWithCustom({MyCustomThemeData? myCustomThemeData}) {
return copyWith(
extensions: <ThemeExtension<dynamic>>{
if (myCustomThemeData != null) myCustomThemeData,
},
);
}
/*
static ThemeData lerp(ThemeData? a, ThemeData? b, double t) {
return ThemeData.lerp(a, b, t)?.copyWithCustom(
myCustomThemeData: MyCustomThemeData.lerp(
a?.extension<MyCustomThemeData>(),
b?.extension<MyCustomThemeData>(),
t,
),
);
}
*/
}
In my_custom_themedata.dart, I am just commenting out the static ThemeData lerp
definition. Any answer fixing are welcome.