l10n: typography fixes for arabic/persian

This commit is contained in:
Thibault Deckers 2024-05-19 01:28:26 +02:00
parent ea53420c17
commit dd6258d8ac
18 changed files with 108 additions and 53 deletions

3
devtools_options.yaml Normal file
View file

@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:

View file

@ -11,6 +11,7 @@ import 'package:aves/services/common/services.dart';
import 'package:aves/theme/text.dart'; import 'package:aves/theme/text.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/time_utils.dart'; import 'package:aves/utils/time_utils.dart';
import 'package:intl/intl.dart';
extension ExtraAvesEntryProps on AvesEntry { extension ExtraAvesEntryProps on AvesEntry {
bool get isValid => !isMissingAtPath && sizeBytes != 0 && width > 0 && height > 0; bool get isValid => !isMissingAtPath && sizeBytes != 0 && width > 0 && height > 0;
@ -51,9 +52,10 @@ extension ExtraAvesEntryProps on AvesEntry {
// text // text
String get resolutionText { String getResolutionText(String locale) {
final ws = width; final numberFormat = NumberFormat('0', locale);
final hs = height; final ws = numberFormat.format(width);
final hs = numberFormat.format(height);
return isRotated ? '$hs${AText.resolutionSeparator}$ws' : '$ws${AText.resolutionSeparator}$hs'; return isRotated ? '$hs${AText.resolutionSeparator}$ws' : '$ws${AText.resolutionSeparator}$hs';
} }

View file

@ -47,10 +47,11 @@ extension ExtraCoordinateFormat on CoordinateFormat {
final min = minDecimal.toInt(); final min = minDecimal.toInt();
final sec = (minDecimal - min) * 60; final sec = (minDecimal - min) * 60;
var minText = NumberFormat('0' * (minuteSecondPadding ? 2 : 1), locale).format(min); final degText = NumberFormat('0', locale).format(deg);
var secText = NumberFormat('${'0' * (minuteSecondPadding ? 2 : 1)}${secondDecimals > 0 ? '.${'0' * secondDecimals}' : ''}', locale).format(sec); final minText = NumberFormat('0' * (minuteSecondPadding ? 2 : 1), locale).format(min);
final secText = NumberFormat('${'0' * (minuteSecondPadding ? 2 : 1)}${secondDecimals > 0 ? '.${'0' * secondDecimals}' : ''}', locale).format(sec);
return '$deg° $minText $secText'; return '$degText° $minText $secText';
} }
static List<String> _toDecimal(AppLocalizations l10n, LatLng latLng) { static List<String> _toDecimal(AppLocalizations l10n, LatLng latLng) {

View file

@ -45,3 +45,13 @@ bool shouldUseNativeDigits(Locale? countrifiedLocale) {
return true; return true;
} }
} }
bool canHaveLetterSpacing(String locale) {
switch (locale) {
case 'ar':
case 'fa':
return false;
default:
return true;
}
}

View file

@ -1,5 +1,5 @@
import 'package:aves/model/device.dart'; import 'package:aves/model/device.dart';
import 'package:aves/ref/locales.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/about/policy_page.dart'; import 'package:aves/widgets/about/policy_page.dart';
import 'package:aves/widgets/common/basic/link_chip.dart'; import 'package:aves/widgets/common/basic/link_chip.dart';
@ -10,13 +10,6 @@ import 'package:flutter/material.dart';
class AppReference extends StatelessWidget { class AppReference extends StatelessWidget {
static const avesGithub = 'https://github.com/deckerst/aves'; static const avesGithub = 'https://github.com/deckerst/aves';
static const _appTitleStyle = TextStyle(
fontSize: 20,
fontWeight: FontWeight.normal,
letterSpacing: 1.0,
fontFeatures: [FontFeature.enable('smcp')],
);
const AppReference({super.key}); const AppReference({super.key});
@override @override
@ -38,27 +31,35 @@ class AppReference extends StatelessWidget {
} }
Widget _buildAvesLine(BuildContext context) { Widget _buildAvesLine(BuildContext context) {
final locale = context.l10n.localeName;
final textScaler = MediaQuery.textScalerOf(context); final textScaler = MediaQuery.textScalerOf(context);
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
AvesLogo( AvesLogo(
size: textScaler.scale(_appTitleStyle.fontSize!) * 1.3, size: textScaler.scale(_getAppTitleStyle(locale).fontSize!) * 1.3,
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
context.l10n.appName, context.l10n.appName,
style: _appTitleStyle, style: _getAppTitleStyle(locale),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
device.packageVersion, device.packageVersion,
style: _appTitleStyle, style: _getAppTitleStyle(locale),
), ),
], ],
); );
} }
TextStyle _getAppTitleStyle(String locale) => TextStyle(
fontSize: 20,
fontWeight: FontWeight.normal,
letterSpacing: canHaveLetterSpacing(locale) ? 1 : 0,
fontFeatures: const [FontFeature.enable('smcp')],
);
static List<Widget> buildLinks(BuildContext context) { static List<Widget> buildLinks(BuildContext context) {
final l10n = context.l10n; final l10n = context.l10n;
return [ return [

View file

@ -122,6 +122,9 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
} }
Widget _buildStep(int step, String text, String buttonText, VoidCallback onPressed) { Widget _buildStep(int step, String text, String buttonText, VoidCallback onPressed) {
final locale = context.l10n.localeName;
final numberFormat = NumberFormat.decimalPattern(locale);
final isMonochrome = settings.themeColorMode == AvesThemeColorMode.monochrome; final isMonochrome = settings.themeColorMode == AvesThemeColorMode.monochrome;
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 4), padding: const EdgeInsets.symmetric(vertical: 4),
@ -136,7 +139,7 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
)), )),
shape: BoxShape.circle, shape: BoxShape.circle,
), ),
child: Text('$step'), child: Text(numberFormat.format(step)),
), ),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded(child: Text(text)), Expanded(child: Text(text)),

