How can I Allow a user to change font in Flutter App? - flutter

I'm wanting to allow my users to have more customization in the UI of our app. Currently, our app is set up using ThemeData and Google Fonts.
I've implemented a simple dropdown to allow a user to select a font. How can I then take that selected font and change all fonts globally?
Main road block I'm finding it that I only want to change the font in TextStyle and not fontWeight / fontSize etc. I can save the user's selection via shared preferences or to the users collection on Firebase, but then how can I change .roboto to the selected choice.
Example of how I currently use Google Fonts (my current font is Roboto)
AutoSizeText(
AppLocalizations.of(context)!.newNote,
style: GoogleFonts.roboto(
textStyle: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
maxLines: 1,
),
Let me know if you need more explenation.

You can implemnent it similar to the themeMode setting in the new Flutter app skeleton. Something like this, only changing fontFamily as an example:
class MyApp extends StatelessWidget {
const MyApp({
super.key,
required this.settingsController,
});
final SettingsController settingsController;
#override
Widget build(BuildContext context) {
// The AnimatedBuilder Widget listens to the SettingsController for changes.
// Whenever the user updates their settings, the MaterialApp is rebuilt.
return AnimatedBuilder(
animation: settingsController,
builder: (BuildContext context, Widget? child) {
final themeData = ThemeData(
fontFamily: settingsController.fontFamily, // for example GoogleFonts.roboto().fontFamily
);
return MaterialApp(
theme: themeData,
// ...
The SettingsController class should implement ChangeNotifier and call notifyListeners() when the user changes font.
Se this post on how to use Roboto in a TextTheme: https://stackoverflow.com/a/64271758/20444

Related

Primary custom color not working in flutter

I have a problem here I want to change color of textbutton from themedata but its not working. Here is my code :
darkTheme: ThemeData(
primaryColor:Colors.white,
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(primary: Colors.white),
)
),
and my button code :
TextButton(
style: TextButton.styleFrom(
primary: Theme.of(context).primaryColor,
textStyle: TextStyle(fontSize: 16),),
onPressed: (){}, child: Text("Hellosir",))
I can think of two problems why this is not working.
First, you want to access ThemeData defined in darkTheme, but your themeMode is not dark. So in MaterialApp add themeMode: ThemeMode.dark parameter as well.
Second, your button where you call Theme.of(context).primaryColor is inside same widget as your definition of Theme, and your context still doesn't have that data. So only context of children of current widget have this data. Solution would be to make a new widget with your button inside it, or wrap your button with Builder widget which have context inside its builder.
Your problem can be first, second or both.

Create custom TextStyle class on Flutter

How can I create a custom TextStyle class in flutter? For example I have several buttons and want the text inside to all be the same fontSize and color but I don't want to repeat the TextStyle inside of each Text widget, but instead create something such as CustomTextStyle that I could use in the place of the TextStyle itself.
Is there a way to do this?
Create a separate class names CustomTextStyle and add the styles inside them like:
class CustomTextStyle {
static const TextStyle nameOfTextStyle = TextStyle(
fontSize: 24,
color: Colors.green,
fontWeight: FontWeight.bold,
);
}
Now you can use it in Text widget like:
Text('Lorem Ipsum',
style: CustomTextStyle.nameOfTextStyle,
)
To define your button or text style globally, you need to use theme.
For example, you can define a custom theme for TextButton :
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
textButtonTheme: TextButtonThemeData(
style: ButtonStyle(
textStyle: MaterialStateProperty.all(
const TextStyle(fontSize: 20),
),
),
),
[...]
There are three ways to do this:
Using Theme:
You can declare multiple TextStyles separately in Theme based on widgets, for e.g. AppBar's TitleTextTheme, AppBar's ToolbarTextTheme, Button's TextTheme, General TextTheme for the whole app, or based on ThemeMode, for e.g. Dark Mode or Light mode, you can combine these two Widgets based and ThemeModes based as well.
To do this, Create a class called ThemeModel for example, Create the ThemeData Variables in it in which you'll declare these TextThemes and use it in your MaterialApp in main.dart as
MaterialApp(
...,
theme: ThemeModel().lightMode,
darkTheme: ThemeModel().darkMode,
)
Using a Custom Text Widget.
const CustomText extends StatelessWidget {
final String text;
const CustomText(this.text, {Key? key}): super(key:key);
#override
Widget build(BuildContext context) {
return Text(
text,
style: TextStyle(
color: YourColor,
fontSize: YourSize,
fontWeight: YourFontWeight
),
);
and use it as,
ElevatedButton(
onPressed: (){},
child: CustomText('Click Me'),
)
This way the TextStyle will remain same wherever you'll use this widget.
Using Custom TextStyle:
class CustomTextStyle {
static const TextStyle textStyle = TextStyle(
color: YourColor,
fontSize: YourSize,
fontWeight: YourFontWeight
);
}
and, use it as:
ElevatedButton(
onPressed: (){},
child: Text('Click Me',
style: CustomTextStyle.textStyle,
),
)
Hope, you understand that every one of these methods have their own utility and can be customized way further to make your app development easy.

Set default icon theme to cupertino flutter appbar

The app that im building requires me to have an AppBar with a leading back button. However I prefer the cupertino back button(iOS-style) for the leading icon instead of the default back button for android. I am aware that I can manually change the leading button of each AppBar by using an iconButton but i was wondering if there is any easy way to do this like a theme. Any help appreciated.
Instead of using MaterialApp as your root widget you can use CupertinoApp to do the same, assuming that the above changing of the AppBar is needed for each screen in your app. This will automatically set the icon as you require
Here is a simple example to help you
Root Widget or starting point of the app
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return const CupertinoApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
Then using a CupertinoPageScaffold where you want the CupertinoNavigationBar (I mean your appbar with ios icons) with the chevron icon like ios
Widget build(BuildContext context) {
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
// Try removing opacity to observe the lack of a blur effect and of sliding content.
automaticallyImplyLeading: true // This will decide if the leading icon comes in by default
backgroundColor: CupertinoColors.systemGrey.withOpacity(0.5),
middle: const Text('Sample Code'),
),
child: Column(
children: <Widget>[
Container(height: 50, color: CupertinoColors.systemRed),
Container(height: 50, color: CupertinoColors.systemGreen),
Container(height: 50, color: CupertinoColors.systemBlue),
Container(height: 50, color: CupertinoColors.systemYellow),
],
),
);
Facing a relatively similar problem I used the builder property, which it should work with any App like :
CupertinoApp(
builder: (_, child) => IconTheme(
data: IconThemeData(
size: 15,
color: const Color(0xffffffff),
),
child: child,
),
)
My problem was with the default icon color and size but you can use AppBarTheme or any similar widget to achieve what you want.
This may help you override default value with majority of the lacking theme delegates when working with the cupertino family (It's not yet mature like the material but I can see the constant and rapid effort and the future of it).

Cant use MediaQuery.of(context) for Themes in Flutter

Hi I am trying to create several TextTheme and change the fonts sizes using MediaQuery.of(context) based on this article:
Flutter — Effectively scale UI according to different screen sizes
But I am getting this error:
MediaQuery.of() called with a context that does not contain a MediaQuery.
I know based on this post: Flutter Error: MediaQuery.of() called with a context that does not contain a MediaQuery
I should use MediaQuery on my HomePage but then I cannot create themes using MediaQuery then?
Here is my code:
child:
MaterialApp(
theme: ThemeData(
/// TextFields Handlers transparent
textSelectionHandleColor: Colors.transparent,
pageTransitionsTheme: const PageTransitionsTheme(
builders: <TargetPlatform, PageTransitionsBuilder>{
TargetPlatform.android: ZoomPageTransitionsBuilder(),
},
),
textTheme: TextTheme(
/// Pages Titles
headline1: textTheme(
fontSize: (MediaQuery.of(context).size.width / 100) * 1.5,
fontWeight: FontWeight.w600,
color: Globals.allColors['celeste'],
),
headline2: textTheme(
fontSize: 15,
fontWeight: FontWeight.w600,
color: Globals.allColors['cetaceanBlue']),
...
The error is at:
(MediaQuery.of(context).size.width / 100) * 1.5,
Thanks in advance!
No, you cannot create your material theme with values retrieved from Mediaquery. You set 'default' font size, etc. in your material app theme data. Then in your home page build method you call Mediaquery and modify your font size for that page if necessary.
Try wrapping your entire homepage in a builder widget.
You could do this like a one-off kind of thing. You could save your text themes in a themes.dart file or something like that..and then subsequently, you could have a loading page, where you make a MediaQuery and calculate your theme values there.
I think you might have to run setState after that, maybe not, because when you navigate to your home screen from there, the values of the theme would have updated.
MediaQuery needs the MaterialApp widget as an ancestor, you can't use it when constructing the MaterialApp itself. A workaround is not using the theme property but wrapping the descendants with a Theme widget using the builder property. It will have the MaterialApp as an ancestor so you can use MediaQuery there.
MaterialApp(
builder: (context, child) {
return Theme(
data: ThemeData(
// your theme
// can use e.g. MediaQuery.of(context).size.width here
),
child: child,
);
},
home: SomeScreen(),
// etc, no theme here
)

How to set color of all text in a specific container in flutter?

I don't want to change the text color of the whole app. Just all the text inside a container. Can I wrap it with some other widget or something for this ?
To apply certain TextStyle properties only to a subtree of your app. You can use DefaultTextStyle
DefaultTextStyle(
child: Container(child: /* your subtree */),
style: TextStyle(color: Colors.red),
),
as a comment pointed out, this replaces all defaults, not just the color. This can be mitigated by using the merge constructor:
DefaultTextStyle.merge(
child: Container(child: /* your subtree */),
style: TextStyle(color: Colors.red),
),
flutter's answer is good in my opinion. But the power of ThemeData is more than you think. Here is the official documentation about Themes for part of an application.
You could provide a Theme to wrap your container to provide a new theme. Here is two way to slove it:
1. Creating unique ThemeData
/*Not recommended, this could make a totally different If you just want a little part changed.*/
Theme(
// Create a unique theme with "ThemeData"
data: ThemeData(
textTheme: /* Your Text Theme*/,
),
child: Container(
onPressed: () {},
child: Text("Your Text Here"),
),
);
2. Extending the parent theme
Theme(
// Find and extend the parent theme using "copyWith". See the next
// section for more info on `Theme.of`.
data: Theme.of(context).copyWith(textTheme: /* Provide your theme here! */),
child: Container(
child: Text("your text here"),
),
);
You could also use existed theme with a little changed:
Theme.of(context).textTheme.copyWith(
body1: Theme.of(context).textTheme.body1.copyWith(color: Colors.red),
)
Use DefaultTextStyle.merge to keep your theme and just change the color.
DefaultTextStyle.merge(
style: TextStyle(color: Colors.grey[400]),
child: Column(...),
)
If you are using the MaterialApp widget you could use the theme property of it and set different Text themes and call them anywhere in your app. For example the following code defines 3 different text themes:
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "Time Tracker",
theme: ThemeData(
textTheme: TextTheme(
headline: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold,color: Colors.blue),
title: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic,color: Colors.red),
body1: TextStyle(fontSize: 14.0, fontFamily: 'Hind',color: Colors.yellow),
),
),
home: LandingPage(),
);
}
}
You can then call a particular theme(headline) anywhere in your app like this:
Text('Home Page',style: Theme.of(context).textTheme.headline,)
Which gives you the headline TextTheme
I have functions for all my styles
TextStyle largeTextStyle() => TextStyle(fontSize: 150);
then I just do
Text("blah", style:largeTextStyle())