Compare commits
No commits in common. "0f8f9abcf42e4cfadc8cc9e59e503514cceefe87" and "75f561a19e0484e67e511dbf29601ec5f58544aa" have entirely different histories.
0f8f9abcf4
...
75f561a19e
@ -58,13 +58,7 @@ class MalformedValueException extends ClientException {
|
|||||||
: super('Key `$key` references a malformed value. ($value)');
|
: super('Key `$key` references a malformed value. ($value)');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Exception thrown when the parser fails.
|
|
||||||
class ParserException extends ClientException {
|
class ParserException extends ClientException {
|
||||||
ParserException(String message, StackTrace? stackTrace)
|
ParserException(String message, StackTrace? stackTrace)
|
||||||
: super('$message\n\n$stackTrace');
|
: super('$message\n\n$stackTrace');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Exception thrown when the i18n is not loaded.
|
|
||||||
class NotLoadedException extends ClientException {
|
|
||||||
NotLoadedException() : super('I18n not loaded.');
|
|
||||||
}
|
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
// Copyright (C) 2023 WYATT GROUP
|
|
||||||
// Please see the AUTHORS file for details.
|
|
||||||
//
|
|
||||||
// This program is free software: you can redistribute it and/or modify
|
|
||||||
// it under the terms of the GNU General Public License as published by
|
|
||||||
// the Free Software Foundation, either version 3 of the License, or
|
|
||||||
// any later version.
|
|
||||||
//
|
|
||||||
// This program is distributed in the hope that it will be useful,
|
|
||||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
// GNU General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU General Public License
|
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
|
|
||||||
class AssetsUtils {
|
|
||||||
/// Checks if the given [assetPath] is a local asset.
|
|
||||||
///
|
|
||||||
/// In fact, this method loads the asset and checks if it is null.
|
|
||||||
static Future<bool> assetExists(String assetPath) async {
|
|
||||||
final encoded = utf8.encoder.convert(
|
|
||||||
Uri(path: Uri.encodeFull(assetPath)).path,
|
|
||||||
);
|
|
||||||
final asset = await ServicesBinding.instance.defaultBinaryMessenger
|
|
||||||
.send('flutter/assets', encoded.buffer.asByteData());
|
|
||||||
|
|
||||||
return asset != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cleans the given [path] by removing all the consecutive slashes.
|
|
||||||
/// For example, `///a///b///c///` becomes `/a/b/c/`.
|
|
||||||
/// It also removes the first slash if it exists.
|
|
||||||
static String cleanPath(String path) =>
|
|
||||||
path.replaceAll(RegExp(r'\/+'), '/').replaceFirst(RegExp(r'^\/'), '');
|
|
||||||
}
|
|
@ -14,10 +14,11 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import 'package:flutter/services.dart' show AssetBundle, rootBundle;
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/services.dart' show ServicesBinding, rootBundle;
|
||||||
import 'package:wyatt_i18n/src/core/enums/format.dart';
|
import 'package:wyatt_i18n/src/core/enums/format.dart';
|
||||||
import 'package:wyatt_i18n/src/core/exceptions/exceptions.dart';
|
import 'package:wyatt_i18n/src/core/exceptions/exceptions.dart';
|
||||||
import 'package:wyatt_i18n/src/core/utils/assets_utils.dart';
|
|
||||||
import 'package:wyatt_i18n/src/domain/data_sources/i18n_data_source.dart';
|
import 'package:wyatt_i18n/src/domain/data_sources/i18n_data_source.dart';
|
||||||
|
|
||||||
/// {@template assets_file_data_source_impl}
|
/// {@template assets_file_data_source_impl}
|
||||||
@ -33,11 +34,10 @@ import 'package:wyatt_i18n/src/domain/data_sources/i18n_data_source.dart';
|
|||||||
/// {@endtemplate}
|
/// {@endtemplate}
|
||||||
class AssetsFileDataSourceImpl extends I18nDataSource {
|
class AssetsFileDataSourceImpl extends I18nDataSource {
|
||||||
/// {@macro assets_file_data_source_impl}
|
/// {@macro assets_file_data_source_impl}
|
||||||
AssetsFileDataSourceImpl({
|
const AssetsFileDataSourceImpl({
|
||||||
this.basePath = 'assets',
|
this.basePath = 'assets',
|
||||||
this.baseName = 'i18n',
|
this.baseName = 'i18n',
|
||||||
super.format = Format.arb,
|
super.format = Format.arb,
|
||||||
super.defaultLocale = 'en',
|
|
||||||
}) : super();
|
}) : super();
|
||||||
|
|
||||||
/// The folder where the i18n files are located.
|
/// The folder where the i18n files are located.
|
||||||
@ -46,23 +46,29 @@ class AssetsFileDataSourceImpl extends I18nDataSource {
|
|||||||
/// The name of the i18n files without the extension.
|
/// The name of the i18n files without the extension.
|
||||||
final String baseName;
|
final String baseName;
|
||||||
|
|
||||||
/// The asset bundle used to load the i18n files.
|
/// Checks if the given [assetPath] is a local asset.
|
||||||
final AssetBundle assetBundle = rootBundle;
|
///
|
||||||
|
/// In fact, this method loads the asset and checks if it is null.
|
||||||
|
Future<bool> assetExists(String assetPath) async {
|
||||||
|
final encoded = utf8.encoder.convert(
|
||||||
|
Uri(path: Uri.encodeFull(assetPath)).path,
|
||||||
|
);
|
||||||
|
final asset = await ServicesBinding.instance.defaultBinaryMessenger
|
||||||
|
.send('flutter/assets', encoded.buffer.asByteData());
|
||||||
|
|
||||||
|
return asset != null;
|
||||||
|
}
|
||||||
|
|
||||||
/// Tries to load the i18n file from the given [locale].
|
/// Tries to load the i18n file from the given [locale].
|
||||||
Future<String> _tryLoad(String? locale) async {
|
Future<String> _tryLoad(String? locale) async {
|
||||||
String? content;
|
String? content;
|
||||||
final ext = format.name;
|
final ext = format.name;
|
||||||
|
final path = locale == null
|
||||||
final path = AssetsUtils.cleanPath(
|
? '$basePath/$baseName.$ext'
|
||||||
locale == null
|
: '$basePath/$baseName.$locale.$ext';
|
||||||
? '$basePath/$baseName.$defaultLocale.$ext'
|
|
||||||
: '$basePath/$baseName.$locale.$ext',
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (await AssetsUtils.assetExists(path)) {
|
if (await assetExists(path)) {
|
||||||
content = await assetBundle.loadString(path);
|
content = await rootBundle.loadString(path);
|
||||||
}
|
}
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
content = null;
|
content = null;
|
||||||
@ -72,10 +78,7 @@ class AssetsFileDataSourceImpl extends I18nDataSource {
|
|||||||
/// default i18n file.
|
/// default i18n file.
|
||||||
if (content == null && locale != null) {
|
if (content == null && locale != null) {
|
||||||
try {
|
try {
|
||||||
final fallbackPath = AssetsUtils.cleanPath(
|
content = await rootBundle.loadString('$basePath/$baseName.$ext');
|
||||||
'$basePath/$baseName.$defaultLocale.$ext',
|
|
||||||
);
|
|
||||||
content = await assetBundle.loadString(fallbackPath);
|
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
throw SourceNotFoundException(path, format: format);
|
throw SourceNotFoundException(path, format: format);
|
||||||
}
|
}
|
||||||
@ -95,7 +98,7 @@ class AssetsFileDataSourceImpl extends I18nDataSource {
|
|||||||
Future<String> _tryLoadUri(Uri uri) async {
|
Future<String> _tryLoadUri(Uri uri) async {
|
||||||
String? content;
|
String? content;
|
||||||
try {
|
try {
|
||||||
content = await assetBundle.loadString(uri.toString());
|
content = await rootBundle.loadString(uri.toString());
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
throw SourceNotFoundException(uri.toString(), format: format);
|
throw SourceNotFoundException(uri.toString(), format: format);
|
||||||
}
|
}
|
||||||
|
@ -14,117 +14,21 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:wyatt_i18n/src/core/enums/format.dart';
|
import 'package:wyatt_i18n/src/core/enums/format.dart';
|
||||||
import 'package:wyatt_i18n/src/core/exceptions/exceptions.dart';
|
|
||||||
import 'package:wyatt_i18n/src/core/utils/assets_utils.dart';
|
|
||||||
import 'package:wyatt_i18n/src/domain/data_sources/i18n_data_source.dart';
|
import 'package:wyatt_i18n/src/domain/data_sources/i18n_data_source.dart';
|
||||||
|
|
||||||
/// {@template network_data_source_impl}
|
|
||||||
/// Implementation of [I18nDataSource] that loads i18n files from the network.
|
|
||||||
///
|
|
||||||
/// The [baseUri] is the base uri where the i18n files are located.
|
|
||||||
/// The [baseName] is the name of the i18n files without the extension.
|
|
||||||
/// The [fallbackAssetPath] is the path to the fallback i18n files.
|
|
||||||
/// The [format] is the format of the i18n files.
|
|
||||||
///
|
|
||||||
/// For example, if the i18n files are located at `https://example.com/i18n/`
|
|
||||||
/// and are named `i18n.en.arb`, `i18n.fr.arb`, etc., then the [baseUri] is
|
|
||||||
/// `https://example.com/i18n/` and the [baseName] is `i18n` and the
|
|
||||||
/// [format] is [Format.arb].
|
|
||||||
///
|
|
||||||
/// If the i18n file for the given locale is not found or if the fetch fails,
|
|
||||||
/// then the fallback i18n files are loaded from the [fallbackAssetPath] in
|
|
||||||
/// the root bundle.
|
|
||||||
///
|
|
||||||
/// For example, if the fallback i18n files are located in the `assets` and are
|
|
||||||
/// named `i18n.arb`, `i18n.en.arb`, `i18n.fr.arb`, etc., then the
|
|
||||||
/// [fallbackAssetPath] is `assets`.
|
|
||||||
/// {@endtemplate}
|
|
||||||
class NetworkDataSourceImpl extends I18nDataSource {
|
class NetworkDataSourceImpl extends I18nDataSource {
|
||||||
/// {@macro network_data_source_impl}
|
const NetworkDataSourceImpl({super.format = Format.arb}) : super();
|
||||||
const NetworkDataSourceImpl({
|
|
||||||
required this.baseUri,
|
@override
|
||||||
this.baseName = 'i18n',
|
Future<String> load({required String? locale}) {
|
||||||
this.fallbackAssetPath = 'assets',
|
// TODO(wyatt): implement load
|
||||||
super.format = Format.arb,
|
throw UnimplementedError();
|
||||||
super.defaultLocale = 'en',
|
|
||||||
}) : super();
|
|
||||||
|
|
||||||
/// The base uri where the i18n files are located.
|
|
||||||
final Uri baseUri;
|
|
||||||
|
|
||||||
/// The name of the i18n files without the extension.
|
|
||||||
final String baseName;
|
|
||||||
|
|
||||||
/// The path to the fallback i18n files.
|
|
||||||
///
|
|
||||||
/// This is used when the i18n file for the given locale is not found or
|
|
||||||
/// if the fetch fails.
|
|
||||||
final String fallbackAssetPath;
|
|
||||||
|
|
||||||
/// Tries to load the i18n file from the given [locale].
|
|
||||||
Future<String> _tryLoad(String? locale, {Uri? overrideUri}) async {
|
|
||||||
String? content;
|
|
||||||
final ext = format.name;
|
|
||||||
|
|
||||||
/// If the locale is null, then we try to load the default i18n file from
|
|
||||||
/// the fallback asset path.
|
|
||||||
/// Otherwise, we try to load the i18n file for the given locale from the
|
|
||||||
/// base uri.
|
|
||||||
final path = AssetsUtils.cleanPath(
|
|
||||||
locale == null
|
|
||||||
? '$fallbackAssetPath/$baseName.$defaultLocale.$ext'
|
|
||||||
: overrideUri?.toString() ?? '$baseUri/$baseName.$locale.$ext',
|
|
||||||
);
|
|
||||||
|
|
||||||
if (locale == null) {
|
|
||||||
/// Load the default i18n file from the fallback asset path.
|
|
||||||
try {
|
|
||||||
if (await AssetsUtils.assetExists(path)) {
|
|
||||||
content = await rootBundle.loadString(path);
|
|
||||||
}
|
|
||||||
} catch (_) {
|
|
||||||
content = null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/// Load the i18n file for the given locale from the base uri.
|
|
||||||
try {
|
|
||||||
final response = await http.get(Uri.parse(path));
|
|
||||||
if (response.statusCode >= 200 && response.statusCode < 300) {
|
|
||||||
content = response.body;
|
|
||||||
}
|
|
||||||
} catch (_) {
|
|
||||||
content = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If the i18n file is not found, then we try to load the
|
|
||||||
/// default i18n file.
|
|
||||||
if (content == null && locale != null) {
|
|
||||||
try {
|
|
||||||
final fallbackPath = AssetsUtils.cleanPath(
|
|
||||||
'$fallbackAssetPath/$baseName.$defaultLocale.$ext',
|
|
||||||
);
|
|
||||||
content = await rootBundle.loadString(fallbackPath);
|
|
||||||
} catch (_) {
|
|
||||||
throw SourceNotFoundException(path, format: format);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If the default i18n file is not found, then we throw an exception.
|
|
||||||
/// This case should happen only if the locale is null.
|
|
||||||
if (content == null) {
|
|
||||||
throw SourceNotFoundException(path, format: format);
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<String> load({required String? locale}) async => _tryLoad(locale);
|
Future<String> loadFrom(Uri uri) {
|
||||||
|
// TODO(wyatt): implement loadFrom
|
||||||
@override
|
throw UnimplementedError();
|
||||||
Future<String> loadFrom(Uri uri) async => _tryLoad(null, overrideUri: uri);
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,27 +15,18 @@
|
|||||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||||
|
import 'package:wyatt_i18n/src/core/utils/i18n_parser.dart';
|
||||||
import 'package:wyatt_i18n/wyatt_i18n.dart';
|
import 'package:wyatt_i18n/wyatt_i18n.dart';
|
||||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart' hide Option;
|
import 'package:wyatt_type_utils/wyatt_type_utils.dart' hide Option;
|
||||||
|
|
||||||
/// {@template i18n_repository_impl}
|
|
||||||
/// The default implementation of [I18nRepository].
|
|
||||||
/// {@endtemplate}
|
|
||||||
class I18nRepositoryImpl extends I18nRepository {
|
class I18nRepositoryImpl extends I18nRepository {
|
||||||
/// {@macro i18n_repository_impl}
|
|
||||||
I18nRepositoryImpl({
|
I18nRepositoryImpl({
|
||||||
required this.dataSource,
|
required this.dataSource,
|
||||||
}) : super();
|
}) : super();
|
||||||
|
|
||||||
/// The data source used to load the i18n file.
|
|
||||||
final I18nDataSource dataSource;
|
final I18nDataSource dataSource;
|
||||||
|
|
||||||
/// The current i18n instance.
|
|
||||||
I18n _i18n = const I18n.empty();
|
I18n _i18n = const I18n.empty();
|
||||||
|
|
||||||
@override
|
|
||||||
I18n get i18n => _i18n;
|
|
||||||
|
|
||||||
Future<I18n> _parse(
|
Future<I18n> _parse(
|
||||||
String content,
|
String content,
|
||||||
Parser<String, Map<String, dynamic>> parser, {
|
Parser<String, Map<String, dynamic>> parser, {
|
||||||
@ -138,19 +129,4 @@ class I18nRepositoryImpl extends I18nRepository {
|
|||||||
throw KeyNotFoundException(key, arguments);
|
throw KeyNotFoundException(key, arguments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Result<I18n, AppException> getI18n() =>
|
|
||||||
Result.conditional(!_i18n.isEmpty, _i18n, NotLoadedException());
|
|
||||||
|
|
||||||
@override
|
|
||||||
Result<String, AppException> getLocale() =>
|
|
||||||
Result.conditional(!_i18n.isEmpty, _i18n.locale, NotLoadedException());
|
|
||||||
|
|
||||||
@override
|
|
||||||
Result<void, AppException> setI18n(I18n i18n) {
|
|
||||||
_i18n = i18n;
|
|
||||||
|
|
||||||
return const Ok(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,33 +17,23 @@
|
|||||||
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
import 'package:wyatt_architecture/wyatt_architecture.dart';
|
||||||
import 'package:wyatt_i18n/src/core/enums/format.dart';
|
import 'package:wyatt_i18n/src/core/enums/format.dart';
|
||||||
|
|
||||||
/// {@template i18n_data_source}
|
|
||||||
/// Base class for i18n data sources.
|
/// Base class for i18n data sources.
|
||||||
///
|
///
|
||||||
/// This class is used to load i18n files from a source.
|
/// This class is used to load i18n files from a source.
|
||||||
/// It returns a [Future] that resolves to a [String] containing the i18n file
|
/// It returns a [Future] that resolves to a [String] containing the i18n file
|
||||||
/// contents.
|
/// contents.
|
||||||
/// {@endtemplate}
|
|
||||||
abstract class I18nDataSource extends BaseDataSource {
|
abstract class I18nDataSource extends BaseDataSource {
|
||||||
/// {@macro i18n_data_source}
|
const I18nDataSource({this.format = Format.arb});
|
||||||
const I18nDataSource({
|
|
||||||
this.format = Format.arb,
|
|
||||||
this.defaultLocale = 'en',
|
|
||||||
});
|
|
||||||
|
|
||||||
/// The format of the i18n file.
|
|
||||||
final Format format;
|
final Format format;
|
||||||
|
|
||||||
/// The default locale.
|
|
||||||
final String defaultLocale;
|
|
||||||
|
|
||||||
/// Loads the i18n file from the source.
|
/// Loads the i18n file from the source.
|
||||||
/// If [locale] is not `null`, it will load the file with the given [locale].
|
/// If [locale] is not `null`, it will load the file with the given [locale].
|
||||||
/// Otherwise, it will load the file from the default location.
|
/// Otherwise, it will load the file from the default location.
|
||||||
Future<String> load({required String? locale});
|
Future<String> load({required String? locale});
|
||||||
|
|
||||||
/// Loads the i18n file from the source.
|
/// Loads the i18n file from the source.
|
||||||
///
|
///
|
||||||
/// This method is used to load the i18n file from the given [Uri].
|
/// This method is used to load the i18n file from the given [Uri].
|
||||||
Future<String> loadFrom(Uri uri);
|
Future<String> loadFrom(Uri uri);
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,4 @@ class I18n extends Entity {
|
|||||||
bool containsKey(String key) => data.containsKey(key);
|
bool containsKey(String key) => data.containsKey(key);
|
||||||
|
|
||||||
dynamic operator [](String key) => data[key];
|
dynamic operator [](String key) => data[key];
|
||||||
|
|
||||||
bool get isEmpty => data.isEmpty && unparsedData.isEmpty && locale.isEmpty;
|
|
||||||
}
|
}
|
||||||
|
@ -19,18 +19,10 @@ import 'package:wyatt_i18n/src/core/utils/parser.dart';
|
|||||||
import 'package:wyatt_i18n/src/domain/entities/i18n.dart';
|
import 'package:wyatt_i18n/src/domain/entities/i18n.dart';
|
||||||
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
|
||||||
|
|
||||||
/// {@template i18n_repository}
|
|
||||||
/// Base class for i18n repositories.
|
/// Base class for i18n repositories.
|
||||||
///
|
|
||||||
/// This class is used to manage i18n files.
|
|
||||||
/// {@endtemplate}
|
|
||||||
abstract class I18nRepository extends BaseRepository {
|
abstract class I18nRepository extends BaseRepository {
|
||||||
/// {@macro i18n_repository}
|
|
||||||
const I18nRepository() : super();
|
const I18nRepository() : super();
|
||||||
|
|
||||||
/// The current i18n file.
|
|
||||||
I18n get i18n;
|
|
||||||
|
|
||||||
/// Loads the i18n file from the source.
|
/// Loads the i18n file from the source.
|
||||||
/// If [strict] is `true`, it will throw an NoLocaleException if the
|
/// If [strict] is `true`, it will throw an NoLocaleException if the
|
||||||
/// `@@locale` key is not found in the i18n file, otherwise it will
|
/// `@@locale` key is not found in the i18n file, otherwise it will
|
||||||
@ -52,26 +44,8 @@ abstract class I18nRepository extends BaseRepository {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/// Gets the translation for the given [key].
|
/// Gets the translation for the given [key].
|
||||||
///
|
|
||||||
/// If [arguments] is not `null`, it will replace the placeholders in the
|
|
||||||
/// translation with the given arguments.
|
|
||||||
Result<String, AppException> get(
|
Result<String, AppException> get(
|
||||||
String key, [
|
String key, [
|
||||||
Map<String, dynamic> arguments = const {},
|
Map<String, dynamic> arguments = const {},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
/// Sets the current i18n instance.
|
|
||||||
///
|
|
||||||
/// This method is used to set the current i18n instance.
|
|
||||||
Result<void, AppException> setI18n(I18n i18n);
|
|
||||||
|
|
||||||
/// Gets the current i18n instance.
|
|
||||||
///
|
|
||||||
/// This method is used to get the current i18n instance.
|
|
||||||
Result<I18n, AppException> getI18n();
|
|
||||||
|
|
||||||
/// Gets the current locale.
|
|
||||||
///
|
|
||||||
/// This method is used to get the current locale.
|
|
||||||
Result<String, AppException> getLocale();
|
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@ environment:
|
|||||||
dependencies:
|
dependencies:
|
||||||
collection: ^1.17.0
|
collection: ^1.17.0
|
||||||
flutter: {sdk: flutter}
|
flutter: {sdk: flutter}
|
||||||
http: ^0.13.5
|
|
||||||
intl: ^0.18.0
|
intl: ^0.18.0
|
||||||
path: ^1.8.0
|
path: ^1.8.0
|
||||||
petitparser: ^5.1.0
|
petitparser: ^5.1.0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user