LCOV - code coverage report
Current view: top level - lib/design/form - list_selector_page.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 77 83 92.8 %
Date: 2024-10-04 11:12:13 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/herald/app_locale.dart';
       6             : import 'package:app_finance/_configs/design_type.dart';
       7             : import 'package:app_finance/design/form/list_selector_item.dart';
       8             : import 'package:app_finance/_configs/theme_helper.dart';
       9             : import 'package:app_finance/_ext/build_context_ext.dart';
      10             : import 'package:app_finance/_ext/color_ext.dart';
      11             : import 'package:app_finance/design/form/simple_input.dart';
      12             : import 'package:flutter/material.dart';
      13             : import 'package:flutter_grid_layout/flutter_grid_layout.dart';
      14             : import 'package:provider/provider.dart';
      15             : 
      16             : typedef FntSelectorCallback = Widget Function(
      17             :   List<ListSelectorItem> options,
      18             :   NavigatorState nav,
      19             : );
      20             : 
      21             : class ListSelectorPage<T extends Object?> extends StatefulWidget {
      22             :   final T? result;
      23             :   final List<ListSelectorItem> options;
      24             :   final String tooltip;
      25             :   final FntSelectorCallback? itemBuilder;
      26             : 
      27           1 :   const ListSelectorPage({
      28             :     super.key,
      29             :     required this.options,
      30             :     required this.tooltip,
      31             :     this.result,
      32             :     this.itemBuilder,
      33             :   });
      34             : 
      35           1 :   @override
      36           1 :   State<StatefulWidget> createState() => ListSelectorPageState<T>();
      37             : }
      38             : 
      39             : class ListSelectorPageState<T extends Object?> extends State<ListSelectorPage> {
      40             :   final controller = TextEditingController();
      41             :   late NavigatorState nav;
      42             :   dynamic result;
      43             :   List<ListSelectorItem> options = [];
      44             :   String value = '';
      45             : 
      46           1 :   @override
      47             :   void initState() {
      48           3 :     result = widget.result;
      49           3 :     options = widget.options;
      50          10 :     controller.addListener(() => controller.text.isEmpty && value.isEmpty ? () {} : setState(filter));
      51           1 :     super.initState();
      52             :   }
      53             : 
      54           1 :   @override
      55             :   void dispose() {
      56           2 :     controller.dispose();
      57           1 :     super.dispose();
      58             :   }
      59             : 
      60           1 :   void filter() {
      61           3 :     value = controller.text;
      62           8 :     options = widget.options.where((e) => e.match(value)).toList();
      63             :   }
      64             : 
      65           1 :   Widget itemBuilder(List<ListSelectorItem> options) =>
      66           2 :       widget.itemBuilder?.call(options, nav) ??
      67           1 :       ListView.builder(
      68           1 :         itemCount: options.length,
      69           1 :         itemBuilder: (BuildContext context, int index) {
      70           1 :           return ListTile(
      71           5 :             tileColor: index % 2 == 0 ? context.colorScheme.primary.withOpacity(0.05) : null,
      72           3 :             hoverColor: context.colorScheme.primary.withOpacity(0.15),
      73           2 :             title: options[index].suggest(context),
      74           4 :             onTap: () => nav.pop<T>(options[index] as T),
      75             :           );
      76             :         },
      77             :       );
      78             : 
      79           1 :   @override
      80             :   Widget build(BuildContext context) {
      81           1 :     final design = Provider.of<AppDesign>(context, listen: false);
      82           2 :     if (design.value == AppDesignType.germany) {
      83           0 :       options.sort((a, b) => a.name.compareTo(b.name));
      84             :     }
      85           1 :     final indent = ThemeHelper.getIndent();
      86           2 :     nav = Navigator.of(context);
      87           1 :     return Directionality(
      88             :       textDirection: TextDirection.ltr,
      89           1 :       child: Scaffold(
      90           3 :         appBar: AppBar(backgroundColor: context.colorScheme.primary, toolbarHeight: 0),
      91           1 :         body: Column(
      92           1 :           crossAxisAlignment: AppDesign.getAlignment(),
      93           1 :           mainAxisAlignment: AppDesign.getAlignment<MainAxisAlignment>(),
      94           1 :           children: [
      95           1 :             Container(
      96           2 :               padding: EdgeInsets.all(ThemeHelper.getIndent()),
      97             :               width: double.infinity,
      98           1 :               decoration: BoxDecoration(
      99           2 :                 color: context.colorScheme.surface,
     100           1 :                 boxShadow: [
     101           1 :                   BoxShadow(
     102           3 :                     color: context.colorScheme.onSurface.withOpacity(0.1),
     103             :                     offset: const Offset(0, 1),
     104             :                     blurRadius: 3,
     105             :                     spreadRadius: 1,
     106             :                   ),
     107           1 :                   BoxShadow(
     108           2 :                     color: context.colorScheme.surface,
     109             :                     offset: const Offset(0, 3),
     110             :                     blurRadius: 0,
     111             :                     spreadRadius: 0,
     112             :                   ),
     113             :                 ],
     114           5 :                 border: Border(bottom: BorderSide(width: 4, color: context.colorScheme.surface.withOpacity(0.2))),
     115             :               ),
     116           1 :               child: Container(
     117           1 :                 decoration: BoxDecoration(
     118           2 :                   color: context.colorScheme.surface,
     119           4 :                   border: Border.all(color: context.colorScheme.onSurface.withOpacity(0.1)),
     120           3 :                   borderRadius: BorderRadius.all(Radius.circular(indent / 2)),
     121             :                 ),
     122           1 :                 height: ThemeHelper.barHeight + indent,
     123             :                 width: double.infinity,
     124           2 :                 padding: EdgeInsets.all(indent / 2),
     125           1 :                 child: GridContainer(
     126           1 :                   alignment: AppDesign.getAlignment<MainAxisAlignment>(),
     127           3 :                   rows: [null, ThemeHelper.barHeight / 2, ThemeHelper.barHeight / 2, ThemeHelper.barHeight, indent],
     128             :                   // ignore: prefer_const_literals_to_create_immutables
     129           1 :                   columns: [ThemeHelper.barHeight],
     130           1 :                   children: [
     131           1 :                     GridItem(
     132             :                       start: const Size(0, 0),
     133             :                       end: const Size(2, 1),
     134           1 :                       child: SimpleInput(
     135           1 :                         controller: controller,
     136           2 :                         tooltip: widget.tooltip,
     137             :                         withLabel: true,
     138             :                         forceFocus: true,
     139           0 :                         onFieldSubmitted: (String value) =>
     140           0 :                             nav.pop<T>(widget.options.where((e) => e.match(value)).firstOrNull as T?),
     141             :                       ),
     142             :                     ),
     143           1 :                     GridItem(
     144             :                       start: const Size(1, 0),
     145             :                       end: const Size(3, 1),
     146           1 :                       child: IconButton(
     147           2 :                         tooltip: AppLocale.labels.clear,
     148           1 :                         style: ButtonStyle(
     149           1 :                           backgroundColor: WidgetStateProperty.all<Color>(
     150           6 :                             context.colorScheme.surface.mesh(context.colorScheme.primary.withOpacity(1), 0.1),
     151             :                           ),
     152             :                         ),
     153             :                         icon: const Icon(Icons.clear),
     154           0 :                         onPressed: () => nav.pop<T?>(null),
     155             :                       ),
     156             :                     ),
     157           1 :                     GridItem(
     158             :                       start: const Size(3, 0),
     159             :                       end: const Size(4, 1),
     160           1 :                       child: IconButton(
     161           2 :                         tooltip: AppLocale.labels.returnBack,
     162             :                         icon: const Icon(Icons.rotate_left_rounded),
     163           0 :                         onPressed: () => nav.pop<T?>(result as T?),
     164             :                       ),
     165             :                     ),
     166             :                   ],
     167             :                 ),
     168             :               ),
     169             :             ),
     170           1 :             if (result != null)
     171           1 :               ListTile(
     172           3 :                 tileColor: context.colorScheme.primary.withOpacity(0.15),
     173           3 :                 hoverColor: context.colorScheme.primary.withOpacity(0.20),
     174           8 :                 title: widget.options.where((e) => e.equal(result)).firstOrNull?.build(context) ?? ThemeHelper.emptyBox,
     175           0 :                 onTap: () => nav.pop<T>(result as T),
     176             :               ),
     177           5 :             Expanded(child: Padding(padding: EdgeInsets.all(indent), child: itemBuilder(options))),
     178             :           ],
     179             :         ),
     180             :       ),
     181             :     );
     182             :   }
     183             : }

Generated by: LCOV version 1.14