diff --git a/packages/google_fonts/CHANGELOG.md b/packages/google_fonts/CHANGELOG.md index 0d4e6102..1877d445 100644 --- a/packages/google_fonts/CHANGELOG.md +++ b/packages/google_fonts/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.3.1 - 2025-07-30 +### Changed +- Update AssetManifest to use the builtin Flutter API. + ## 6.3.0 - 2024-11-01 ### Added - `Anton SC` diff --git a/packages/google_fonts/lib/src/asset_manifest.dart b/packages/google_fonts/lib/src/asset_manifest.dart deleted file mode 100644 index a420a6e2..00000000 --- a/packages/google_fonts/lib/src/asset_manifest.dart +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 The Flutter team. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:convert' as convert; - -import 'package:flutter/foundation.dart'; -// TODO(andrewkolos): remove this after flutter adds its own AssetManifest API -// (see https://github.com/flutter/flutter/pull/119277) which will replace the -// one defined here. -// ignore: undefined_hidden_name -import 'package:flutter/services.dart' hide AssetManifest; - -/// A class to obtain and memoize the app's asset manifest. -/// -/// Used to check whether a font is provided as an asset. -class AssetManifest { - AssetManifest({this.enableCache = true}); - - static Future>?>? _jsonFuture; - - /// Whether the rootBundle should cache AssetManifest.json. - /// - /// Enabled by default. Should only be disabled during tests. - final bool enableCache; - - Future>?>? json() { - _jsonFuture ??= _loadAssetManifestJson(); - return _jsonFuture; - } - - Future>?> _loadAssetManifestJson() async { - try { - final jsonString = await rootBundle.loadString( - 'AssetManifest.json', - cache: enableCache, - ); - return _manifestParser(jsonString); - } catch (e) { - rootBundle.evict('AssetManifest.json'); - rethrow; - } - } - - static Future>?> _manifestParser(String? jsonData) { - if (jsonData == null) { - return SynchronousFuture(null); - } - final parsedJson = convert.json.decode(jsonData) as Map; - final parsedManifest = >{ - for (final entry in parsedJson.entries) - entry.key: (entry.value as List).cast(), - }; - return SynchronousFuture(parsedManifest); - } - - @visibleForTesting - static void reset() => _jsonFuture = null; -} diff --git a/packages/google_fonts/lib/src/google_fonts_base.dart b/packages/google_fonts/lib/src/google_fonts_base.dart index 55edbbe7..991d02d7 100755 --- a/packages/google_fonts/lib/src/google_fonts_base.dart +++ b/packages/google_fonts/lib/src/google_fonts_base.dart @@ -4,20 +4,10 @@ import 'package:crypto/crypto.dart'; import 'package:flutter/material.dart'; -// TODO(andrewkolos): The flutter framework wishes to add a new class named -// `AssetManifest` to its API (see https://github.com/flutter/flutter/pull/119277). -// However, doing so would break integration tests that utilize google_fonts due -// to name collision with the `AssetManifest` class that this package already -// defines (see https://github.com/flutter/flutter/pull/119273). -// Once the AssetManifest API is added to flutter, update this package to use it -// instead of the AssetManifest class this package defines and remove this `hide` -// and the ignore annotation. -// ignore: undefined_hidden_name -import 'package:flutter/services.dart' hide AssetManifest; +import 'package:flutter/services.dart'; import 'package:http/http.dart' as http; import '../google_fonts.dart'; -import 'asset_manifest.dart'; import 'file_io.dart' // Stubbed implementation by default. // Concrete implementation if File IO is available. if (dart.library.io) 'file_io_desktop_and_mobile.dart' as file_io; @@ -43,7 +33,7 @@ final Set> pendingFontFutures = {}; http.Client httpClient = http.Client(); @visibleForTesting -AssetManifest assetManifest = AssetManifest(); +AssetManifest? assetManifest; /// Creates a [TextStyle] that either uses the [fontFamily] for the requested /// GoogleFont, or falls back to the pre-bundled [fontFamily]. @@ -147,11 +137,9 @@ Future loadFontIfNecessary(GoogleFontsDescriptor descriptor) async { Future? byteData; // Check if this font can be loaded by the pre-bundled assets. - final assetManifestJson = await assetManifest.json(); + assetManifest ??= await AssetManifest.loadFromAssetBundle(rootBundle); final assetPath = _findFamilyWithVariantAssetPath( - descriptor.familyWithVariant, - assetManifestJson, - ); + descriptor.familyWithVariant, assetManifest?.listAssets()); if (assetPath != null) { byteData = rootBundle.load(assetPath); } @@ -298,20 +286,18 @@ int _computeMatch(GoogleFontsVariant a, GoogleFontsVariant b) { /// Returns the path of the font asset if found, otherwise an empty string. String? _findFamilyWithVariantAssetPath( GoogleFontsFamilyWithVariant familyWithVariant, - Map>? manifestJson, + List? manifestValues, ) { - if (manifestJson == null) return null; + if (manifestValues == null) return null; final apiFilenamePrefix = familyWithVariant.toApiFilenamePrefix(); - for (final assetList in manifestJson.values) { - for (final String asset in assetList) { - for (final matchingSuffix in ['.ttf', '.otf'].where(asset.endsWith)) { - final assetWithoutExtension = - asset.substring(0, asset.length - matchingSuffix.length); - if (assetWithoutExtension.endsWith(apiFilenamePrefix)) { - return asset; - } + for (final asset in manifestValues) { + for (final matchingSuffix in ['.ttf', '.otf'].where(asset.endsWith)) { + final assetWithoutExtension = + asset.substring(0, asset.length - matchingSuffix.length); + if (assetWithoutExtension.endsWith(apiFilenamePrefix)) { + return asset; } } } diff --git a/packages/google_fonts/pubspec.yaml b/packages/google_fonts/pubspec.yaml index 69b28f26..5519d3f9 100644 --- a/packages/google_fonts/pubspec.yaml +++ b/packages/google_fonts/pubspec.yaml @@ -1,6 +1,6 @@ name: google_fonts description: A Flutter package to use fonts from fonts.google.com. Supports HTTP fetching, caching, and asset bundling. -version: 6.3.0 +version: 6.3.1 repository: https://github.com/material-foundation/flutter-packages/tree/main/packages/google_fonts issue_tracker: https://github.com/material-foundation/flutter-packages/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_fonts%22 screenshots: diff --git a/packages/google_fonts/test/asset_manifest_test.dart b/packages/google_fonts/test/asset_manifest_test.dart deleted file mode 100644 index 1f7aa686..00000000 --- a/packages/google_fonts/test/asset_manifest_test.dart +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2020 The Flutter team. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:convert'; - -// ignore: undefined_hidden_name -import 'package:flutter/services.dart' hide AssetManifest; -import 'package:flutter_test/flutter_test.dart'; -import 'package:google_fonts/src/asset_manifest.dart'; - -const _fakeAssetManifestText = '{"value": ["fake"]}'; -var _assetManifestLoadCount = 0; - -late AssetManifest assetManifest; - -void main() { - setUpAll(() async { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMessageHandler('flutter/assets', (message) { - _assetManifestLoadCount++; - final Uint8List encoded = utf8.encoder.convert(_fakeAssetManifestText); - return Future.value(encoded.buffer.asByteData()); - }); - // Disable cache so that we can see if AssetManifest.json is requested more - // than once. - assetManifest = AssetManifest(enableCache: false); - }); - - tearDown(() async { - _assetManifestLoadCount = 0; - AssetManifest.reset(); - }); - - testWidgets('AssetManifest loads once when called multiple times in parallel', - (tester) async { - final manifestJsons = await Future.wait>?>([ - assetManifest.json()!, - assetManifest.json()!, - assetManifest.json()!, - ]); - _verifyAssetManifestLoadedOnce(); - manifestJsons.forEach(_verifyAssetManifestContent); - }); - - testWidgets( - 'AssetManifest loads once when called multiple times in parallel then multiple times in succession', - (tester) async { - final manifestJsons = await Future.wait>?>([ - assetManifest.json()!, - assetManifest.json()!, - assetManifest.json()!, - ]); - _verifyAssetManifestLoadedOnce(); - manifestJsons.forEach(_verifyAssetManifestContent); - - final manifestJson3 = await assetManifest.json(); - final manifestJson4 = await assetManifest.json(); - _verifyAssetManifestLoadedOnce(); - _verifyAssetManifestContent(manifestJson3); - _verifyAssetManifestContent(manifestJson4); - }); - - testWidgets('AssetManifest loads', (tester) async { - final manifestJson = await assetManifest.json(); - _verifyAssetManifestLoadedOnce(); - _verifyAssetManifestContent(manifestJson); - }); - - testWidgets( - 'AssetManifest loads once when called multiple times in succession', - (tester) async { - final manifestJson1 = await assetManifest.json(); - _verifyAssetManifestLoadedOnce(); - _verifyAssetManifestContent(manifestJson1); - - final manifestJson2 = await assetManifest.json(); - _verifyAssetManifestLoadedOnce(); - _verifyAssetManifestContent(manifestJson2); - }); -} - -void _verifyAssetManifestLoadedOnce() { - expect(_assetManifestLoadCount, 1); -} - -void _verifyAssetManifestContent(Map? manifestJson) { - expect(manifestJson!['value'], ['fake']); -} diff --git a/packages/google_fonts/test/generated_font_methods_test.dart b/packages/google_fonts/test/generated_font_methods_test.dart index 447e784f..06184146 100644 --- a/packages/google_fonts/test/generated_font_methods_test.dart +++ b/packages/google_fonts/test/generated_font_methods_test.dart @@ -3,16 +3,19 @@ // found in the LICENSE file. import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:google_fonts/src/asset_manifest.dart'; import 'package:google_fonts/src/google_fonts_base.dart'; import 'package:http/http.dart' as http; import 'package:mockito/mockito.dart'; class MockHttpClient extends Mock implements http.Client {} -class MockAssetManifest extends Mock implements AssetManifest {} +class MockAssetManifest extends Mock implements AssetManifest { + @override + List listAssets() => []; +} void main() { setUpAll(() { diff --git a/packages/google_fonts/test/google_fonts_text_style_test.dart b/packages/google_fonts/test/google_fonts_text_style_test.dart index 8f65790f..7c967155 100644 --- a/packages/google_fonts/test/google_fonts_text_style_test.dart +++ b/packages/google_fonts/test/google_fonts_text_style_test.dart @@ -4,9 +4,9 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:google_fonts/src/asset_manifest.dart'; import 'package:google_fonts/src/google_fonts_base.dart'; import 'package:google_fonts/src/google_fonts_descriptor.dart'; import 'package:google_fonts/src/google_fonts_variant.dart'; @@ -20,7 +20,10 @@ class MockHttpClient extends Mock implements http.Client { } } -class MockAssetManifest extends Mock implements AssetManifest {} +class MockAssetManifest extends Mock implements AssetManifest { + @override + List listAssets() => []; +} const _fakeResponse = 'fake response body - success'; // The number of bytes in _fakeResponse. diff --git a/packages/google_fonts/test/load_font_if_necessary_test.dart b/packages/google_fonts/test/load_font_if_necessary_test.dart index 0d99118c..530d5f75 100644 --- a/packages/google_fonts/test/load_font_if_necessary_test.dart +++ b/packages/google_fonts/test/load_font_if_necessary_test.dart @@ -2,11 +2,9 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/cupertino.dart'; -// ignore: undefined_hidden_name -import 'package:flutter/services.dart' hide AssetManifest; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:google_fonts/src/asset_manifest.dart'; import 'package:google_fonts/src/google_fonts_base.dart'; import 'package:google_fonts/src/google_fonts_descriptor.dart'; import 'package:google_fonts/src/google_fonts_family_with_variant.dart'; @@ -24,7 +22,12 @@ class MockHttpClient extends Mock implements http.Client { } } -class MockAssetManifest extends Mock implements AssetManifest {} +class MockAssetManifest extends Mock implements AssetManifest { + @override + List listAssets() { + return []; + } +} class FakePathProviderPlatform extends Fake with MockPlatformInterfaceMixin diff --git a/packages/google_fonts/test/load_font_if_necessary_with_local_fonts_test.dart b/packages/google_fonts/test/load_font_if_necessary_with_local_fonts_test.dart index 9bda2f96..2ecc61bd 100644 --- a/packages/google_fonts/test/load_font_if_necessary_with_local_fonts_test.dart +++ b/packages/google_fonts/test/load_font_if_necessary_with_local_fonts_test.dart @@ -2,8 +2,7 @@ import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; -// ignore: undefined_hidden_name -import 'package:flutter/services.dart' hide AssetManifest; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/src/google_fonts_base.dart'; @@ -22,6 +21,13 @@ class MockHttpClient extends Mock implements http.Client { } } +class MockAssetManifest extends Mock implements AssetManifest { + @override + List listAssets() { + return ['google_fonts/Foo-BlackItalic.ttf']; + } +} + class FakePathProviderPlatform extends Fake with MockPlatformInterfaceMixin implements PathProviderPlatform { @@ -59,6 +65,7 @@ void main() { setUp(() async { mockHttpClient = MockHttpClient(); httpClient = mockHttpClient; + assetManifest = MockAssetManifest(); GoogleFonts.config.allowRuntimeFetching = true; when(mockHttpClient.gets(any)).thenAnswer((_) async { return http.Response(_fakeResponse, 200);