Skip to content

Commit

Permalink
fix: #1478 - additional cache folder for tintable svg files (#1612)
Browse files Browse the repository at this point in the history
* fix: #1478 - additional cache folder for tintable svg files

New files:
* `arrow-top-right-thick.svg`: (tintable) optimized version of https://static.openfoodfacts.org/images/icons/dist/arrow-top-right-thick.svg
* `vegetarian.svg`: (tintable) version of vegetarian.svg, taken from https://static.openfoodfacts.org/images/icons/dist/vegetarian.svg and optimized

Moved files (from `assets/cache` to `assets/cacheTintable`):
* `activity-walking.svg`
* `agriculture.svg`
* `arrow-bottom-right-thick.svg`
* `car.svg`
* `egg.svg`
* `leaf.svg`
* `monkey_happy.svg`
* `monkey_uncertain.svg`
* `monkey_unhappy.svg`
* `packaging.svg`
* `palm-oil.svg`
* `public.svg`
* `scale-balance.svg`
* `transportation.svg`

Impacted files:
* `abstract_cache.dart`: refactored with new protected method `getFilename`
* `pubspec.yaml`: added an asset folder for tintable svg cached files
* `svg_async_asset.svg`: added comments about the new asset folder
* `svg_cache.dart`: now we use 2 cache folders - one for colored icons, one for tintable icons that need a color as parameter

* fix: #1478 - several asset cache folders

Deleted file:
* `abstract_async_asset.dart`

New file:
* `asset_cache_helper.dart`: Asset cache helper class

Impacted files:
* `abstract_cache.dart`: now we deal with several possible asset cache files (folders)
* `raster_async_asset.svg`: now a `StatefulWidget` that uses new class `AssetCacheHelper`
* `raster_cache.svg`: now uses new class `AssetCacheHelper`
* `svg_async_asset.svg`: now a `StatefulWidget` that uses new class `AssetCacheHelper`
* `svg_cache.dart`: now uses new class `AssetCacheHelper`
  • Loading branch information
monsieurtanuki authored Apr 24, 2022
1 parent b333d73 commit a3a02d1
Show file tree
Hide file tree
Showing 24 changed files with 167 additions and 83 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
File renamed without changes
1 change: 1 addition & 0 deletions packages/smooth_app/assets/cacheTintable/vegetarian.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

This file was deleted.

