feat: localization
This commit is contained in:
parent
de8f57fcc8
commit
f8f40f6db6
10 changed files with 247 additions and 111 deletions
3
l10n.yaml
Normal file
3
l10n.yaml
Normal file
|
@ -0,0 +1,3 @@
|
|||
arb-dir: lib/l10n
|
||||
template-arb-file: app_en.arb
|
||||
output-localization-file: app_localizations.dart
|
92
lib/l10n/app_en.arb
Normal file
92
lib/l10n/app_en.arb
Normal file
|
@ -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"
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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<CreateEntryView> {
|
|||
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<CreateEntryView> {
|
|||
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<CreateEntryView> {
|
|||
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<CreateEntryView> {
|
|||
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<CreateEntryView> {
|
|||
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<CreateEntryView> {
|
|||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
const Text("Category"),
|
||||
Text(AppLocalizations.of(context)!.category),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
|
@ -155,13 +156,14 @@ class _CreateEntryViewState extends State<CreateEntryView> {
|
|||
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;
|
||||
|
|
|
@ -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<HomeView> {
|
|||
children: [
|
||||
SpeedDialChild(
|
||||
child: const Icon(Icons.edit),
|
||||
label: "Add new",
|
||||
label: AppLocalizations.of(context)!.addNew,
|
||||
onTap: () async {
|
||||
var sw = await Navigator.of(context).push<Wallet>(
|
||||
MaterialPageRoute(
|
||||
|
@ -78,7 +79,7 @@ class _HomeViewState extends State<HomeView> {
|
|||
}),
|
||||
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<HomeView> {
|
|||
),
|
||||
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<HomeView> {
|
|||
],
|
||||
),
|
||||
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<HomeView> {
|
|||
],
|
||||
)
|
||||
: (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<HomeView> {
|
|||
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<HomeView> {
|
|||
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<HomeView> {
|
|||
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<HomeView> {
|
|||
},
|
||||
child: const Text("Cancel")),
|
||||
],
|
||||
title: "Select languages for OCR",
|
||||
title: AppLocalizations.of(context)!.ocrSelect,
|
||||
content: Column(
|
||||
children: [
|
||||
...List.generate(
|
||||
|
|
|
@ -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<SettingsView> {
|
|||
@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<SettingsView> {
|
|||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
|
|
@ -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<TessdataListView> {
|
|||
@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<TessdataListView> {
|
|||
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<TessdataListView> {
|
|||
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<TessdataListView> {
|
|||
},
|
||||
),
|
||||
PlatformButton(
|
||||
text: "No",
|
||||
text: AppLocalizations.of(context)!.no,
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
|
@ -90,7 +91,8 @@ class _TessdataListViewState extends State<TessdataListView> {
|
|||
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<TessdataListView> {
|
|||
if (snapshot.hasError) {
|
||||
return const Text("Error");
|
||||
}
|
||||
return Text(
|
||||
"Download progress: ${snapshot.data} %");
|
||||
return Text(AppLocalizations.of(context)!
|
||||
.langDownloadProgress(snapshot.data!));
|
||||
},
|
||||
stream: progressStream.stream,
|
||||
),
|
||||
|
|
|
@ -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<SetupView> {
|
|||
"space_between_amount_and_symbol": false,
|
||||
"symbol_on_left": true,
|
||||
});
|
||||
var categories = <WalletCategory>[
|
||||
var categories = <WalletCategory>[];
|
||||
var name = "";
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
categories = [
|
||||
WalletCategory(
|
||||
name: "Health",
|
||||
name: AppLocalizations.of(context)!.categoryHealth,
|
||||
type: EntryType.expense,
|
||||
id: 1,
|
||||
icon: IconData(Icons.medical_information.codePoint,
|
||||
fontFamily: 'MaterialIcons'),
|
||||
),
|
||||
WalletCategory(
|
||||
name: "Car",
|
||||
name: AppLocalizations.of(context)!.categoryCar,
|
||||
type: EntryType.expense,
|
||||
id: 2,
|
||||
icon: IconData(Icons.car_repair.codePoint, fontFamily: 'MaterialIcons'),
|
||||
),
|
||||
WalletCategory(
|
||||
name: "Food",
|
||||
name: AppLocalizations.of(context)!.categoryFood,
|
||||
type: EntryType.expense,
|
||||
id: 3,
|
||||
icon: IconData(Icons.restaurant.codePoint, fontFamily: 'MaterialIcons'),
|
||||
),
|
||||
WalletCategory(
|
||||
name: "Travel",
|
||||
name: AppLocalizations.of(context)!.categoryTravel,
|
||||
type: EntryType.expense,
|
||||
id: 4,
|
||||
icon: IconData(Icons.train.codePoint, fontFamily: 'MaterialIcons'),
|
||||
),
|
||||
];
|
||||
var name = "";
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -73,15 +82,16 @@ class _SetupViewState extends State<SetupView> {
|
|||
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<SetupView> {
|
|||
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(
|
||||
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.")),
|
||||
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<SetupView> {
|
|||
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<SetupView> {
|
|||
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<SetupView> {
|
|||
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<SetupView> {
|
|||
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<SetupView> {
|
|||
}
|
||||
categories.add(
|
||||
WalletCategory(
|
||||
name: "Edit me",
|
||||
name: AppLocalizations.of(context)!
|
||||
.setupWalletNamePlaceholder,
|
||||
type: EntryType.expense,
|
||||
id: id,
|
||||
icon: IconData(Icons.question_mark.codePoint,
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue