prasule/lib/views/setup.dart

478 lines
20 KiB
Dart
Raw Permalink Normal View History

import 'dart:async';
2023-09-08 11:50:21 +02:00
import 'package:currency_picker/currency_picker.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:flex_color_picker/flex_color_picker.dart';
2023-09-08 11:50:21 +02:00
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
2023-09-08 11:50:21 +02:00
import 'package:flutter_iconpicker/flutter_iconpicker.dart';
import 'package:introduction_screen/introduction_screen.dart';
import 'package:prasule/api/category.dart';
import 'package:prasule/api/wallet.dart';
import 'package:prasule/api/wallet_manager.dart';
2023-09-08 11:50:21 +02:00
import 'package:prasule/pw/platformbutton.dart';
import 'package:prasule/pw/platformfield.dart';
import 'package:prasule/pw/platformroute.dart';
import 'package:prasule/util/show_message.dart';
import 'package:prasule/util/text_color.dart';
import 'package:prasule/util/utils.dart';
2023-09-08 11:50:21 +02:00
import 'package:prasule/views/home.dart';
import 'package:shared_preferences/shared_preferences.dart';
2023-09-08 11:50:21 +02:00
/// View that shows on first-time setup
2023-09-08 11:50:21 +02:00
class SetupView extends StatefulWidget {
/// View that shows on first-time setup
const SetupView({super.key, this.newWallet = false});
2023-09-08 11:50:21 +02:00
/// We are only creating a new wallet, no first-time setup
final bool newWallet;
2023-09-08 11:50:21 +02:00
@override
State<SetupView> createState() => _SetupViewState();
}
class _SetupViewState extends State<SetupView> {
var _selectedCurrency = Currency.from(
json: {
"code": "USD",
"name": "United States Dollar",
"symbol": r"$",
"flag": "USD",
"decimal_digits": 2,
"number": 840,
"name_plural": "US dollars",
"thousands_separator": ",",
"decimal_separator": ".",
"space_between_amount_and_symbol": false,
"symbol_on_left": true,
},
);
List<WalletCategory> categories = <WalletCategory>[];
String name = "";
2024-01-30 22:00:19 +01:00
final _balanceController = TextEditingController(text: "0.0");
2023-10-11 18:37:18 +02:00
@override
void didChangeDependencies() {
super.didChangeDependencies();
if (categories.isEmpty) {
name = AppLocalizations.of(context).setupNamePlaceholder;
categories = [
WalletCategory(
name: AppLocalizations.of(context).noCategory,
id: 0,
icon: IconData(
Icons.payments.codePoint,
fontFamily: 'MaterialIcons',
),
color: Theme.of(context).colorScheme.secondary,
),
WalletCategory(
name: AppLocalizations.of(context).categoryHealth,
id: 1,
icon: IconData(
Icons.medical_information.codePoint,
fontFamily: 'MaterialIcons',
),
color: Colors.red.shade700
.harmonizeWith(Theme.of(context).colorScheme.primary),
),
WalletCategory(
name: AppLocalizations.of(context).categoryCar,
id: 2,
icon:
IconData(Icons.car_repair.codePoint, fontFamily: 'MaterialIcons'),
color: Colors.purple
.harmonizeWith(Theme.of(context).colorScheme.primary),
),
WalletCategory(
name: AppLocalizations.of(context).categoryFood,
id: 3,
icon:
IconData(Icons.restaurant.codePoint, fontFamily: 'MaterialIcons'),
color: Colors.green.shade700
.harmonizeWith(Theme.of(context).colorScheme.primary),
),
WalletCategory(
name: AppLocalizations.of(context).categoryTravel,
id: 4,
icon: IconData(Icons.train.codePoint, fontFamily: 'MaterialIcons'),
color: Colors.orange.shade700
.harmonizeWith(Theme.of(context).colorScheme.primary),
),
];
setState(() {});
}
2023-10-11 18:37:18 +02:00
}
2023-09-08 11:50:21 +02:00
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(AppLocalizations.of(context).setup),
actions: [
Tooltip(
message: AppLocalizations.of(context).about,
child: IconButton(
onPressed: () {
showAbout(context);
},
icon: const Icon(Icons.info_outline),
),
),
],
),
2023-09-08 11:50:21 +02:00
body: Center(
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: IntroductionScreen(
dotsDecorator: DotsDecorator(
activeColor: Theme.of(context).colorScheme.primary,
),
showBackButton: true,
next: Text(AppLocalizations.of(context).next),
back: Text(AppLocalizations.of(context).back),
done: Text(AppLocalizations.of(context).finish),
onDone: () async {
2023-09-08 11:50:21 +02:00
if (name.isEmpty) {
unawaited(
showMessage(
AppLocalizations.of(context).errorEmptyName,
context,
),
);
return;
}
if (await WalletManager.exists(name) && context.mounted) {
unawaited(
showMessage(
AppLocalizations.of(context).walletExists,
context,
),
);
2023-09-08 11:50:21 +02:00
return;
}
final wallet = Wallet(
2024-01-30 22:00:19 +01:00
name: name,
currency: _selectedCurrency,
categories: categories,
2024-02-10 16:20:44 +01:00
starterBalance: double.parse(_balanceController.text),);
await WalletManager.saveWallet(wallet);
if (widget.newWallet && context.mounted) {
Navigator.of(context).pop();
return;
}
if (!context.mounted) return;
unawaited(
Navigator.of(context).pushReplacement(
platformRoute(
(c) => const HomeView(),
),
),
2023-09-08 11:50:21 +02:00
);
},
pages: [
PageViewModel(
decoration:
const PageDecoration(bodyAlignment: Alignment.center),
2023-10-11 18:37:18 +02:00
titleWidget: Padding(
padding: const EdgeInsets.all(8),
2023-09-08 11:50:21 +02:00
child: Text(
AppLocalizations.of(context).welcome,
2023-10-11 18:37:18 +02:00
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
2023-09-08 11:50:21 +02:00
textAlign: TextAlign.center,
),
),
2023-10-11 18:37:18 +02:00
bodyWidget: Column(
2023-09-08 11:50:21 +02:00
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
if (!widget.newWallet)
Flexible(
child: Text(
AppLocalizations.of(context).welcomeAboutPrasule,
2024-02-10 15:22:30 +01:00
textAlign: TextAlign.center,
),
),
if (!widget.newWallet)
const SizedBox(
2024-02-10 15:22:30 +01:00
height: 8,
),
2023-09-08 11:50:21 +02:00
Flexible(
2023-10-11 18:37:18 +02:00
child: Text(
AppLocalizations.of(context).welcomeInstruction,
2024-02-10 15:22:30 +01:00
textAlign: TextAlign.center,
),
2023-10-11 18:37:18 +02:00
),
2023-09-08 11:50:21 +02:00
],
),
),
PageViewModel(
decoration:
const PageDecoration(bodyAlignment: Alignment.center),
2023-10-11 18:37:18 +02:00
titleWidget: Padding(
padding: const EdgeInsets.all(8),
2023-09-08 11:50:21 +02:00
child: Text(
AppLocalizations.of(context).setupWalletNameCurrency,
2023-09-08 11:50:21 +02:00
textAlign: TextAlign.center,
2023-10-11 18:37:18 +02:00
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
2023-09-08 11:50:21 +02:00
),
),
bodyWidget: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
child: PlatformField(
controller: TextEditingController(
text:
AppLocalizations.of(context).setupNamePlaceholder,
),
2023-09-08 11:50:21 +02:00
onChanged: (t) {
name = t;
},
),
),
const SizedBox(
height: 5,
),
PlatformButton(
text: AppLocalizations.of(context)
2023-10-11 18:37:18 +02:00
.setupCurrency(_selectedCurrency.code),
2023-09-08 11:50:21 +02:00
onPressed: () {
showCurrencyPicker(
context: context,
onSelect: (currency) {
_selectedCurrency = currency;
setState(() {});
},
);
},
),
const SizedBox(
height: 5,
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.7,
child: PlatformField(
labelText:
AppLocalizations.of(context).setupStartingBalance,
keyboardType: const TextInputType.numberWithOptions(
decimal: true,
),
2024-01-30 22:00:19 +01:00
controller: _balanceController,
inputFormatters: [
FilteringTextInputFormatter.allow(
RegExp(r'\d+[\.,]{0,1}\d{0,}'),
),
],
prefix: Padding(
padding: const EdgeInsets.only(right: 4),
child: Text(_selectedCurrency.symbol),
),
),
),
2023-09-08 11:50:21 +02:00
],
),
),
PageViewModel(
decoration:
const PageDecoration(bodyAlignment: Alignment.center),
2023-10-11 18:37:18 +02:00
titleWidget: Padding(
padding: const EdgeInsets.all(8),
2023-09-08 11:50:21 +02:00
child: Text(
AppLocalizations.of(context).setupCategoriesHeading,
2023-09-08 11:50:21 +02:00
textAlign: TextAlign.center,
2023-10-11 18:37:18 +02:00
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
2023-09-08 11:50:21 +02:00
),
),
bodyWidget: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
2023-10-11 18:37:18 +02:00
Text(
AppLocalizations.of(context).setupCategoriesEditHint,
2023-09-08 11:50:21 +02:00
textAlign: TextAlign.center,
),
IconButton(
onPressed: () {
var id = 0;
while (categories
.where((element) => element.id == id)
.isNotEmpty) {
id++; // create unique ID
}
categories.add(
WalletCategory(
name: AppLocalizations.of(context)
.setupWalletNamePlaceholder,
id: id,
icon: IconData(
Icons.question_mark.codePoint,
fontFamily: 'MaterialIcons',
),
color: Colors.blueGrey.harmonizeWith(
Theme.of(context).colorScheme.primary,
),
),
);
setState(() {});
},
icon: const Icon(Icons.add),
),
2023-09-08 11:50:21 +02:00
SizedBox(
height: MediaQuery.of(context).size.height * 0.64,
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (context, i) => (i == 0)
? const SizedBox()
: ListTile(
leading: GestureDetector(
onTap: () async {
2024-02-09 17:01:18 +01:00
final icon = await showIconPicker(
context,
);
if (icon != null) categories[i].icon = icon;
final materialEnabled =
(await SharedPreferences.getInstance())
.getBool("useMaterialYou") ??
false;
if (!context.mounted) return;
2024-02-10 15:14:58 +01:00
await showAdaptiveDialog<void>(
context: context,
builder: (c) => AlertDialog.adaptive(
actions: [
PlatformButton(
text: AppLocalizations.of(context)
.done,
onPressed: () {
Navigator.of(c).pop();
},
),
],
title: Text(
AppLocalizations.of(context)
.pickColor,
),
content: Column(
children: [
ColorPicker(
pickersEnabled: {
ColorPickerType.wheel: true,
ColorPickerType.primary: false,
ColorPickerType.custom: false,
ColorPickerType.bw: false,
ColorPickerType.accent:
materialEnabled,
},
color: categories[i].color,
onColorChanged: (color) {
categories[i].color = color;
setState(() {});
},
),
],
),
),
);
setState(() {});
},
child: Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: categories[i].color,
2023-09-08 11:50:21 +02:00
),
child: Padding(
padding: const EdgeInsets.all(8),
child: Icon(
categories[i].icon,
color: categories[i]
.color
.calculateTextColor(),
),
2023-09-08 11:50:21 +02:00
),
),
),
trailing: IconButton(
icon: const Icon(Icons.cancel),
onPressed: () {
categories.removeAt(i);
setState(() {});
},
),
title: GestureDetector(
onTap: () {
final controller = TextEditingController(
text: categories[i].name,
);
2024-02-10 15:14:58 +01:00
showAdaptiveDialog<void>(
context: context,
builder: (c) => AlertDialog.adaptive(
actions: [
TextButton(
onPressed: () {
if (controller.text.isEmpty) {
return;
}
categories[i].name =
controller.text;
Navigator.of(context).pop();
},
child: Text(
AppLocalizations.of(context).ok,
),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
AppLocalizations.of(context)
.cancel,
),
),
],
title: Text(
AppLocalizations.of(context)
.setupCategoriesEditingName,
),
content: SizedBox(
width: 400,
child: PlatformField(
controller: controller,
),
),
),
);
},
child: Text(
categories[i].name,
style: const TextStyle(
fontWeight: FontWeight.bold,
),
),
),
),
2023-09-08 11:50:21 +02:00
itemCount: categories.length,
),
),
],
),
),
],
),
),
),
);
}
}