Get ThemeData from static area - flutter

I am saving my Text Styles in seperate text_styles.dart file. When i want to use theme colors just like Theme.of(context).primaryColor, I cant reach ThemeData object from text_styles.dart .I solved my problem with this kind of solution but this is not good solution.
TextStyle kWelcomePageHeaderTextStyle(BuildContext context) => TextStyle(
fontFamily: "Courgette",
fontSize: 30.0,
color: Theme.of(context).primaryColor,
);
So, i need to get ThemeData from static area for use my Text Styles like this.
const kWelcomePageHeaderTextStyle = TextStyle(
fontFamily: "Courgette",
fontSize: 30.0,
color: [THEME_DATA_OBJECT_NEEDED].primaryColor,
);
Can I get ThemeData object from text_styles.dart or is there any better solution?

I found better solution with Dependency Injection. I registered the dependency which is BuildContext in MaterialApp.
void main() {
final GetIt sl = GetIt.instance;
runApp(MaterialApp(
theme: myLightTheme,
darkTheme: myDarkTheme,
builder: (BuildContext context, Widget widget) {
if (!sl.isRegistered<BuildContext>()) {
sl.registerSingleton<BuildContext>(context);
}
return HomePage();
},
));
Then I can get Theme on the static area
const kWelcomePageHeaderTextStyle = TextStyle(
fontFamily: "Courgette",
fontSize: 30.0,
color: Theme.of(sl.get<BuildContext>()).primaryColor,
);

Your app does not have a single, globally available theme. So you cannot get it.
Your app already has two themes out of the box (dark mode/light mode) and you can have many more. You can even have a different theme for a specific subtree in your build methods using the Theme widget. You can read more about it in the documentation.
Getting the Theme from the context is the preferred method.

Related

Error: Could not find the correct Provider<LanguageChangeProvider> above this Languages Widget

I am trying to translate my app that has different screens to different languages using localizations and .arb files. The following code it shows how I implemented in MaterialApp and I only included the provider and localization in the main file, not all the pages:
The following are the package versions:
flutter_localizations:
sdk: flutter
localization: ^2.1.0
provider: ^5.0.0
Here is the LanguageChangeProvider class:
class LanguageChangeProvider with ChangeNotifier {
Locale _currentLocal = new Locale("en");
Locale get currentLocal => _currentLocal;
void changeLocalLanguage(String _localLanguage) {
this._currentLocal = new Locale(_localLanguage);
notifyListeners();
}
}
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<LanguageChangeProvider>(
create: (context) => LanguageChangeProvider(),
),
StreamProvider<Userid>.value(
value: AuthService()
.user, //the user is from services auth and non function stream
initialData: null,
),
],
child: Builder(builder: (context) {
return MaterialApp(
locale: Provider.of<LanguageChangeProvider>(context, listen: true)
.currentLocal,
localizationsDelegates: [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
debugShowCheckedModeBanner: false,
color: Color(0xFF13294b),
title: 'Personal Expenses',
theme: ThemeData(
colorScheme: ColorScheme.light(
primary: const Color(0xFF13294b),
),
buttonTheme: ButtonThemeData(textTheme: ButtonTextTheme.primary),
accentColor: Colors.amber,
fontFamily: 'Quicksand',
textTheme: ThemeData.light().textTheme.copyWith(
headline6: TextStyle(
fontFamily: 'FjallaOne',
fontWeight: FontWeight.bold,
fontSize: 18,
color: Color(0xFF13294b),
),
button: TextStyle(color: Colors.white),
),
appBarTheme: AppBarTheme(
textTheme: ThemeData.light().textTheme.copyWith(
headline6: TextStyle(
fontFamily: 'FjallaOne',
fontSize: 20,
fontWeight: FontWeight.bold,
backgroundColor: Color(0xFF13294b),
),
),
),
),
home: Wrapper(),
);
}),
);
}
}
I was able to replicate your issue by removing the Builder widget that you're assigning as the child of the MultiProvider:
Then I added it again and I'm able to pull values from the LanguageChangeNotifier service down the widget hierarchy. For example, I added a property called "valueFromLanguages" in the LanguageChangeNotifier:
class LanguageChangeProvider extends ChangeNotifier {
String valueFromLanguages = "Hello from the Languages Provided Service!";
Locale currentLocal = Locale.fromSubtags();
}
then I pulled it using the Provider.of inside the Wrapper widget and I was able to see the value, as in:
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(Provider.of<LanguageChangeProvider>(context).valueFromLanguages)
),
);
}
}
Here's a Gist I created real quick (I mocked a few things to make it work) but pretty much illustrates my point that the way you originally have it, it should work. Just make sure you're accessing the provided service down in the hierarchy the correct way.

