LCOV - code coverage report
Current view: top level - lib/pages/home - home_page.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 78 124 62.9 %
Date: 2024-10-04 11:09:33 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2023 The terCAD team. All rights reserved.
       2             : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
       3             : 
       4             : import 'package:app_finance/_classes/herald/app_design.dart';
       5             : import 'package:app_finance/_classes/storage/app_data.dart';
       6             : import 'package:app_finance/_classes/herald/app_locale.dart';
       7             : import 'package:app_finance/_classes/structure/currency/exchange.dart';
       8             : import 'package:app_finance/_classes/storage/app_preferences.dart';
       9             : import 'package:app_finance/_configs/screen_helper.dart';
      10             : import 'package:app_finance/_configs/theme_helper.dart';
      11             : import 'package:app_finance/_classes/structure/navigation/app_route.dart';
      12             : import 'package:app_finance/components/_core/components_builder.dart';
      13             : import 'package:app_finance/components/widgets/account_flow_chart.dart';
      14             : import 'package:app_finance/components/widgets/budget_forecast_chart.dart';
      15             : import 'package:app_finance/components/component_recent.dart';
      16             : import 'package:app_finance/components/widgets/bill_ytd_chart.dart';
      17             : import 'package:app_finance/pages/_interfaces/abstract_page_state.dart';
      18             : import 'package:app_finance/pages/home/home_edit_page.dart';
      19             : import 'package:app_finance/pages/start/start_page.dart';
      20             : import 'package:app_finance/design/wrapper/grid_layer.dart';
      21             : import 'package:app_finance/pages/home/widgets/init_tab.dart';
      22             : import 'package:app_finance/design/button/toolbar_button_widget.dart';
      23             : import 'package:app_finance/components/widgets/account_widget.dart';
      24             : import 'package:app_finance/components/widgets/bill_widget.dart';
      25             : import 'package:app_finance/components/widgets/budget_widget.dart';
      26             : import 'package:app_finance/pages/home/widgets/goal_widget.dart';
      27             : import 'package:flutter/foundation.dart';
      28             : import 'package:flutter/material.dart';
      29             : import 'package:flutter_markdown/flutter_markdown.dart';
      30             : import 'package:flutter_svg/flutter_svg.dart';
      31             : import 'package:intl/intl.dart' as intl;
      32             : import 'package:package_info_plus/package_info_plus.dart';
      33             : import 'package:provider/provider.dart';
      34             : 
      35             : class HomePage extends StatefulWidget {
      36           5 :   const HomePage({super.key});
      37             : 
      38           3 :   @override
      39           3 :   HomePageState createState() => HomePageState();
      40             : }
      41             : 
      42             : class HomePageState extends AbstractPageState<HomePage> {
      43             :   String? toExpand;
      44             :   bool isEditMode = false;
      45             :   late String version;
      46             : 
      47           3 :   @override
      48             :   initState() {
      49           3 :     super.initState();
      50           6 :     toExpand = AppPreferences.get(AppPreferences.prefExpand);
      51           6 :     version = AppPreferences.get(AppPreferences.prefVersion) ?? '';
      52           6 :     PackageInfo.fromPlatform().then((PackageInfo value) {
      53           0 :       if (version != value.version) {
      54           0 :         WidgetsBinding.instance.addPostFrameCallback((_) => showModalBottomSheet(
      55           0 :               context: context,
      56           0 :               builder: (BuildContext context) => buildReleaseHelper(context, version),
      57             :             ));
      58           0 :         AppPreferences.set(AppPreferences.prefVersion, value.version);
      59             :       }
      60             :     });
      61             :   }
      62             : 
      63           0 :   Widget buildReleaseHelper(BuildContext context, String version) => buildHelper(
      64             :         context,
      65             :         type: 'upgrade',
      66           0 :         builder: (context, snapshot) {
      67           0 :           if (snapshot.hasData) {
      68           0 :             String data = snapshot.data!;
      69           0 :             if (version.isNotEmpty) {
      70           0 :               data = data.split('### $version')[0];
      71             :             }
      72           0 :             return Directionality(
      73           0 :               textDirection: AppDesign.getAlignment<TextDirection>(),
      74           0 :               child: Markdown(data: data),
      75             :             );
      76             :           }
      77           0 :           return Container();
      78             :         },
      79             :       );
      80             : 
      81           0 :   @override
      82           0 :   String getTitle() => AppLocale.labels.appTitle;
      83             : 
      84           3 :   @override
      85             :   Widget buildBottomBar(BuildContext context, BoxConstraints constraints) {
      86           6 :     return ThemeHelper.isWearable ? ThemeHelper.emptyBox : super.buildBottomBar(context, constraints);
      87             :   }
      88             : 
      89           0 :   @override
      90             :   Widget? buildRightBar(BuildContext context, BoxConstraints constraints) {
      91           0 :     return ThemeHelper.isWearable ? null : super.buildRightBar(context, constraints);
      92             :   }
      93             : 
      94           3 :   @override
      95             :   Widget? getBarLeading(NavigatorState nav) {
      96           3 :     if (ScreenHelper.state().isWide) {
      97             :       return ThemeHelper.emptyBox;
      98             :     }
      99           3 :     return Builder(
     100           3 :       builder: (BuildContext context) {
     101           3 :         return ToolbarButtonWidget(
     102             :           icon: Icons.menu,
     103             :           color: Colors.white70,
     104           6 :           tooltip: AppLocale.labels.navigationTooltip,
     105           0 :           onPressed: () => Scaffold.of(context).openDrawer(),
     106             :         );
     107             :       },
     108             :     );
     109             :   }
     110             : 
     111           3 :   @override
     112             :   List<Widget> getBarActions(NavigatorState nav) {
     113           3 :     final isWide = ScreenHelper.state().isWide;
     114           3 :     return [
     115           3 :       ToolbarButtonWidget(
     116             :         isWide: isWide,
     117             :         icon: Icons.app_registration_outlined,
     118             :         color: Colors.white70,
     119           6 :         tooltip: AppLocale.labels.customizeTooltip,
     120           0 :         onPressed: () => setState(() => isEditMode = true),
     121             :       ),
     122           9 :       if (![TargetPlatform.iOS, TargetPlatform.macOS, TargetPlatform.android].contains(defaultTargetPlatform))
     123           0 :         ToolbarButtonWidget(
     124             :           isWide: isWide,
     125             :           icon: Icons.switch_access_shortcut_add_outlined,
     126             :           color: Colors.white70,
     127           0 :           tooltip: AppLocale.labels.subscriptionTooltip,
     128           0 :           onPressed: () => nav.pushNamed(AppRoute.subscriptionRoute),
     129             :         ),
     130             :     ];
     131             :   }
     132             : 
     133           3 :   @override
     134             :   Widget getBarTitle(BuildContext context, [bool isBottom = false]) {
     135           3 :     return SvgPicture.asset(
     136             :       'assets/images/fingrom.svg',
     137             :       height: isBottom ? 20 : 40,
     138             :       alignment: Alignment.centerLeft,
     139           6 :       semanticsLabel: AppLocale.labels.appTitle,
     140             :     );
     141             :   }
     142             : 
     143           3 :   @override
     144           6 :   String getButtonName() => AppLocale.labels.addMainTooltip;
     145             : 
     146           3 :   @override
     147             :   Widget buildButton(BuildContext context, BoxConstraints constraints) {
     148           3 :     NavigatorState nav = Navigator.of(context);
     149           3 :     return FloatingActionButton(
     150             :       heroTag: 'home_page',
     151           3 :       mini: ThemeHelper.isWearable,
     152           2 :       onPressed: () => nav.pushNamed(AppRoute.billAddRoute),
     153           3 :       tooltip: getButtonName(),
     154             :       child: const Icon(Icons.add),
     155             :     );
     156             :   }
     157             : 
     158           3 :   @override
     159             :   Widget build(BuildContext context) {
     160           6 :     Provider.of<AppLocale>(context, listen: false).updateState(context);
     161           3 :     if (isEditMode) {
     162           0 :       return HomeEditPage(callback: () => setState(() => isEditMode = false));
     163             :     }
     164           3 :     if (AppPreferences.get(AppPreferences.prefPrivacyPolicy) == null) {
     165             :       return const StartPage();
     166             :     }
     167           6 :     return Consumer<AppData>(builder: (context, appState, _) {
     168           3 :       state = appState;
     169           3 :       if (appState.isLoading) {
     170             :         return const InitTab();
     171             :       }
     172           3 :       return super.build(context);
     173             :     });
     174             :   }
     175             : 
     176           3 :   @override
     177             :   Widget buildContent(BuildContext context, BoxConstraints constraints) {
     178           3 :     final data = ComponentsBuilder.getData(context);
     179           0 :     if (data != null && data.isNotEmpty) {
     180           0 :       return ComponentsBuilder(data);
     181             :     }
     182           3 :     double indent = ThemeHelper.getIndent();
     183           3 :     EdgeInsets margin = EdgeInsets.only(top: indent);
     184           3 :     final countWidth = ThemeHelper.getWidthCount(constraints);
     185           3 :     final countHeight = ThemeHelper.getHeightCount(context, constraints);
     186           3 :     bool isVertical = countWidth == 1 && !ThemeHelper.isWearable;
     187           3 :     bool isWide = ScreenHelper.state().isWide;
     188           3 :     double width = ThemeHelper.getWidth(context, 3, constraints);
     189          12 :     double partWidth = width / countWidth - indent * (countWidth - 1);
     190             : 
     191           3 :     final billWidget = BillWidget(
     192             :       margin: margin,
     193          21 :       title: '${AppLocale.labels.billHeadline}, ${intl.DateFormat.MMMM(AppLocale.code).format(DateTime.now())}',
     194           6 :       state: state.get(AppDataType.bills),
     195             :       limit: 7,
     196             :       route: AppRoute.billRoute,
     197           6 :       tooltip: AppLocale.labels.billTooltip,
     198             :       width: isVertical ? width : partWidth,
     199             :       hasExpand: isVertical,
     200           3 :       toExpand: toExpand,
     201           0 :       callback: (v) => setState(() => toExpand = v),
     202             :     );
     203           3 :     final accountWidget = AccountWidget(
     204             :       margin: margin,
     205          15 :       title: '${AppLocale.labels.accountHeadline}, ${AppLocale.labels.total}',
     206           6 :       state: state.get(AppDataType.accounts),
     207             :       limit: 7,
     208             :       route: AppRoute.accountRoute,
     209           6 :       tooltip: AppLocale.labels.accountTooltip,
     210             :       width: isVertical ? width : partWidth,
     211             :       hasExpand: isVertical,
     212           3 :       toExpand: toExpand,
     213           0 :       callback: (v) => setState(() => toExpand = v),
     214           9 :     )..exchange = Exchange(store: super.state);
     215           3 :     final budgetWidget = BudgetWidget(
     216             :       margin: margin,
     217          15 :       title: '${AppLocale.labels.budgetHeadline}, ${AppLocale.labels.left}',
     218           6 :       state: state.get(AppDataType.budgets),
     219             :       limit: 7,
     220             :       route: AppRoute.budgetRoute,
     221           6 :       tooltip: AppLocale.labels.budgetTooltip,
     222             :       width: isVertical ? width : partWidth,
     223             :       hasExpand: isVertical,
     224           3 :       toExpand: toExpand,
     225           0 :       callback: (v) => setState(() => toExpand = v),
     226           9 :     )..exchange = Exchange(store: super.state);
     227             : 
     228           3 :     return GridLayer(
     229             :       padding: indent,
     230             :       width: width,
     231             :       crossAxisCount: countWidth,
     232             :       strategy: switch (countWidth) {
     233           3 :         4 => [
     234           0 :             [0, 6],
     235           0 :             [2, 5],
     236           0 :             [3, 4],
     237           0 :             [1, 7]
     238             :           ],
     239           3 :         3 => [
     240           0 :             [2],
     241           0 :             [3],
     242           0 :             [0, 1]
     243             :           ],
     244           6 :         2 => [
     245           3 :             [2, 3],
     246           3 :             [0, 1]
     247             :           ],
     248           0 :         _ => [
     249           0 :             [0, 1, 2, 3]
     250             :           ]
     251             :       },
     252           3 :       children: [
     253           3 :         countHeight > 3
     254             :             ? isWide
     255           0 :                 ? Expanded(
     256           0 :                     child: Padding(
     257           0 :                       padding: EdgeInsets.only(top: indent / 2),
     258             :                       child: const ComponentRecent({'type': ComponentRecentType.goals, 'count': 7}),
     259             :                     ),
     260             :                   )
     261           3 :                 : GoalWidget(
     262             :                     width: isVertical ? width : partWidth,
     263           6 :                     state: super.state.getList(AppDataType.goals),
     264             :                   )
     265             :             : ThemeHelper.emptyBox,
     266             :         billWidget,
     267             :         accountWidget,
     268             :         budgetWidget,
     269           0 :         () => const Expanded(
     270             :               child: Column(
     271             :                 children: [
     272             :                   Expanded(child: ThemeHelper.emptyBox),
     273             :                   AccountFlowChart(),
     274             :                   ThemeHelper.hIndent6x,
     275             :                 ],
     276             :               ),
     277             :             ),
     278           0 :         () => const Expanded(
     279             :               child: Column(
     280             :                 children: [
     281             :                   Expanded(child: ThemeHelper.emptyBox),
     282             :                   BudgetForecastChart(),
     283             :                   ThemeHelper.hIndent6x,
     284             :                 ],
     285             :               ),
     286             :             ),
     287           0 :         () => const Expanded(
     288             :               child: Column(
     289             :                 children: [
     290             :                   Expanded(child: ThemeHelper.emptyBox),
     291             :                   BillYtdChart(),
     292             :                   ThemeHelper.hIndent6x,
     293             :                 ],
     294             :               ),
     295             :             ),
     296           0 :         () => const Expanded(
     297             :               child: ComponentRecent({'type': ComponentRecentType.invoice, 'count': 7}),
     298             :             ),
     299             :       ],
     300             :     );
     301             :   }
     302             : }

Generated by: LCOV version 1.14