From f8f40f6db6ee68e9751c10ae807b0b1cbc9b84f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maty=C3=A1=C5=A1=20Caras?= Date: Wed, 11 Oct 2023 18:37:18 +0200 Subject: [PATCH] feat: localization --- l10n.yaml | 3 + lib/l10n/app_en.arb | 92 +++++++++++++++++ lib/main.dart | 8 ++ lib/views/create_entry.dart | 22 +++-- lib/views/home.dart | 50 +++++----- lib/views/settings/settings.dart | 11 ++- lib/views/settings/tessdata_list.dart | 24 ++--- lib/views/setup.dart | 137 +++++++++++++++----------- pubspec.lock | 5 + pubspec.yaml | 6 +- 10 files changed, 247 insertions(+), 111 deletions(-) create mode 100644 l10n.yaml create mode 100644 lib/l10n/app_en.arb diff --git a/l10n.yaml b/l10n.yaml new file mode 100644 index 0000000..4e6692e --- /dev/null +++ b/l10n.yaml @@ -0,0 +1,3 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb new file mode 100644 index 0000000..ccc94c4 --- /dev/null +++ b/lib/l10n/app_en.arb @@ -0,0 +1,92 @@ +{ + "categoryHealth": "Health", + "categoryCar": "Car", + "categoryFood": "Food", + "categoryTravel": "Travel", + "next": "Next", + "back": "Back", + "finish": "Finish", + "errorEmptyName": "Name cannot be empty", + "welcome": "Welcome!", + "welcomeAboutPrasule": "Prašule is an expense tracker tool designed for people, who don't want to spend too much time filling in all the little details.", + "welcomeInstruction": "On this screen you will set up your 'wallet', in which you will track your expenses categorized under categories, which you can later set in the settings menu.", + "setupWalletNameCurrency": "Set your wallet's name and currency", + "setupNamePlaceholder": "Your awesome name here...", + "setupCurrency": "Currency: {currency}", + "@setupCurrency": { + "description": "Shows the currently selected currency on the setup screen", + "placeholders": { + "currency": { + "type": "String", + "example": "CZK" + } + } + }, + "setupCategoriesHeading": "Create categories", + "setupCategoriesEditHint": "Tap on the icon or name to edit it", + "ok": "Ok", + "cancel": "Cancel", + "setupCategoriesEditingName": "Editing name", + "setupWalletNamePlaceholder": "Edit me", + "addNew": "Add new", + "addCamera": "Add through camera", + "addGallery": "Add through saved image", + "home": "Home", + "settings": "Settings", + "about": "About", + "noEntries": "No entries :(", + "noEntriesSub": "Add one using the floating action button.", + "sureDialog": "Are you sure?", + "deleteSure": "Do you really want to delete this entry?", + "missingOcr": "You don't have any OCR language data downloaded!", + "download": "Download", + "ocrLoading": "Loading text from image, please wait a moment...", + "yes": "Yes", + "no": "No", + "ocrSelect": "Select languages for OCR", + "createEntry": "Create new entry", + "name": "Name", + "amount": "Amount", + "type": "Type", + "expense": "Expense", + "income": "Income", + "category": "Category", + "save": "Save", + "downloadedOcr": "View downloaded OCR data", + "downloadedOcrDesc": "This data is used by the OCR engine to recognize text from pictues", + "ocr": "OCR", + "ocrData": "OCR Data", + "downloaded": "Downloaded", + "deleteOcr": "Do you really want to delete '$lang' OCR data?\nYou will not be able to use these language data when scanning pictures.", + "@deleteOcr": { + "description": "Shown when a user wants to delete OCR data through settings", + "placeholders": { + "lang": { + "type": "String", + "example": "ces" + } + } + }, + "langDownloadDialog": "Downloading $lang, please wait...", + "@langDownloadDialog": { + "description": "Shown as a title of a dialog while downloading new OCR data", + "placeholders": { + "lang": { + "type": "String", + "example": "ces" + } + } + }, + "langDownloadProgress": "Download progress: $progress %", + "@langDownloadProgress": { + "description": "Progress percentage shown while downloading OCR data", + "placeholders": { + "progress":{ + "type":"num", + "example":"99.7" + } + } + }, + "addingFromOcr": "Add from OCR", + "license":"©️ 2023 Matyáš Caras\nReleased under the GNU AGPL license version 3" +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index ebfcfda..3a77cee 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -3,9 +3,11 @@ import 'dart:io'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:logger/logger.dart'; import 'package:prasule/util/color_schemes.g.dart'; import 'package:prasule/views/home.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; void main() { runApp(const MyApp()); @@ -22,6 +24,12 @@ class MyApp extends StatelessWidget { return (Platform.isAndroid) ? DynamicColorBuilder( builder: (light, dark) => MaterialApp( + localizationsDelegates: const [ + AppLocalizations.delegate, + ...GlobalMaterialLocalizations.delegates, + ...GlobalCupertinoLocalizations.delegates + ], + supportedLocales: AppLocalizations.supportedLocales, title: 'Prašule', theme: ThemeData( colorScheme: light ?? lightColorScheme, diff --git a/lib/views/create_entry.dart b/lib/views/create_entry.dart index bef5781..feaa4e2 100644 --- a/lib/views/create_entry.dart +++ b/lib/views/create_entry.dart @@ -7,6 +7,7 @@ import 'package:prasule/api/wallet.dart'; import 'package:prasule/api/walletmanager.dart'; import 'package:prasule/pw/platformbutton.dart'; import 'package:prasule/pw/platformfield.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class CreateEntryView extends StatefulWidget { final Wallet w; @@ -43,7 +44,7 @@ class _CreateEntryViewState extends State { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - title: const Text("Create new entry"), + title: Text(AppLocalizations.of(context)!.createEntry), ), body: SizedBox( width: MediaQuery.of(context).size.width, @@ -56,7 +57,7 @@ class _CreateEntryViewState extends State { SizedBox( width: MediaQuery.of(context).size.width * 0.8, child: PlatformField( - labelText: "Name", + labelText: AppLocalizations.of(context)!.name, controller: TextEditingController(text: newEntry.data.name), onChanged: (v) { newEntry.data.name = v; @@ -69,7 +70,7 @@ class _CreateEntryViewState extends State { SizedBox( width: MediaQuery.of(context).size.width * 0.8, child: PlatformField( - labelText: "Amount", + labelText: AppLocalizations.of(context)!.amount, controller: TextEditingController( text: newEntry.data.amount.toString()), keyboardType: @@ -99,8 +100,8 @@ class _CreateEntryViewState extends State { value: EntryType.expense, child: SizedBox( width: MediaQuery.of(context).size.width * 0.8 - 24, - child: const Text( - "Expense", + child: Text( + AppLocalizations.of(context)!.expense, ), ), ), @@ -108,7 +109,7 @@ class _CreateEntryViewState extends State { value: EntryType.income, child: SizedBox( width: MediaQuery.of(context).size.width * 0.8 - 24, - child: const Text("Income"), + child: Text(AppLocalizations.of(context)!.income), ), ), ], @@ -122,7 +123,7 @@ class _CreateEntryViewState extends State { const SizedBox( height: 20, ), - const Text("Category"), + Text(AppLocalizations.of(context)!.category), const SizedBox( height: 10, ), @@ -155,13 +156,14 @@ class _CreateEntryViewState extends State { height: 15, ), PlatformButton( - text: "Save", + text: AppLocalizations.of(context)!.save, onPressed: () { if (newEntry.data.name.isEmpty) { ScaffoldMessenger.of(context).clearSnackBars(); ScaffoldMessenger.of(context).showSnackBar( - const SnackBar( - content: Text("Name cannot be empty"), + SnackBar( + content: Text( + AppLocalizations.of(context)!.errorEmptyName), ), ); return; diff --git a/lib/views/home.dart b/lib/views/home.dart index 51d49ef..6aa127b 100644 --- a/lib/views/home.dart +++ b/lib/views/home.dart @@ -19,6 +19,7 @@ import 'package:prasule/views/multientry_creator.dart'; import 'package:prasule/views/settings/settings.dart'; import 'package:prasule/views/settings/tessdata_list.dart'; import 'package:prasule/views/setup.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class HomeView extends StatefulWidget { const HomeView({super.key}); @@ -64,7 +65,7 @@ class _HomeViewState extends State { children: [ SpeedDialChild( child: const Icon(Icons.edit), - label: "Add new", + label: AppLocalizations.of(context)!.addNew, onTap: () async { var sw = await Navigator.of(context).push( MaterialPageRoute( @@ -78,7 +79,7 @@ class _HomeViewState extends State { }), SpeedDialChild( child: const Icon(Icons.camera_alt), - label: "Add through camera", + label: AppLocalizations.of(context)!.addCamera, onTap: () async { final ImagePicker picker = ImagePicker(); final XFile? media = @@ -88,7 +89,7 @@ class _HomeViewState extends State { ), SpeedDialChild( child: const Icon(Icons.image), - label: "Add through saved image", + label: AppLocalizations.of(context)!.addGallery, onTap: () { startOcr(ImageSource.gallery); }, @@ -96,24 +97,24 @@ class _HomeViewState extends State { ], ), appBar: AppBar( - title: const Text("Home"), + title: Text(AppLocalizations.of(context)!.home), actions: [ PopupMenuButton( - itemBuilder: (context) => ["Settings", "About"] - .map((e) => PopupMenuItem(value: e, child: Text(e))) - .toList(), + itemBuilder: (context) => [ + AppLocalizations.of(context)!.settings, + AppLocalizations.of(context)!.about + ].map((e) => PopupMenuItem(value: e, child: Text(e))).toList(), onSelected: (value) { - if (value == "Settings") { + if (value == AppLocalizations.of(context)!.settings) { Navigator.of(context).push( MaterialPageRoute( builder: (context) => const SettingsView(), ), ); - } else if (value == "About") { + } else if (value == AppLocalizations.of(context)!.about) { showAboutDialog( context: context, - applicationLegalese: - "©️ 2023 Matyáš Caras\nReleased under the GNU AGPL license version 3", + applicationLegalese: AppLocalizations.of(context)!.license, applicationName: "Prašule"); } }, @@ -135,17 +136,17 @@ class _HomeViewState extends State { ], ) : (selectedWallet!.entries.isEmpty) - ? const Column( + ? Column( children: [ Text( - "No entries :(", - style: TextStyle( + AppLocalizations.of(context)!.noEntries, + style: const TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), Text( - "Add one using the floating action button.", + AppLocalizations.of(context)!.noEntriesSub, ) ], ) @@ -198,9 +199,10 @@ class _HomeViewState extends State { showDialog( context: context, builder: (cx) => PlatformDialog( - title: "Are you sure", - content: const Text( - "Do you really want to delete this entry?"), + title: + AppLocalizations.of(context)!.sureDialog, + content: Text( + AppLocalizations.of(context)!.deleteSure), actions: [ PlatformButton( text: "Yes", @@ -256,10 +258,9 @@ class _HomeViewState extends State { if (!mounted) return; ScaffoldMessenger.of(context).showSnackBar( SnackBar( - content: - const Text("You do not have any OCR language data downloaded"), + content: Text(AppLocalizations.of(context)!.missingOcr), action: SnackBarAction( - label: "Download", + label: AppLocalizations.of(context)!.download, onPressed: () { Navigator.of(context).push( MaterialPageRoute( @@ -299,9 +300,8 @@ class _HomeViewState extends State { if (!mounted) return; showDialog( context: context, - builder: (c) => const PlatformDialog( - title: - "Loading text from image, please wait a moment..."), + builder: (c) => PlatformDialog( + title: AppLocalizations.of(context)!.ocrLoading), barrierDismissible: false); var string = await FlutterTesseractOcr.extractText(media.path, language: selected, @@ -339,7 +339,7 @@ class _HomeViewState extends State { }, child: const Text("Cancel")), ], - title: "Select languages for OCR", + title: AppLocalizations.of(context)!.ocrSelect, content: Column( children: [ ...List.generate( diff --git a/lib/views/settings/settings.dart b/lib/views/settings/settings.dart index 69db0c5..7bd4a31 100644 --- a/lib/views/settings/settings.dart +++ b/lib/views/settings/settings.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:prasule/views/settings/tessdata_list.dart'; import 'package:settings_ui/settings_ui.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class SettingsView extends StatefulWidget { const SettingsView({super.key}); @@ -13,7 +14,7 @@ class _SettingsViewState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text("Settings")), + appBar: AppBar(title: Text(AppLocalizations.of(context)!.settings)), body: SettingsList( applicationType: ApplicationType.both, darkTheme: SettingsThemeData( @@ -23,15 +24,15 @@ class _SettingsViewState extends State { SettingsSection( tiles: [ SettingsTile.navigation( - title: const Text("View downloaded OCR data"), - description: const Text( - "This data is used by the OCR to recognise text from pictures"), + title: Text(AppLocalizations.of(context)!.downloadedOcr), + description: + Text(AppLocalizations.of(context)!.downloadedOcrDesc), onPressed: (context) => Navigator.of(context).push( MaterialPageRoute( builder: (c) => const TessdataListView())), ) ], - title: const Text("OCR"), + title: Text(AppLocalizations.of(context)!.ocr), ), ], ), diff --git a/lib/views/settings/tessdata_list.dart b/lib/views/settings/tessdata_list.dart index db96744..472eec1 100644 --- a/lib/views/settings/tessdata_list.dart +++ b/lib/views/settings/tessdata_list.dart @@ -7,6 +7,7 @@ import 'package:prasule/main.dart'; import 'package:prasule/network/tessdata.dart'; import 'package:prasule/pw/platformbutton.dart'; import 'package:prasule/pw/platformdialog.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class TessdataListView extends StatefulWidget { const TessdataListView({super.key}); @@ -28,7 +29,7 @@ class _TessdataListViewState extends State { @override Widget build(BuildContext context) { return Scaffold( - appBar: AppBar(title: const Text("OCR data")), + appBar: AppBar(title: Text(AppLocalizations.of(context)!.ocrData)), body: Center( child: SizedBox( width: MediaQuery.of(context).size.width * 0.9, @@ -49,8 +50,8 @@ class _TessdataListViewState extends State { title: Text(_tessdata[i].keys.first), trailing: TextButton( child: Text(_tessdata[i][_tessdata[i].keys.first]! - ? "Downloaded" - : "Download"), + ? AppLocalizations.of(context)!.downloaded + : AppLocalizations.of(context)!.download), onPressed: () async { var lang = _tessdata[i].keys.first; if (_tessdata[i][lang]!) { @@ -58,12 +59,12 @@ class _TessdataListViewState extends State { showDialog( context: context, builder: (context) => PlatformDialog( - title: "Warning", - content: Text( - "Do you want to delete '$lang' OCR data?\nYou will not be able to utilize this language when using the OCR functions."), + title: AppLocalizations.of(context)!.sureDialog, + content: Text(AppLocalizations.of(context)! + .deleteOcr(lang)), actions: [ PlatformButton( - text: "Yes", + text: AppLocalizations.of(context)!.yes, onPressed: () async { await TessdataApi.deleteData(lang); _tessdata[i][lang] = true; @@ -71,7 +72,7 @@ class _TessdataListViewState extends State { }, ), PlatformButton( - text: "No", + text: AppLocalizations.of(context)!.no, onPressed: () { Navigator.of(context).pop(); }, @@ -90,7 +91,8 @@ class _TessdataListViewState extends State { showDialog( context: context, builder: (c) => PlatformDialog( - title: "Downloading $lang, please wait...", + title: AppLocalizations.of(context)! + .langDownloadDialog(lang), content: StreamBuilder( builder: (context, snapshot) { if (snapshot.connectionState == @@ -100,8 +102,8 @@ class _TessdataListViewState extends State { if (snapshot.hasError) { return const Text("Error"); } - return Text( - "Download progress: ${snapshot.data} %"); + return Text(AppLocalizations.of(context)! + .langDownloadProgress(snapshot.data!)); }, stream: progressStream.stream, ), diff --git a/lib/views/setup.dart b/lib/views/setup.dart index 1ac0d04..c1aaba1 100644 --- a/lib/views/setup.dart +++ b/lib/views/setup.dart @@ -9,6 +9,7 @@ import 'package:prasule/pw/platformbutton.dart'; import 'package:prasule/pw/platformdialog.dart'; import 'package:prasule/pw/platformfield.dart'; import 'package:prasule/views/home.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class SetupView extends StatefulWidget { const SetupView({super.key}); @@ -31,34 +32,42 @@ class _SetupViewState extends State { "space_between_amount_and_symbol": false, "symbol_on_left": true, }); - var categories = [ - WalletCategory( - name: "Health", - type: EntryType.expense, - id: 1, - icon: IconData(Icons.medical_information.codePoint, - fontFamily: 'MaterialIcons'), - ), - WalletCategory( - name: "Car", - type: EntryType.expense, - id: 2, - icon: IconData(Icons.car_repair.codePoint, fontFamily: 'MaterialIcons'), - ), - WalletCategory( - name: "Food", - type: EntryType.expense, - id: 3, - icon: IconData(Icons.restaurant.codePoint, fontFamily: 'MaterialIcons'), - ), - WalletCategory( - name: "Travel", - type: EntryType.expense, - id: 4, - icon: IconData(Icons.train.codePoint, fontFamily: 'MaterialIcons'), - ), - ]; + var categories = []; var name = ""; + + @override + void initState() { + super.initState(); + categories = [ + WalletCategory( + name: AppLocalizations.of(context)!.categoryHealth, + type: EntryType.expense, + id: 1, + icon: IconData(Icons.medical_information.codePoint, + fontFamily: 'MaterialIcons'), + ), + WalletCategory( + name: AppLocalizations.of(context)!.categoryCar, + type: EntryType.expense, + id: 2, + icon: IconData(Icons.car_repair.codePoint, fontFamily: 'MaterialIcons'), + ), + WalletCategory( + name: AppLocalizations.of(context)!.categoryFood, + type: EntryType.expense, + id: 3, + icon: IconData(Icons.restaurant.codePoint, fontFamily: 'MaterialIcons'), + ), + WalletCategory( + name: AppLocalizations.of(context)!.categoryTravel, + type: EntryType.expense, + id: 4, + icon: IconData(Icons.train.codePoint, fontFamily: 'MaterialIcons'), + ), + ]; + setState(() {}); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -73,15 +82,16 @@ class _SetupViewState extends State { showNextButton: true, showBackButton: true, showDoneButton: true, - next: const Text("Next"), - back: const Text("Back"), - done: const Text("Finish"), + next: Text(AppLocalizations.of(context)!.next), + back: Text(AppLocalizations.of(context)!.back), + done: Text(AppLocalizations.of(context)!.finish), onDone: () { if (name.isEmpty) { ScaffoldMessenger.of(context) .clearSnackBars(); // TODO: iOS replacement - ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text("Name cannot be empty"))); + ScaffoldMessenger.of(context).showSnackBar(SnackBar( + content: + Text(AppLocalizations.of(context)!.errorEmptyName))); return; } var wallet = Wallet( @@ -100,39 +110,43 @@ class _SetupViewState extends State { PageViewModel( decoration: const PageDecoration(bodyAlignment: Alignment.center), - titleWidget: const Padding( - padding: EdgeInsets.all(8), + titleWidget: Padding( + padding: const EdgeInsets.all(8), child: Text( - "Welcome!", - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + AppLocalizations.of(context)!.welcome, + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.bold), textAlign: TextAlign.center, ), ), - bodyWidget: const Column( + bodyWidget: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ Flexible( - child: Text( - "Prašule is an expense tracker tool designed for people, who don't want to spend too much time filling in all the little details.")), - SizedBox( + child: Text( + AppLocalizations.of(context)!.welcomeAboutPrasule), + ), + const SizedBox( height: 5, ), Flexible( - child: Text( - "On this screen you will set up your 'wallet', in which you will track your expenses categorized under categories, which you can later set in the settings menu.")), + child: Text( + AppLocalizations.of(context)!.welcomeInstruction), + ), ], ), ), PageViewModel( decoration: const PageDecoration(bodyAlignment: Alignment.center), - titleWidget: const Padding( - padding: EdgeInsets.all(8), + titleWidget: Padding( + padding: const EdgeInsets.all(8), child: Text( - "Set your wallet's name and currency", + AppLocalizations.of(context)!.setupWalletNameCurrency, textAlign: TextAlign.center, - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.bold), ), ), bodyWidget: Column( @@ -141,7 +155,8 @@ class _SetupViewState extends State { SizedBox( width: MediaQuery.of(context).size.width * 0.7, child: PlatformField( - labelText: "Your awesome name here...", + labelText: + AppLocalizations.of(context)!.setupNamePlaceholder, onChanged: (t) { name = t; }, @@ -151,7 +166,8 @@ class _SetupViewState extends State { height: 5, ), PlatformButton( - text: "Currency: ${_selectedCurrency.code}", + text: AppLocalizations.of(context)! + .setupCurrency(_selectedCurrency.code), onPressed: () { showCurrencyPicker( context: context, @@ -168,19 +184,20 @@ class _SetupViewState extends State { PageViewModel( decoration: const PageDecoration(bodyAlignment: Alignment.center), - titleWidget: const Padding( - padding: EdgeInsets.all(8), + titleWidget: Padding( + padding: const EdgeInsets.all(8), child: Text( - "Create categories", + AppLocalizations.of(context)!.setupCategoriesHeading, textAlign: TextAlign.center, - style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + style: const TextStyle( + fontSize: 24, fontWeight: FontWeight.bold), ), ), bodyWidget: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ - const Text( - "Tap on the icon or name to edit it", + Text( + AppLocalizations.of(context)!.setupCategoriesEditHint, textAlign: TextAlign.center, ), SizedBox( @@ -232,16 +249,19 @@ class _SetupViewState extends State { categories[i].name = controller.text; Navigator.of(context).pop(); }, - child: const Text("Ok"), + child: Text( + AppLocalizations.of(context)!.ok), ), TextButton( onPressed: () { Navigator.of(context).pop(); }, - child: const Text("Cancel"), + child: Text( + AppLocalizations.of(context)!.cancel), ), ], - title: "Editing name", + title: AppLocalizations.of(context)! + .setupCategoriesEditingName, content: SizedBox( width: 400, child: @@ -270,7 +290,8 @@ class _SetupViewState extends State { } categories.add( WalletCategory( - name: "Edit me", + name: AppLocalizations.of(context)! + .setupWalletNamePlaceholder, type: EntryType.expense, id: id, icon: IconData(Icons.question_mark.codePoint, diff --git a/pubspec.lock b/pubspec.lock index 898b0ca..d15d564 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -387,6 +387,11 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.3" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_plugin_android_lifecycle: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7300743..da8059e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -45,12 +45,14 @@ dependencies: flutter_iconpicker: ^3.2.4 dynamic_color: ^1.6.6 introduction_screen: ^3.1.11 - intl: ^0.18.1 + intl: any grouped_list: ^5.1.2 flutter_speed_dial: ^7.0.0 image_picker: ^1.0.1 flutter_tesseract_ocr: ^0.4.23 flutter_slidable: ^3.0.0 + flutter_localizations: + sdk: flutter dev_dependencies: flutter_test: @@ -91,7 +93,7 @@ flutter_launcher_icons: # The following section is specific to Flutter packages. flutter: - + generate: true # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class.