Cleanly overriding parts of a theme locally in Flutter - flutter

I have a widget which has two TextFields as descendants. I would like to apply the same styling to these TextFields. My understanding is that the right way to do this is to apply a localized theme to my widget tree. The following is my attempt. This is a code snippet from my root widget's build function. Is there not a cleaner way to do this?
final ThemeData _themeData = Theme.of(context);
return Theme( // HACK
data: _themeData.copyWith(
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(),
),
textTheme: _themeData.textTheme.copyWith(
subhead: _themeData.textTheme.subhead.copyWith(
fontSize: 30.0,
),
),
),
child: _buildTheRestOfMyWidgetTree(context),
);
The thing that I am annoyed by is that to override a single property (_themeData.textTheme.subhead.fontSize), I have to explicitly and manually make copies of three intermediate data structures (_themeData, then _themeData.textTheme, then _themeData.textTheme.subhead).

While I can understand the frustration of having to "copy" everything, this is how you should do it.
Data are immutable in Flutter. You cannot mutate them, you are forced to clone them with different properties.
Therefore your assumption is correct: If you want to modify a nested property, you have to clone all of its parents too. Which leads to:
final ThemeData theme = Theme.of(context);
theme.copyWith(
textTheme: theme.textTheme.copyWith(
subhead: theme.textTheme.subhead.copyWith(
fontSize: 30.0,
),
),
);
Again: you cannot avoid it.

It would help if you packaged that part of the code up and made it a widget so that your tree is cleaner. That's how it's done in this example.
class TextFieldOverride extends StatelessWidget {
const TextFieldOverride({this.child});
final Widget child;
#override
Widget build(BuildContext context) {
final themeData = Theme.of(context);
return Theme(
child: child,
data: themeData.copyWith(
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder()),
textTheme: themeData.textTheme.copyWith(
subhead: themeData.textTheme.subhead.copyWith(
fontSize: 30.0))));
}
}
...
TextFieldOverride(
child: TextField(...)
)
Or if there are few places the code will be duplicated, you can just make the changes directly:
...
child: TextField(
style: Theme.of(context).textTheme.subhead.copyWith(fontSize: 30.0),
decoration: InputDecoration(border: OutlineInputBorder(),
...
)
)
Or perhaps the best choice is to create a function that does the above for you.
TextField buildTextField(BuildContext context) => TextField(
style: Theme.of(context).textTheme.subhead.copyWith(fontSize: 30.0),
decoration: InputDecoration(border: OutlineInputBorder(),
...
)
)

Related

How do I deal with this insanity of Flutter's closing requirements?

I'm constantly getting stuck with the ]'s, }'s and )'s at the end of my widgets:
Expanded(
child: TextFormField(
controller: feedbackController,
cursorColor: Colors.white,
style: TextStyle(
color: Colors.white,
),
decoration: InputDecoration(labelText: 'text input'),
)
]),
)
)
]),
I don't know how to deal with this. I find myself wasting so much time just guessing at how to close my triple nested widgets nested within double widgets nested within whatever. It's too convoluted. Is there a finishing tool for building UI's in flutter? An addon that'll close these out for me?
edit: here's what finally closed it.
)
)]))]
)
)]);
}
}
You can format the document to see the widget tree correctly, but basically in your code that you showed above it should be like this:
Expanded(
child: TextFormField(
cursorColor: Colors.white,
style: TextStyle(
color: Colors.white,
),
decoration: InputDecoration(labelText: 'text input'),
)),
Expanded is a class. In the above code you are initializing the class Expanded, and the constructor takes a required child argument of type Widget. Thus you use the class TextFormField.
So basically whenever you use a class you need to use (), each class will have many properties which you seperate them using , (example style is a property which has a value of type TextStyle so you initialize the class and close it ),
You use [] when you have a list example the column widget has a children property which is of type List<Widget>. Finally, you use ; before using }. The semicolon indicates an end of statement, example:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Text("test")
);
}

Flutter: MediaQuery.of() error when trying to style TextField in inputDecorationTheme under MaterialApp()

I'm trying to set inputDecorationTheme with some responsive fontSize and padding using MediaQuery, but getting following error :
"MediaQuery.of() called with a context that does not contain a MediaQuery. No MediaQuery ancestor could be found starting from the context that was passed to MediaQuery.of(). This can happen because you do not have a WidgetsApp or MaterialApp widget (those widgets introduce a MediaQuery), or it can happen if the context you use comes from a widget above those widgets.
The context used was: MyApp
"
I have already gone through other posts having similar issue, but in my case I'm using MediaQuery inside ThemeData > inputDecorationTheme.
Note: I'm using separate file for accessing MediQuery.
Below is my code :
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: MyHomeApp(),
theme: ThemeData(
primaryColor: Color(0xff00bcd4),
// TextFormField Decoration
inputDecorationTheme: InputDecorationTheme(
contentPadding:
EdgeInsets.symmetric(vertical: SizeConfig.heightMultiplier * 2),
labelStyle: TextStyle(
fontFamily: "Montserrat",
fontSize: SizeConfig.textMultiplier * 2.3),
hintStyle: TextStyle(
fontFamily: "Montserrat",
fontSize: SizeConfig.textMultiplier * 2.2),
enabledBorder:
OutlineInputBorder(borderSide: BorderSide(color: custColor)),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.greenAccent),
),
),
),

