Dynamic Iconfont Loading in Flutter: Update Icons Without Re‑Release
This guide explains how to implement a Flutter solution that dynamically loads and updates iconfont TTF files at runtime, covering the FontLoader API, remote font fetching, name‑to‑Unicode mapping, and a FutureBuilder‑based extension that refreshes icons without requiring a new app release.
Background
Flutter developers often use iconfont files to display vector icons instead of raster images, benefiting from smaller package size, scalability, and text styling. However, the traditional static approach requires a new app release whenever an icon needs to be changed, which is cumbersome and ineffective for older versions.
Traditional Static Usage
Iconfont files are downloaded from the iconfont website, placed in the assets/fonts directory, and referenced in pubspec.yaml:
fonts:
- family: IconFont
fonts:
- asset: assets/fonts/iconfont.ttfA Dart class (e.g., ZcyIcons) is generated to map each icon to an IconData constant. While the class can be accessed via a map, it only works for icons that have already been defined.
Dynamic Loading Solution
Step 1 – Load Remote TTF File
The Flutter FontLoader class enables runtime font loading. By fetching a remote TTF file, comparing its hash with the locally stored version, and loading it via FontLoader, the app can update its icon set without a new release.
class FontLoader {
...
void addFont(Future<ByteData> bytes) {
if (_loaded) throw StateError('FontLoader is already loaded');
_fontFutures.add(bytes.then((ByteData data) =>
Uint8List.view(data.buffer, data.offsetInBytes, data.lengthInBytes)));
}
Future<void> load() async {
if (_loaded) throw StateError('FontLoader is already loaded');
_loaded = true;
final loadFutures = _fontFutures.map((Future<Uint8List> f) =>
f.then<void>((Uint8List list) => loadFont(list, family)));
return Future.wait(loadFutures.toList());
}
}Key helper functions:
static Future<ByteData> httpFetchFontAndSaveToDevice(Uri fontUri) async {
http.Response response;
try {
response = await _httpClient.get(uri);
} catch (e) {
throw Exception('Failed to get font with url: ${fontUrl.path}');
}
if (response.statusCode == 200) {
return ByteData.view(response.bodyBytes.buffer);
} else {
throw Exception('Failed to download font with url: ${fontUrl.path}');
}
}
static Future<void> loadFontIfNecessary(ByteData loader, String fontFamilyToLoad) async {
assert(fontFamilyToLoad != null && loader != null);
if (_loadedFonts.contains(fontFamilyToLoad)) return;
_loadedFonts.add(fontFamilyToLoad);
try {
Future<ByteData> byteData = file_io.loadFontFromDeviceFileSystem(fontFamilyToLoad);
if (await byteData != null) {
return _loadFontByteData(fontFamilyToLoad, byteData);
}
byteData = loader();
if (await byteData != null) {
final fontLoader = FontLoader(familyWithVariantString);
fontLoader.addFont(byteData);
await fontLoader.load();
successLoadedFonts.add(familyWithVariantString);
}
} catch (e) {
_loadedFonts.remove(fontFamilyToLoad);
print('Error: unable to load font $fontFamilyToLoad because $e');
}
}Step 2 – Map Icon Names to Unicode CodePoints
The remote API returns a JSON mapping of icon names to their Unicode values. After downloading the TTF, the app builds a map ( _aliasMap) so that an icon can be retrieved by its logical name:
MyIcons.from(StringToInt(_aliasMap['tongzhi']));This eliminates the need for developers to look up hexadecimal code points manually.
Step 3 – Refresh Icons After Font Update
Even after the new font is loaded, existing Icon widgets do not automatically redraw. By extending Icon with a dynamic getter that wraps the widget in a FutureBuilder, the UI can be forced to rebuild when the font load completes.
extension DynamicIconExtension on Icon {
Widget get dynamic {
if (this.icon is! DynamicIconDataMixin) return this;
final mix = this.icon as DynamicIconDataMixin;
final loadedKey = UniqueKey();
return FutureBuilder(
future: mix.dynamicIconFont.loadedAsync,
builder: (context, snapshot) {
return KeyedSubtree(
key: snapshot.hasData ? loadedKey : null,
child: this,
);
},
);
}
}
// Usage example
Icon(MyIcons.from('')).dynamic;Capabilities Achieved
Dynamic modification of existing icons
Set icons by logical name or Unicode code point at runtime
Add new icons without rebuilding the app
The entire workflow is illustrated in the following diagram:
Conclusion
The core principle is leveraging FontLoader for runtime TTF loading, combined with a remote hash‑check, name‑to‑Unicode mapping, and a FutureBuilder ‑based widget extension to refresh icons instantly. This approach requires a solid understanding of Flutter’s font system, asynchronous programming, and widget rebuilding mechanics.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
政采云技术
ZCY Technology Team (Zero), based in Hangzhou, is a growth-oriented team passionate about technology and craftsmanship. With around 500 members, we are building comprehensive engineering, project management, and talent development systems. We are committed to innovation and creating a cloud service ecosystem for government and enterprise procurement. We look forward to your joining us.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
