flutter-packages
flutter-packages copied to clipboard
Add ability to preload fonts
Issue
When a Google font is used the first time, the text shows in the fallback font initially, and then shows the actual Google Font as shown in the attached recording.
This behavior appears in both cases:
- Font is available in assets and
GoogleFonts.config.allowRuntimeFetching = false. - Font is fetched from network/cache and not available in asset.
The recording is based on the second case in a Flutter Web app, and the font is most likely coming from cache. For the first case, the font-switch is quicker but it is still apparent.
https://user-images.githubusercontent.com/46427323/109412438-0da8a980-79b9-11eb-9779-e098d210985d.mov
Request It'd be nice to have an asynchronous function that can be called and waited for to preload the fonts for a given list of fonts or from all the fonts in the assets. This will allow avoiding this undesirable font-switch from showing up. An example API for the suggested request would be something like this:
final fonts = <String>['Caveat', 'Roboto', ....... , 'Lato'];
await GoogleFonts.preloadFonts(fonts);
// or
await GoogleFonts.preloadAssetFonts();
Additional Context
The following was previously suggested by @clocksmith in #103 but the issue is closed:
When fonts are coming from assets, they still need to be loaded by the FontLoader, which is very fast when loading from assets, but not necessarily done before the first screen. We might need to make a preload function available to support this case (something that calls loadFontIfNecessary).
- Would be very useful. Came here to create exactly the same issue.
I came to report the same. +1 👍
For this solution, would be it ok to block on the splash screen while the fonts are loaded, causing a slightly delayed start up time?
@clocksmith I think that would be the best.
No. This way if the app supports multiple languages, the splash screen will be dramatically delayed, even though the user does not necessarily needs all those fonts for multiple languages.
It would be much better if we had an async function using which we could preload specific fonts the way it is described in this issue.
I also found this issue when drawing some text to the canvas. And since the font was not loaded when the canvas was drawn, the font remains unchanged. Unless I triggered repainting the canvas, it shows the font properly.
I also found this issue when drawing some text to the canvas. And since the font was not loaded when the canvas was drawn, the font remains unchanged. Unless I triggered repainting the canvas, it shows the font properly.
This is the same issue as we are facing with google fonts in Flame too, since we are drawing the text directly on the canvas.
Wanted to create a same issue. Its a mandatory feature.
+1 👍
I also found this issue when drawing some text to the canvas. And since the font was not loaded when the canvas was drawn, the font remains unchanged. Unless I triggered repainting the canvas, it shows the font properly.
This is the same issue as we are facing with google fonts in Flame too, since we are drawing the text directly on the canvas.
Also having issues using it in Flame because it is not preloaded. But in our usecase we are not loading from assets and we are loading the fonts dynamically. It would be nice to await preloading specific fonts.
Something like
await GoogleFonts.getFontAsync()
Made a feature request here
@spydon seems to be working with flame components also and it is possible to make multiple calls from multiple components to preload a font at the same time.
same issue +1 currently I'am preloading assets including fonts like this, which seems to work for me:
Future<void> main() async {
final binding = WidgetsFlutterBinding.ensureInitialized();
binding.deferFirstFrame();
binding.addPostFrameCallback((_) async {
BuildContext context = binding.renderViewElement as BuildContext;
if (context != null) {
await precacheImage(const AssetImage("lib/assets/myImage.gif"), context);
Text(
"",
style: GoogleFonts.font1(),
);
Text(
"",
style: GoogleFonts.font2(),
);
}
binding.allowFirstFrame();
});
runApp(const MyApp());
}
Here is a workaround, in my case I can use it since I'm caching some TextPainter.layout results which can be easily wiped.
It is strongly discouraged to use this in a production environment, as if the font cannot be loaded, the event will not fire.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final fontsReady = systemFontsStream(fontsToLoad: 2).last;
GoogleFonts.oswald();
GoogleFonts.lato();
await fontsReady;
runApp(const MyApp());
}
Stream<int> systemFontsStream({int? fontsToLoad}) {
late StreamController<int> controller;
var loadedFonts = 0;
void onSystemFontsLoaded() {
loadedFonts++;
print('Fonts loaded: $loadedFonts');
controller.add(loadedFonts);
if (loadedFonts == fontsToLoad) {
controller.close();
}
}
void addListener() {
PaintingBinding.instance.systemFonts.addListener(onSystemFontsLoaded);
}
void removeListener() {
PaintingBinding.instance.systemFonts.removeListener(onSystemFontsLoaded);
}
controller = StreamController<int>(
onListen: addListener,
onPause: removeListener,
onResume: addListener,
onCancel: removeListener,
);
return controller.stream;
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
);
}
}
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
return Text(
'Hello, World!',
style: GoogleFonts.lato(),
);
}
}
Update: this will be done this quarter
Googlers, see go/preloading-fonts-flutter
@guidezpl quarter is almost up! haha j/k couldn't resist the saying. hope to see this implemented soon. thanks
Hello, Is there any way to do it while it is being implemented?
For example is there a way to know if a font was loaded in the FontLoader? I want to know if a google font is already loaded, just checking if the font family already exists, if not I know I should loaded and I wait a delay until is loaded.
Thank you
@RicardoMudinyane This is a freely available package I work on when I have time. If you are unsatisfied, I encourage you to contribute. Beyond that, I closed this with #195.