Flutter assign Theme text style

I was wondering is there is a better way to assign a certain default text style of my Theme to a Text widget than this approach.
Text(
'Hello world',
style: Theme.of(context).textTheme.headline1,
),
I did assume there should be something like a separate Widget or a Text Method Text.headline1 or simply a style Command style: TextStyle.headline1.
But seems I have to go through the Theme.of(context) to get this.
Does anyone have a better solution?
I think yon can't escape some boilerplate. For me this approach looks cleanest
import 'package:flutter/material.dart';
class StyledText extends StatelessWidget {
final String text;
late final TextStyle? Function(BuildContext context)? getStyle;
StyledText.headline1(this.text, {Key? key}) : super(key: key) {
getStyle = (context) {
return Theme.of(context).textTheme.headline1;
};
}
StyledText.headline2(this.text, {Key? key}) : super(key: key) {
getStyle = (context) {
return Theme.of(context).textTheme.headline2;
};
}
// ...
#override
Widget build(BuildContext context) {
return Text(text, style: getStyle?.call(context));
}
}
And use the widget like this
StyledText.headline1("Hello world");
Theme.of returns the ThemeData value specified for the nearest BuildContext ancestor. If you don't use it, then you won't be able to access the theme configuration you may set and benefit from its advantages.
However, you can create a class called Styles where you can access the pre-defined colors, text styles and more:
class Styles {
static const Color primaryColor = Colors.blue;
static const TextStyle headline1 = TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.bold,
);
static const TextStyle bodyText1 = TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.normal,
);
}
Here is an example of using it:
Text(
'Hello world',
style: Styles.headline1,
)
You define all Your Theme Style in Main like this
return MaterialApp(
theme: ThemeData(
primarySwatch: Colors.purple,
textTheme: TextTheme(
headline1: TextStyle(
color: const Color(0xFF232323),
fontSize: 26.sp,
fontWeight: FontWeight.w500,
fontFamily: "Mont Regular",
),
),
)
Then use like this
Text("A cloud-agnostic solution for Project and HR Management",
style: Theme.of(context).textTheme.headline1)
You can use TextStyle directly:
Text(
'Hello world',
style: TextStyle(color: Colors.black, fontSize: 15.0), // Etc...
),
Theme.of(context) is a great way to go for a variety of reasons, like switching between light and dark themes. I like to create a variable for the theme and text theme to keep things clean and efficient.
Widget build(BuildContext context) {
final theme = Theme.of(context);
final textTheme = theme.textTheme;
return Column(
children: [
Text('Heading Text', style: textTheme.headline1),
Text('Caption Text', style: textTheme.caption),
Text('Body text...', style: textTheme.bodyText1),
],
);
}

How to create a global TextStyle in Flutter?

In my home page in build widget before return statement I use below code.
If I create an another widget in my home page I have to copy below code before each return statement. If you create another stateless/stateful dart file I need to copy below code again.
My question is How to create a global TextStyle in Flutter? I don't want to change Theme data, all want to use my TextStyle across the my flutter project. Thanks.
TextStyle myNormalStyle = TextStyle(
fontSize: SizerUtil.deviceType == DeviceType.Mobile ? 14.0.sp : 13.0.sp,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.normal,
color: Colors.black);
You can easily you that using by using Theme. As described on the flutter docs you can use it like :
MaterialApp(
title: title,
theme: ThemeData(
// Define the default brightness and colors.
brightness: Brightness.dark,
primaryColor: Colors.lightBlue[800],
accentColor: Colors.cyan[600],
// Define the default font family.
fontFamily: 'Georgia',
// Define the default TextTheme. Use this to specify the default
// text styling for headlines, titles, bodies of text, and more.
textTheme: TextTheme(
headline1: TextStyle(fontSize: 72.0, fontWeight: FontWeight.bold),
headline6: TextStyle(fontSize: 36.0, fontStyle: FontStyle.italic),
bodyText2: TextStyle(fontSize: 14.0, fontFamily: 'Hind'),
),
)
);
or you can also use DefaultTextStyle widget to provide a default TextStyle to all the children of this widget. Read more about DefaultTextStyle.
1º create a class file:
import 'package:flutter/material.dart';
class MyTextStyle {
static const TextStyle textStyle = TextStyle(
color: Color.fromARGB(255, 247, 240, 201),
fontSize: 20,
fontWeight: FontWeight.bold,
);
}
2º import and use in your screen:
import 'package:MyApp/MyTextStyle.dart';
const Text(
'You have pushed the button this many times:',
style: MyTextStyle.textStyle,
),

Custom font is not rendered to golden images when package is provided

I have a custom font defined in module theme. This module is a dependency in module widgets.
A widget in widgets module applies custom font like below
style: TextStyle(
fontSize: fontSize,
fontFamily: "IconActions",
package: "theme"
)
It works fine.
Unfortunately this custom font is not rendered on golden images. I have to remove the package: "theme" to fix that. But that breaks the app and the font is not displayed any more.
So basically I can have the font working correctly in the production code or the test code, but never both.
The custom font is loaded in setUp method of the test
final fontData = File('assets/fonts/IconActions.ttf')
.readAsBytes()
.then((bytes) => ByteData.view(Uint8List.fromList(bytes).buffer));
final fontLoader = FontLoader('IconActions')..addFont(fontData);
await fontLoader.load();
Am I missing something, or is it a bug?
So basically the solution is to remove package: "theme" from the TextStyle to make it work. But that's the half of the solution, because as I mentioned in the question now golden files have the font renderer correctly, but the font doesn't work in the app.
To make it work in the app we need given project structure:
pubspec.yaml (module theme)
flutter:
fonts:
- family 'ComicSans'
fonts:
- asset: packages/theme/fonts/ComicSans.ttf
widget.dart (module theme)
style: TextStyle(
fontSize: fontSize,
fontFamily: "ComicSans",
)
Now in module widgets, which is the module that contains main.dart with its main function that you run, you have to define the font again:
pubspec.yaml (module widgets)
dependencies:
flutter:
sdk: flutter
theme:
path: ../path/to/theme/module
flutter:
fonts:
- family 'ComicSans'
fonts:
- asset: packages/theme/fonts/ComicSans.ttf
Now the font is correctly displayed in both the app and the golden images.
I've had this exact issue for the last year and also couldn't get font loading to work within tests.. did not know that it was specifically the package argument that was breaking it, so thank you for updating with that result.
As for another workaround, there is a way to have the best of both worlds where you can have your standalone font package and not have to declare your packaged font files in your app that is using it.
For example, we have a company branding/typography package which we use across multiple apps that contains all our pre-configured TextStyle declarations, and another standalone package which has custom generated IconData that is stored within a *.ttf file (like FontAwesome).
The package side:
pubspec.yaml
flutter:
uses-material-design: true
assets:
- assets/fonts/
fonts:
- family: MyFont
fonts:
- asset: assets/fonts/MyFont.ttf
weight: 400
# etc
The packaged TextStyle:
class BrandStyles {
static const _packageName = '<package_name>';
static const headline1Style = TextStyle(
color: Colors.black,
fontFamily: 'MyFont',
fontSize: 60.0,
fontStyle: FontStyle.normal,
fontWeight: FontWeight.w400,
height: 1.16,
letterSpacing: 0,
package: _packageName,
);
// etc
}
Golden test
void main() {
final widget = MaterialApp(
theme: ThemeData(
textTheme: TextTheme(
// use custom extension method to remove `package` value
headline1: BrandStyles.headline1Style.trimFontPackage(),
),
),
home: Scaffold(
body: SafeArea(child: StylesExample()),
),
);
setUp(() async {
TestWidgetsFlutterBinding.ensureInitialized();
final file = File('path/to/packaged/asset/MyFont.ttf').readAsBytesSync();
final bytes = Future<ByteData>.value(file.buffer.asByteData());
await (FontLoader('MyFont')..addFont(bytes)).load();
});
testWidgets('Golden typography test', (WidgetTester tester) async {
await tester.pumpWidget(widget);
await expectLater(
find.byType(MaterialApp), matchesGoldenFile('goldens/typography.png'));
});
}
extension StylingExtensions on TextStyle {
TextStyle trimFontPackage() {
return TextStyle(
inherit: inherit,
color: color,
backgroundColor: backgroundColor,
fontSize: fontSize,
fontWeight: fontWeight,
fontStyle: fontStyle,
letterSpacing: letterSpacing,
wordSpacing: wordSpacing,
textBaseline: textBaseline,
height: height,
locale: locale,
foreground: foreground,
background: background,
shadows: shadows,
fontFeatures: fontFeatures,
decoration: decoration,
decorationColor: decorationColor,
decorationStyle: decorationStyle,
decorationThickness: decorationThickness,
debugLabel: debugLabel,
/// `replaceAll` only required if loading multiple fonts,
/// otherwise set value to your single `fontFamily` name
fontFamily: fontFamily.replaceAll('packages/<package_name>/', ''),
);
}
}
Or if like me, you have the same issue with custom icons, the same can be done within your golden test for your custom IconData with a similar extension method, removing the fontPackage value:
extension IconExtensions on IconData {
IconData convertToGolden() => IconData(
this.codePoint,
fontFamily: this.fontFamily,
);
}
Your app side
pubspec.yaml
# ...
dependencies:
flutter:
sdk: flutter
<package_name>:
git:
url: <url_to_hosted_package>.git
ref: <release_tag>
main.dart
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.light().copyWith(
textTheme: TextTheme(
headline1: BrandStyles.headline1Style,
),
),
);
}
}
There is now no longer a need to declare your fonts within your apps pubspec.yaml, or even have the style package(s) within the same project/repository as your implementing app.

