I have a custom made icon font .ttf which I use within my apps as IconData
, allowing for use the same way you would with Flutter's built-in material icons.
My custom font class:
class MyIcons {
MyIcons._();
static const iconFontFamily = 'MyIcons';
static const iconFontPackage = 'my_icon_package';
/// edit_outline
static const IconData edit_outline = IconData(0xe000, fontFamily: iconFontFamily, fontPackage: iconFontPackage);
// etc
}
Usage:
Icon(
MyIcons.edit_outline,
size: 24,
)
And all works well within the app. However now I am trying to generate golden test files to ensure my icons work as expected after I've updated the .ttf, but the icons are always only replaced with the test Ahem font squares.
If I use Flutter's default icons, and set uses-material-design: true
in the pubspec.yaml
, this allows the default icons to be rendered properly within the golden test files, however no matter what I try I cannot get my own icons to be rendered.
Other things I've tried and been unsuccessful with:
Is there a way to do this?
I ended up solving the gotchas with this thanks to a related question here.
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).
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,
);
}
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.