How to edit the flappy_search_bar flutter package?

I want to edit some features of the flappy_search_bar
As you see see the loading spinner is blue and I can't seem to edit this to any alternate color. I also am unable to edit the cursor color which is also blue.
Here is a copy of my current code block:
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SearchBar<Post>(
searchBarPadding: EdgeInsets.symmetric(horizontal: 20),
headerPadding: EdgeInsets.symmetric(horizontal: 10),
listPadding: EdgeInsets.symmetric(horizontal: 10),
onSearch: _getALlPosts,
hintStyle: TextStyle(color: Colors.black87),
hintText: 'Search',
iconActiveColor: Colors.deepPurpleAccent,
// cursorColor: Colors.deepPurpleAccent,
// loader: Colors.deepPurpleAccent,
textStyle: TextStyle(
fontFamily: 'OpenSans',
fontSize: 18.0,
),`
So far, I have tried editing directly within the search bar as seen above, adding extension code as follows:
class Styling extends SearchBar {
Styling(this.cursorColor);
final Color cursorColor;
static const accentColor = Colors.deepPurpleAccent;
#override
Widget build(BuildContext context) {
return Theme(
// data: Theme.of(context).copyWith(
// cursorColor: Theme.of(context).accentColor,
// ),
);
}
}
I have also tried editing the package files directly but this hasn't been a success either.
Has anyone suggest how to edit the cursor color and loading spinner color of the flappy_search_bar package?
I've been trying to do this too and managed to get it done.
To edit your cursor, apply the following to your top level MaterialApp under ThemeData :
cursorColor: Colors.red,
cupertinoOverrideTheme: CupertinoThemeData(
primaryColor: Colors.red,
),
This sets the color of the Cursor in ALL TextFields , I don't know how to edit it just for the TextField in the flappy_search_bar widget
For the loading indication, add this under the SearchBar you're using:
SearchBar<SomeClass>(
....
loader: const Center(child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation<Color>(Colors.red))),
....
);
This is what the widget does internally if you look at the source.
Change Colors.red to whatever color you need.
Hope that helps.

Set TextFromField style in Theme

So in my flutter app, I create TextFormField to use as an input like so (and return it in a scaffold):
final password = TextFormField(
controller: passController,
autofocus: false,
obscureText: true,
decoration: InputDecoration(hintText: 'Password'),
style: new TextStyle(color: Colors.orange),
);
I would like to change the style property inside a themeData, but I couldn't find which property to specify.
The closest one I could get to was textTheme: new TextTheme(body1: new TextStyle(color: Colors.orange)), but this one does nothing to my TextFormField.
How can I set the TextFormField style? Please help, I'm really new to Dart and also programming. I'm currently 13 and I have nobody to help me out with these types of things.
P.S: The complete code is on GitHub if needed: https://github.com/Legolaszstudio/novynotifier
Update:
If you want to change the Text within TextField by Theme of your Flutter App. It's now subtitle1 of TextTheme.
textTheme: TextTheme(
subtitle1: TextStyle(color: Colors.blue),
)
Uhm! That's a long question. TextFormField are subclass of the TextField. The default style of TextField could be found from source below.
final TextStyle style = themeData.textTheme.subhead.merge(widget.style);
So, you have 2 solutions for your source code.
Solution 1
Delete style property are inputted to password.
final password = TextFormField(
controller: passController,
autofocus: false,
obscureText: true,
decoration: InputDecoration(hintText: 'Password'),
style: new TextStyle(color: Colors.orange), // ★ => Delete this.
);
Define a custom subhead style from DataTheme and input into Material app.
MaterialApp(
theme: ThemeData(
textTheme: TextTheme(
subhead: TextStyle(color: Colors.orange),
),
),
home: null,
)
Solution 2
Define a custom subhead style from DataTheme and input into Material app.
MaterialApp(
theme: ThemeData(
textTheme: TextTheme(
subhead: TextStyle(color: Colors.orange),
),
),
home: null,
)
Copy this subhead style into password
final themes = Theme.of(context);
final password = TextFormField(
controller: passController,
autofocus: false,
obscureText: true,
decoration: InputDecoration(hintText: 'Password'),
style: themes.textTheme.subhead, // ★ => Update this.
);
I believe you are looking for subhead
textTheme: TextTheme(
subhead: TextStyle(color: Colors.orange),
)
2021 - 2022
The Material spec for typography changed significantly in 2021 and now uses display, headline, title, body, and label types. titleMedium is the one that will effect all of your TextFields and TextFormFields.
This fact is highlighted in the TextTheme class docs as well.
If you're using the the titleMedium TextStyle for something else, you can easily wrap your TextFields and TextFormFields in a Theme widget and update this style specifically for these widgets like this:
Theme(
data: Theme.of(context).copyWith(
textTheme: Theme.of(context).textTheme.copyWith(
titleMedium: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w300,
),
)),
child: TextField()
),

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())