Advantages and Disadvantages of static methods and functions

I know that functions are independent from any class and static methods are attached to a class. It looks like both of them achieve the same. This leads me to the following question: What are the advantages and disadvantages of them?
I want to create set of functions for offline PIN configuration like setPin, changePin, verifyPin. Do I have to wrap them inside a class as static methods or can I just create them as functions in a Dart file?
There are no clear "Advantages and Disadvantage" of static methods versus functions.
As you have correctly pointed out, the only difference is that static members are connected to a class.
class A {
static bool b() => false;
}
bool c() => true;
The only difference here would be that you need to access the static member via A.b and c can be accessed directly.
Static methods are not even inherited, meaning that class B extends A {} will not allow you to use B.b because b is a static member of A.
Having said that, #jamesdlin pointed out an article for writing good Dart design. This article describes that you should avoid creating classes with only static members and calls it bad design, i.e. not idiomatic Dart:
In idiomatic Dart, classes define kinds of objects. A type that is never instantiated is a code smell.
Coming back to your question, you should probably create your functions as top-level functions if they are not part of an object according to idiomatic Dart design based on the article from the Dart team.
However, you might consider changing the way you store your "PIN configuration" because this sounds like it would be ideal to store this information as an object.
I totally agree that just using classes with static methods is a bad design decision.
It enforces the OO principles but actually does not bring any benefit of OO.
If can not support inheritance and polymorphism then what is the point?
Functions on the other hand can bring more flexibility as dart supports standalone functions like javascript etc..
As an example lets look at a flutter class for creating a custom theme ..
This example is with a static getter..
import 'package:flutter/material.dart';
class CustomTheme {
static ThemeData get lightTheme {
return ThemeData(
primarySwatch: Colors.purple,
fontFamily: 'Quicksand',
errorColor: Colors.purple,
textTheme: ThemeData.light().textTheme.copyWith(
headline6: TextStyle(
fontFamily: 'OpenSans',
fontSize: 18,
fontWeight: FontWeight.bold,
),
button: TextStyle(
fontFamily: 'OpenSans',
color: Colors.white,
fontSize: 18,
),
),
accentColor: Colors.indigo,
appBarTheme: AppBarTheme(
elevation: 12,
textTheme: ThemeData.light().textTheme.copyWith(
headline6: TextStyle(
fontFamily: 'Quicksand-Bold',
color: Colors.white,
fontSize: 18,
),
),
),
);
}
}
Think about writing this with a function like this..
import 'package:flutter/material.dart';
ThemeData myCustomLightTheme() {
final ThemeData _lightTheme = ThemeData.light();
final _appBarTheme = AppBarTheme(
elevation: 12,
textTheme: _lightTheme.textTheme.copyWith(
headline6: TextStyle(
fontFamily: 'Quicksand-Bold',
color: Colors.white,
fontSize: 18,
),
),
);
final _textTheme = _lightTheme.textTheme.copyWith(
headline6: TextStyle(
fontFamily: 'OpenSans',
fontSize: 18,
fontWeight: FontWeight.bold,
),
button: TextStyle(
fontFamily: 'OpenSans',
color: Colors.white,
fontSize: 18,
),
);
return ThemeData(
primarySwatch: Colors.purple,
fontFamily: 'Quicksand',
errorColor: Colors.purple,
textTheme: _textTheme,
accentColor: Colors.indigo,
appBarTheme: _appBarTheme);
}