27 changes: 24 additions & 3 deletions packages/smooth_app/lib/cards/category_cards/abstract_cache.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,38 @@ abstract class AbstractCache extends StatelessWidget {
final double? height;
final bool displayAssetWhileWaiting;

/// Returns a list of possible related cached filenames.
@protected
String? getFullFilename() {
List<String> getCachedFilenames() {
final List<String> result = <String>[];
final String? filename = getFilename();
if (filename == null) {
return result;
}
result.add(getCacheFilename(filename));
return result;
}

/// Returns the path to the asset cached file (not tintable version).
@protected
String getCacheFilename(final String filename) => 'assets/cache/$filename';

/// Returns the path to the asset cached tintable file.
@protected
String getCacheTintableFilename(final String filename) =>
'assets/cacheTintable/$filename';

/// Returns the simple filename of the icon url (without the full path).
@protected
String? getFilename() {
if (iconUrl == null) {
return null;
}
final int position = iconUrl!.lastIndexOf('/');
if (position == -1) {
return null;
}
final String filename = iconUrl!.substring(position + 1);
return 'assets/cache/$filename';
return iconUrl!.substring(position + 1);
}

@protected
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:flutter/material.dart';

/// Asset cache helper class
class AssetCacheHelper {
const AssetCacheHelper(
this.cachedFilenames,
this.url, {
this.width,
this.height,
this.color,
});

/// Full asset names, e.g. 'assets/cache/ab-agriculture-biologique.74x90.svg'
final List<String> cachedFilenames;

/// URL (for debug purpose), e.g. https://static.openfoodfacts.org/images/lang/fr/labels/ab-agriculture-biologique.74x90.svg
final String url;

final double? width;
final double? height;
final Color? color;

Widget getEmptySpace() => SizedBox(
width: width ?? height,
height: height ?? width,
);

void notFound() =>
debugPrint('unexpected case: asset not found $cachedFilenames ($url)');

Exception loadException() =>
Exception('could not load any cached file ($cachedFilenames)');
}
Original file line number Diff line number Diff line change
@@ -1,38 +1,49 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:smooth_app/cards/category_cards/abstract_async_asset.dart';
import 'package:smooth_app/cards/category_cards/asset_cache_helper.dart';

/// Widget with async load of raster asset file (png, jpeg).
class RasterAsyncAsset extends AbstractAsyncAsset {
const RasterAsyncAsset(
final String fullFilename,
final String url, {
final double? width,
final double? height,
}) : super(
fullFilename,
url,
width: width,
height: height,
);
class RasterAsyncAsset extends StatefulWidget {
const RasterAsyncAsset(this.assetCacheHelper);

final AssetCacheHelper assetCacheHelper;

@override
State<RasterAsyncAsset> createState() => _RasterAsyncAssetState();
}

class _RasterAsyncAssetState extends State<RasterAsyncAsset> {
late final Future<ByteData> _loading = _load();

Future<ByteData> _load() {
for (final String cachedFilename
in widget.assetCacheHelper.cachedFilenames) {
try {
return rootBundle.load(cachedFilename);
} catch (e) {
//
}
}
throw widget.assetCacheHelper.loadException();
}

@override
Widget build(BuildContext context) => FutureBuilder<ByteData>(
future: rootBundle.load(fullFilename),
future: _loading,
builder: (BuildContext context, AsyncSnapshot<ByteData> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data != null) {
return Image.memory(
snapshot.data!.buffer.asUint8List(),
width: width,
height: height,
width: widget.assetCacheHelper.width,
height: widget.assetCacheHelper.height,
fit: BoxFit.contain,
);
} else {
notFound();
widget.assetCacheHelper.notFound();
}
}
return getEmptySpace();
return widget.assetCacheHelper.getEmptySpace();
},
);
}
15 changes: 11 additions & 4 deletions packages/smooth_app/lib/cards/category_cards/raster_cache.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:smooth_app/cards/category_cards/abstract_cache.dart';
import 'package:smooth_app/cards/category_cards/asset_cache_helper.dart';
import 'package:smooth_app/cards/category_cards/raster_async_asset.dart';

/// Widget that displays a png/jpeg from network (and cache while waiting).
Expand All @@ -18,8 +19,8 @@ class RasterCache extends AbstractCache {

@override
Widget build(BuildContext context) {
final String? fullFilename = getFullFilename();
if (fullFilename == null) {
final List<String> fullFilenames = getCachedFilenames();
if (fullFilenames.isEmpty) {
return getDefaultUnknown();
}
return Image.network(
Expand All @@ -36,8 +37,14 @@ class RasterCache extends AbstractCache {
return child;
}
return displayAssetWhileWaiting
? RasterAsyncAsset(fullFilename, iconUrl!,
width: width, height: height)
? RasterAsyncAsset(
AssetCacheHelper(
fullFilenames,
iconUrl!,
width: width,
height: height,
),
)
: getCircularProgressIndicator();
},
);
Expand Down
61 changes: 38 additions & 23 deletions packages/smooth_app/lib/cards/category_cards/svg_async_asset.dart
Original file line number Diff line number Diff line change
@@ -1,48 +1,63 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:smooth_app/cards/category_cards/abstract_async_asset.dart';
import 'package:smooth_app/cards/category_cards/asset_cache_helper.dart';

/// Widget with async load of SVG asset file
///
/// SVG files may need to be optimized before being stored in the cache folder.
/// SVG files may need to be optimized before being stored in the cache folders.
/// There are two cache folders:
/// * assets/cache, where most files should be put
/// * assets/cacheTintable, where only colorless files should be put
/// As an example, vegetarian.svg is in both folders:
/// * the assets/cache version has different colors - no color should be applied
/// * the assets/cacheTintable version works with a color applied to it
/// E.g. with https://jakearchibald.github.io/svgomg/
/// C.f. https://github.com/openfoodfacts/smooth-app/issues/52
class SvgAsyncAsset extends AbstractAsyncAsset {
const SvgAsyncAsset(
final String fullFilename,
final String url, {
final double? width,
final double? height,
this.color,
}) : super(
fullFilename,
url,
width: width,
height: height,
);
class SvgAsyncAsset extends StatefulWidget {
const SvgAsyncAsset(this.assetCacheHelper);

final Color? color;
final AssetCacheHelper assetCacheHelper;

@override
State<SvgAsyncAsset> createState() => _SvgAsyncAssetState();
}

class _SvgAsyncAssetState extends State<SvgAsyncAsset> {
late final Future<String> _loading = _load();

Future<String> _load() async {
for (final String cachedFilename
in widget.assetCacheHelper.cachedFilenames) {
try {
return await rootBundle.loadString(cachedFilename);
} catch (e) {
//
}
}
throw widget.assetCacheHelper.loadException();
}

@override
Widget build(BuildContext context) => FutureBuilder<String>(
future: rootBundle.loadString(fullFilename),
future: _loading,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data != null) {
return SvgPicture.string(
snapshot.data!,
width: width,
height: height,
color: color,
width: widget.assetCacheHelper.width,
height: widget.assetCacheHelper.height,
color: widget.assetCacheHelper.color,
fit: BoxFit.contain,
placeholderBuilder: (BuildContext context) => getEmptySpace(),
placeholderBuilder: (BuildContext context) =>
widget.assetCacheHelper.getEmptySpace(),
);
} else {
notFound();
widget.assetCacheHelper.notFound();
}
}
return getEmptySpace();
return widget.assetCacheHelper.getEmptySpace();
},
);
}
36 changes: 29 additions & 7 deletions packages/smooth_app/lib/cards/category_cards/svg_cache.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:smooth_app/cards/category_cards/abstract_cache.dart';
import 'package:smooth_app/cards/category_cards/asset_cache_helper.dart';
import 'package:smooth_app/cards/category_cards/svg_async_asset.dart';

/// Widget that displays a svg from network (and cache while waiting).
Expand All @@ -20,10 +21,29 @@ class SvgCache extends AbstractCache {

final Color? color;

@override
List<String> getCachedFilenames() {
final List<String> result = <String>[];
final String? filename = getFilename();
if (filename == null) {
return result;
}
final String cacheFilename = getCacheFilename(filename);
final String cacheTintableFilename = getCacheTintableFilename(filename);
if (color == null) {
result.add(cacheFilename);
result.add(cacheTintableFilename);
} else {
result.add(cacheTintableFilename);
result.add(cacheFilename);
}
return result;
}

@override
Widget build(BuildContext context) {
final String? fullFilename = getFullFilename();
if (fullFilename == null) {
final List<String> cachedFilenames = getCachedFilenames();
if (cachedFilenames.isEmpty) {
return getDefaultUnknown();
}
return SvgPicture.network(
Expand All @@ -34,11 +54,13 @@ class SvgCache extends AbstractCache {
fit: BoxFit.contain,
placeholderBuilder: (BuildContext context) => displayAssetWhileWaiting
? SvgAsyncAsset(
fullFilename,
iconUrl!,
width: width,
height: height,
color: color,
AssetCacheHelper(
cachedFilenames,
iconUrl!,
width: width,
height: height,
color: color,
),
)
: getCircularProgressIndicator(),
);
Expand Down
1 change: 1 addition & 0 deletions packages/smooth_app/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ flutter:
- assets/app/
- assets/cache/
- assets/categories/
- assets/cacheTintable/
- assets/data/
- assets/home/
- assets/labels/
Expand Down

0 comments on commit a3a02d1

Please sign in to comment.