import 'dart:async'; import 'dart:io'; import 'package:currency_picker/currency_picker.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:flex_color_picker/flex_color_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; 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'; 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'; import 'package:prasule/views/home.dart'; import 'package:shared_preferences/shared_preferences.dart'; /// View that shows on first-time setup class SetupView extends StatefulWidget { /// View that shows on first-time setup const SetupView({super.key, this.newWallet = false}); /// We are only creating a new wallet, no first-time setup final bool newWallet; @override State createState() => _SetupViewState(); } class _SetupViewState extends State { 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 categories = []; final _nameController = TextEditingController(); final _balanceController = TextEditingController(text: "0.0"); @override void didChangeDependencies() { super.didChangeDependencies(); if (categories.isNotEmpty) return; _nameController.text = 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(() {}); } @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), ), ), ], ), 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 { if (_nameController.text.isEmpty) { unawaited( showMessage( AppLocalizations.of(context).errorEmptyName, context, ), ); return; } if (await WalletManager.exists(_nameController.text) && context.mounted) { unawaited( showMessage( AppLocalizations.of(context).walletExists, context, ), ); return; } final wallet = Wallet( name: _nameController.text, currency: _selectedCurrency, categories: categories, 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(), ), ), ); }, pages: [ PageViewModel( decoration: const PageDecoration(bodyAlignment: Alignment.center), titleWidget: Padding( padding: const EdgeInsets.all(8), child: Text( AppLocalizations.of(context).welcome, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), textAlign: TextAlign.center, ), ), bodyWidget: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.min, children: [ if (!widget.newWallet) Flexible( child: Text( AppLocalizations.of(context).welcomeAboutPrasule, textAlign: TextAlign.center, ), ), if (!widget.newWallet) const SizedBox( height: 8, ), Flexible( child: Text( AppLocalizations.of(context).welcomeInstruction, textAlign: TextAlign.center, ), ), ], ), ), PageViewModel( decoration: const PageDecoration(bodyAlignment: Alignment.center), titleWidget: Padding( padding: const EdgeInsets.all(8), child: Text( AppLocalizations.of(context).setupWalletNameCurrency, textAlign: TextAlign.center, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), ), bodyWidget: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ SizedBox( width: MediaQuery.of(context).size.width * 0.7, child: PlatformField(controller: _nameController), ), const SizedBox( height: 5, ), PlatformButton( text: AppLocalizations.of(context) .setupCurrency(_selectedCurrency.code), 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, ), controller: _balanceController, inputFormatters: [ FilteringTextInputFormatter.allow( RegExp(r'\d+[\.,]{0,1}\d{0,}'), ), ], prefix: Padding( padding: const EdgeInsets.only(right: 4), child: Text(_selectedCurrency.symbol), ), ), ), ], ), ), PageViewModel( decoration: const PageDecoration(bodyAlignment: Alignment.center), titleWidget: Padding( padding: const EdgeInsets.all(8), child: Text( AppLocalizations.of(context).setupCategoriesHeading, textAlign: TextAlign.center, style: const TextStyle( fontSize: 24, fontWeight: FontWeight.bold, ), ), ), bodyWidget: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( AppLocalizations.of(context).setupCategoriesEditHint, 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), ), 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 { final icon = await showIconPicker( context, iconPackModes: [ if (!Platform.isIOS && !Platform.isMacOS) IconPack.material, if (Platform.isIOS || Platform.isMacOS) IconPack.cupertino, ], // adaptiveDialog: true, ); if (icon != null) categories[i].icon = icon; final materialEnabled = (await SharedPreferences.getInstance()) .getBool("useMaterialYou") ?? false; if (!context.mounted) return; await showAdaptiveDialog( 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, ), child: Padding( padding: const EdgeInsets.all(8), child: Icon( categories[i].icon, color: categories[i] .color .calculateTextColor(), ), ), ), ), trailing: IconButton( icon: const Icon(Icons.cancel), onPressed: () { categories.removeAt(i); setState(() {}); }, ), title: GestureDetector( onTap: () { final controller = TextEditingController( text: categories[i].name, ); showAdaptiveDialog( 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, ), ), ), ), itemCount: categories.length, ), ), ], ), ), ], ), ), ), ); } }