View file

@ -218,6 +218,9 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final locale = context.l10n.localeName;
final percentFormat = NumberFormat.percentPattern(locale);
final theme = Theme.of(context); final theme = Theme.of(context);
final colorScheme = theme.colorScheme; final colorScheme = theme.colorScheme;
final progressColor = colorScheme.primary; final progressColor = colorScheme.primary;
@ -230,7 +233,6 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
final processedCount = processed.length.toDouble(); final processedCount = processed.length.toDouble();
final total = widget.itemCount; final total = widget.itemCount;
final percent = total == null || total == 0 ? 0.0 : min(1.0, processedCount / total); final percent = total == null || total == 0 ? 0.0 : min(1.0, processedCount / total);
final percentFormat = NumberFormat.percentPattern();
return FadeTransition( return FadeTransition(
opacity: _animation, opacity: _animation,
child: Stack( child: Stack(
@ -351,6 +353,9 @@ class _FeedbackMessageState extends State<_FeedbackMessage> with SingleTickerPro
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final locale = context.l10n.localeName;
final numberFormat = NumberFormat('0', locale);
final textScaler = MediaQuery.textScalerOf(context); final textScaler = MediaQuery.textScalerOf(context);
final theme = Theme.of(context); final theme = Theme.of(context);
final colorScheme = theme.colorScheme; final colorScheme = theme.colorScheme;
@ -388,7 +393,7 @@ class _FeedbackMessageState extends State<_FeedbackMessage> with SingleTickerPro
// because we cannot use the app context theme here // because we cannot use the app context theme here
foreground: widget.progressColor, foreground: widget.progressColor,
center: ChangeHighlightText( center: ChangeHighlightText(
'${(remainingDurationMillis / 1000).ceil()}', numberFormat.format((remainingDurationMillis / 1000).ceil()),
style: contentTextStyle.copyWith( style: contentTextStyle.copyWith(
shadows: [ shadows: [
Shadow( Shadow(

View file

@ -1,8 +1,9 @@
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/ref/locales.dart';
import 'package:aves/theme/colors.dart'; import 'package:aves/theme/colors.dart';
import 'package:aves/theme/themes.dart'; import 'package:aves/theme/themes.dart';
import 'package:aves/widgets/common/basic/text/outlined.dart'; import 'package:aves/widgets/common/basic/text/outlined.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/extensions/theme.dart'; import 'package:aves/widgets/common/extensions/theme.dart';
import 'package:aves/widgets/common/fx/highlight_decoration.dart'; import 'package:aves/widgets/common/fx/highlight_decoration.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
@ -40,7 +41,7 @@ class HighlightTitle extends StatelessWidget {
final style = TextStyle( final style = TextStyle(
shadows: shadows(context), shadows: shadows(context),
fontSize: fontSize, fontSize: fontSize,
letterSpacing: 1.0, letterSpacing: canHaveLetterSpacing(context.l10n.localeName) ? 1 : 0,
fontFeatures: const [FontFeature.enable('smcp')], fontFeatures: const [FontFeature.enable('smcp')],
); );

View file

@ -146,9 +146,11 @@ class _GeoMapState extends State<GeoMap> {
selector: (context, s) => s.mapStyle, selector: (context, s) => s.mapStyle,
builder: (context, mapStyle, child) { builder: (context, mapStyle, child) {
final isHeavy = ExtraEntryMapStyle.isHeavy(mapStyle); final isHeavy = ExtraEntryMapStyle.isHeavy(mapStyle);
final locale = context.l10n.localeName;
Widget _buildMarkerWidget(MarkerKey<AvesEntry> key) => ImageMarker( Widget _buildMarkerWidget(MarkerKey<AvesEntry> key) => ImageMarker(
key: key, key: key,
count: key.count, count: key.count,
locale: locale,
buildThumbnailImage: (extent) => ThumbnailImage( buildThumbnailImage: (extent) => ThumbnailImage(
entry: key.entry, entry: key.entry,
extent: extent, extent: extent,
@ -482,9 +484,11 @@ class _GeoMapState extends State<GeoMap> {
} else { } else {
markerEntry = geoEntry.entry!; markerEntry = geoEntry.entry!;
} }
final locale = context.l10n.localeName;
final markerLocation = LatLng(geoEntry.latitude!, geoEntry.longitude!); final markerLocation = LatLng(geoEntry.latitude!, geoEntry.longitude!);
Widget markerBuilder(BuildContext context) => ImageMarker( Widget markerBuilder(BuildContext context) => ImageMarker(
count: geoEntry.pointsSize, count: geoEntry.pointsSize,
locale: locale,
drawArrow: false, drawArrow: false,
buildThumbnailImage: (extent) => ThumbnailImage( buildThumbnailImage: (extent) => ThumbnailImage(
entry: markerEntry, entry: markerEntry,

View file

@ -1,4 +1,3 @@
import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/trash.dart'; import 'package:aves/model/filters/trash.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
@ -8,6 +7,7 @@ import 'package:aves/model/source/collection_source.dart';
import 'package:aves/model/source/location/country.dart'; import 'package:aves/model/source/location/country.dart';
import 'package:aves/model/source/location/place.dart'; import 'package:aves/model/source/location/place.dart';
import 'package:aves/model/source/tag.dart'; import 'package:aves/model/source/tag.dart';
import 'package:aves/ref/locales.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
@ -111,6 +111,9 @@ class _AppDrawerState extends State<AppDrawer> {
} }
Widget _buildHeader(BuildContext context) { Widget _buildHeader(BuildContext context) {
final l10n = context.l10n;
final locale = l10n.localeName;
Future<void> goTo(String routeName, WidgetBuilder pageBuilder) async { Future<void> goTo(String routeName, WidgetBuilder pageBuilder) async {
Navigator.maybeOf(context)?.pop(); Navigator.maybeOf(context)?.pop();
await Future.delayed(ADurations.drawerTransitionAnimation); await Future.delayed(ADurations.drawerTransitionAnimation);
@ -145,13 +148,13 @@ class _AppDrawerState extends State<AppDrawer> {
OutlinedText( OutlinedText(
textSpans: [ textSpans: [
TextSpan( TextSpan(
text: context.l10n.appName, text: l10n.appName,
style: const TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 38, fontSize: 38,
fontWeight: FontWeight.w300, fontWeight: FontWeight.w300,
letterSpacing: 1.0, letterSpacing: canHaveLetterSpacing(locale) ? 1 : 0,
fontFeatures: [FontFeature.enable('smcp')], fontFeatures: const [FontFeature.enable('smcp')],
), ),
), ),
], ],
@ -177,7 +180,7 @@ class _AppDrawerState extends State<AppDrawer> {
onPressed: () => goTo(AboutPage.routeName, (_) => const AboutPage()), onPressed: () => goTo(AboutPage.routeName, (_) => const AboutPage()),
style: drawerButtonStyle, style: drawerButtonStyle,
icon: const Icon(AIcons.info), icon: const Icon(AIcons.info),
label: Text(context.l10n.drawerAboutButton), label: Text(l10n.drawerAboutButton),
), ),
OutlinedButton.icon( OutlinedButton.icon(
// key is expected by test driver // key is expected by test driver
@ -185,7 +188,7 @@ class _AppDrawerState extends State<AppDrawer> {
onPressed: () => goTo(SettingsPage.routeName, (_) => const SettingsPage()), onPressed: () => goTo(SettingsPage.routeName, (_) => const SettingsPage()),
style: drawerButtonStyle, style: drawerButtonStyle,
icon: const Icon(AIcons.settings), icon: const Icon(AIcons.settings),
label: Text(context.l10n.drawerSettingsButton), label: Text(l10n.drawerSettingsButton),
), ),
], ],
), ),

View file

@ -6,6 +6,7 @@ import 'package:aves/model/settings/enums/home_page.dart';
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_lens.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/ref/locales.dart';
import 'package:aves/widgets/about/about_page.dart'; import 'package:aves/widgets/about/about_page.dart';
import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/collection/collection_page.dart';
import 'package:aves/widgets/common/basic/insets.dart'; import 'package:aves/widgets/common/basic/insets.dart';
@ -90,6 +91,9 @@ class _TvRailState extends State<TvRail> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final l10n = context.l10n;
final locale = l10n.localeName;
final navEntries = _getNavEntries(context); final navEntries = _getNavEntries(context);
return DirectionalSafeArea( return DirectionalSafeArea(
end: false, end: false,
@ -103,13 +107,13 @@ class _TvRailState extends State<TvRail> {
logo, logo,
const SizedBox(width: 16), const SizedBox(width: 16),
Text( Text(
context.l10n.appName, l10n.appName,
style: const TextStyle( style: TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 32, fontSize: 32,
fontWeight: FontWeight.w300, fontWeight: FontWeight.w300,
letterSpacing: 1.0, letterSpacing: canHaveLetterSpacing(locale) ? 1 : 0,
fontFeatures: [FontFeature.enable('smcp')], fontFeatures: const [FontFeature.enable('smcp')],
), ),
), ),
], ],

View file

@ -1,21 +1,24 @@
import 'package:aves/theme/styles.dart'; import 'package:aves/theme/styles.dart';
import 'package:aves/theme/themes.dart'; import 'package:aves/theme/themes.dart';
import 'package:aves/widgets/common/basic/text/outlined.dart'; import 'package:aves/widgets/common/basic/text/outlined.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/extensions/theme.dart'; import 'package:aves/widgets/common/extensions/theme.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
class LinearPercentIndicatorText extends StatelessWidget { class LinearPercentIndicatorText extends StatelessWidget {
final double percent; final double percent;
final percentFormat = NumberFormat.percentPattern();
LinearPercentIndicatorText({ const LinearPercentIndicatorText({
super.key, super.key,
required this.percent, required this.percent,
}); });
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final locale = context.l10n.localeName;
final percentFormat = NumberFormat.percentPattern(locale);
return OutlinedText( return OutlinedText(
textSpans: [ textSpans: [
TextSpan( TextSpan(

View file

@ -32,6 +32,7 @@ import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class BasicSection extends StatefulWidget { class BasicSection extends StatefulWidget {
@ -277,15 +278,6 @@ class _BasicInfoState extends State<_BasicInfo> {
AvesEntry get entry => widget.entry; AvesEntry get entry => widget.entry;
int get megaPixels => (entry.width * entry.height / 1000000).round();
// guess whether this is a photo, according to file type
bool get isPhoto => [MimeTypes.heic, MimeTypes.heif, MimeTypes.jpeg, MimeTypes.tiff].contains(entry.mimeType) || entry.isRaw;
bool get showMegaPixels => isPhoto && megaPixels > 0;
String get rasterResolutionText => '${entry.resolutionText}${showMegaPixels ? '$megaPixels MP' : ''}';
static const ownerPackageNamePropKey = 'owner_package_name'; static const ownerPackageNamePropKey = 'owner_package_name';
static const iconSize = 20.0; static const iconSize = 20.0;
@ -331,7 +323,7 @@ class _BasicInfoState extends State<_BasicInfo> {
l10n.viewerInfoLabelTitle: title, l10n.viewerInfoLabelTitle: title,
l10n.viewerInfoLabelDate: dateText, l10n.viewerInfoLabelDate: dateText,
if (entry.isVideo) ..._buildVideoRows(context), if (entry.isVideo) ..._buildVideoRows(context),
if (showResolution) l10n.viewerInfoLabelResolution: context.applyDirectionality(rasterResolutionText), if (showResolution) l10n.viewerInfoLabelResolution: context.applyDirectionality(getRasterResolutionText(locale)),
l10n.viewerInfoLabelSize: context.applyDirectionality(sizeText), l10n.viewerInfoLabelSize: context.applyDirectionality(sizeText),
if (!entry.trashed) l10n.viewerInfoLabelUri: entry.uri, if (!entry.trashed) l10n.viewerInfoLabelUri: entry.uri,
if (path != null) l10n.viewerInfoLabelPath: path, if (path != null) l10n.viewerInfoLabelPath: path,
@ -384,4 +376,20 @@ class _BasicInfoState extends State<_BasicInfo> {
), ),
]; ];
} }
String getRasterResolutionText(String locale) {
var s = entry.getResolutionText(locale);
// guess whether this is a photo, according to file type
final isPhoto = [MimeTypes.heic, MimeTypes.heif, MimeTypes.jpeg, MimeTypes.tiff].contains(entry.mimeType) || entry.isRaw;
if (isPhoto) {
final numberFormat = NumberFormat('0', locale);
final megaPixels = (entry.width * entry.height / 1000000).round();
if (megaPixels > 0) {
s += '${numberFormat.format(megaPixels)} MP';
}
}
return s;
}
} }

View file

@ -69,10 +69,13 @@ class _ColorSectionSliverState extends State<ColorSectionSliver> {
children: [ children: [
ColorIndicator(value: v.color), ColorIndicator(value: v.color),
const SizedBox(width: 8), const SizedBox(width: 8),
SelectableText( Directionality(
textDirection: TextDirection.ltr,
child: SelectableText(
'#${v.color.hex}', '#${v.color.hex}',
style: const TextStyle(fontFamily: 'monospace'), style: const TextStyle(fontFamily: 'monospace'),
), ),
),
], ],
), ),
), ),

View file

@ -29,7 +29,7 @@ class OverlayDateRow extends StatelessWidget {
final resolutionText = entry.isSvg final resolutionText = entry.isSvg
? entry.aspectRatioText ? entry.aspectRatioText
: entry.isSized : entry.isSized
? entry.resolutionText ? entry.getResolutionText(locale)
: ''; : '';
return Row( return Row(

View file

@ -4,10 +4,12 @@ import 'package:collection/collection.dart';
import 'package:custom_rounded_rectangle_border/custom_rounded_rectangle_border.dart'; import 'package:custom_rounded_rectangle_border/custom_rounded_rectangle_border.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart'; import 'package:flutter_map/flutter_map.dart';
import 'package:intl/intl.dart' as intl;
import 'package:latlong2/latlong.dart'; import 'package:latlong2/latlong.dart';
class ImageMarker extends StatelessWidget { class ImageMarker extends StatelessWidget {
final int? count; final int? count;
final intl.NumberFormat numberFormat;
final bool drawArrow; final bool drawArrow;
final Widget Function(double extent) buildThumbnailImage; final Widget Function(double extent) buildThumbnailImage;
@ -20,12 +22,13 @@ class ImageMarker extends StatelessWidget {
static const innerRadius = Radius.circular(outerBorderRadiusDim - outerBorderWidth); static const innerRadius = Radius.circular(outerBorderRadiusDim - outerBorderWidth);
static const innerBorderRadius = BorderRadius.all(innerRadius); static const innerBorderRadius = BorderRadius.all(innerRadius);
const ImageMarker({ ImageMarker({
super.key, super.key,
required this.count, required this.count,
required String locale,
this.drawArrow = true, this.drawArrow = true,
required this.buildThumbnailImage, required this.buildThumbnailImage,
}); }) : numberFormat = intl.NumberFormat.decimalPattern(locale);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -107,7 +110,7 @@ class ImageMarker extends StatelessWidget {
), ),
), ),
child: Text( child: Text(
'$count', numberFormat.format(count),
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
color: theme.colorScheme.onPrimary, color: theme.colorScheme.onPrimary,

View file

@ -102,7 +102,7 @@ packages:
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
intl: intl:
dependency: transitive dependency: "direct main"
description: description:
name: intl name: intl
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf

View file

@ -15,6 +15,7 @@ dependencies:
equatable: equatable:
fluster: fluster:
flutter_map: flutter_map:
intl:
latlong2: latlong2:
provider: provider: