import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_speed_dial/flutter_speed_dial.dart'; import 'package:flutter_tesseract_ocr/flutter_tesseract_ocr.dart'; import 'package:grouped_list/grouped_list.dart'; import 'package:image_picker/image_picker.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:intl/intl.dart'; import 'package:prasule/api/wallet.dart'; import 'package:prasule/api/walletmanager.dart'; import 'package:prasule/main.dart'; import 'package:prasule/network/tessdata.dart'; import 'package:prasule/pw/platformdialog.dart'; import 'package:prasule/views/create_entry.dart'; import 'package:prasule/views/settings/settings.dart'; import 'package:prasule/views/setup.dart'; class HomeView extends StatefulWidget { const HomeView({super.key}); @override State createState() => _HomeViewState(); } class _HomeViewState extends State { Wallet? selectedWallet; DateTime? prevDate; late String locale; @override void didChangeDependencies() { super.didChangeDependencies(); locale = Localizations.localeOf(context).languageCode; initializeDateFormatting(Localizations.localeOf(context).languageCode); loadWallet(); } void loadWallet() async { var wallets = await WalletManager.listWallets(); if (wallets.isEmpty && mounted) { Navigator.of(context).pushReplacement( MaterialPageRoute(builder: (c) => const SetupView())); return; } selectedWallet = await WalletManager.loadWallet(wallets.first); setState(() {}); } @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: SpeedDial( icon: Icons.add, activeIcon: Icons.close, children: [ SpeedDialChild( child: const Icon(Icons.edit), label: "Add new", onTap: () async { selectedWallet = await Navigator.of(context).push( MaterialPageRoute( builder: (c) => CreateEntryView(w: selectedWallet!), ), ); setState(() {}); }), SpeedDialChild( child: const Icon(Icons.camera_alt), label: "Add through camera", onTap: () async { final ImagePicker picker = ImagePicker(); final XFile? media = await picker.pickImage(source: ImageSource.camera); logger.i(media?.name); }, ), SpeedDialChild( child: const Icon(Icons.image), label: "Add through saved image", onTap: () async { var availableLanguages = await TessdataApi.getDownloadedData(); if (mounted) { var selectedLanguages = List.filled(availableLanguages.length, false); selectedLanguages[ availableLanguages.indexOf("eng.traineddata")] = true; showDialog( context: context, builder: (c) => PlatformDialog( title: "Select languages for OCR", content: Column( children: [ ...List.generate( availableLanguages.length, (index) => Row( children: [ Checkbox( value: selectedLanguages[index], onChanged: (value) { if (value == null || (selectedLanguages .where((element) => element) .length <= 1 && !value)) return; selectedLanguages[index] = value; setState(() {}); // todo: builder }, ), const SizedBox( width: 10, ), Text(availableLanguages[index].split(".").first) ], ), ) ], ), ), ); } }, ), ], ), appBar: AppBar( title: const Text("Home"), actions: [ PopupMenuButton( itemBuilder: (context) => ["Settings", "About"] .map((e) => PopupMenuItem(value: e, child: Text(e))) .toList(), onSelected: (value) { if (value == "Settings") { Navigator.of(context).push( MaterialPageRoute( builder: (context) => const SettingsView(), ), ); } else if (value == "About") { showAboutDialog( context: context, applicationLegalese: "©️ 2023 Matyáš Caras\nReleased under the GNU AGPL license version 3", applicationName: "Prašule"); } }, ) ], ), body: Center( child: SizedBox( width: MediaQuery.of(context).size.width * 0.9, height: MediaQuery.of(context).size.height, child: (selectedWallet == null) ? const Column( children: [ SizedBox( width: 40, height: 40, child: CircularProgressIndicator(), ) ], ) : (selectedWallet!.entries.isEmpty) ? const Column( children: [ Text( "No entries :(", style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, ), ), Text( "Add one using the floating action button.", ) ], ) : GroupedListView( groupHeaderBuilder: (element) => Text( DateFormat.yMMMM(locale).format(element.date), style: TextStyle( color: Theme.of(context).colorScheme.primary), ), elements: selectedWallet!.entries ..sort((a, b) => a.date.compareTo(b.date)), groupBy: (e) => DateFormat.yMMMM(locale).format(e.date), itemBuilder: (context, element) => ListTile( leading: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: Theme.of(context).colorScheme.secondary), child: Padding( padding: const EdgeInsets.all(8.0), child: Icon( element.category.icon, color: Theme.of(context).colorScheme.onSecondary, ), ), ), title: Text(element.name), subtitle: Text( "${element.amount} ${selectedWallet!.currency.symbol}"), ), ), ), ), ); } Future getLostData() async { final ImagePicker picker = ImagePicker(); final LostDataResponse response = await picker.retrieveLostData(); if (response.isEmpty) { return; } final List? files = response.files; if (files != null) { _handleLostFiles(files); } else { logger.e(response.exception); } } void _handleLostFiles(List files) { // TODO: implement } }