LCOV - code coverage report
Current view: top level - lib/widgets/form_widget - cross_reference_form_widget.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 132 132 100.0 %
Date: 2021-11-04 14:59:29 Functions: 0 0 -

          Line data    Source code
       1             : part of apptive_grid_form_widgets;
       2             : 
       3             : /// FormComponent Widget to display a [CrossReferenceFormComponent]
       4             : class CrossReferenceFormWidget extends StatefulWidget {
       5             :   /// Creates a [Checkbox] to display a boolean value contained in [component]
       6           2 :   const CrossReferenceFormWidget({
       7             :     Key? key,
       8             :     required this.component,
       9           2 :   }) : super(key: key);
      10             : 
      11             :   /// Component this Widget should reflect
      12             :   final CrossReferenceFormComponent component;
      13             : 
      14           1 :   @override
      15             :   _CrossReferenceFormWidgetState createState() =>
      16           1 :       _CrossReferenceFormWidgetState();
      17             : }
      18             : 
      19             : class _CrossReferenceFormWidgetState extends State<CrossReferenceFormWidget> {
      20             :   Grid? _grid;
      21             :   dynamic _error;
      22             : 
      23             :   final LinkedScrollControllerGroup _scrollControllerGroup =
      24             :       LinkedScrollControllerGroup();
      25             :   final _controllers = <String, ScrollController>{};
      26             :   ScrollController? _headerController;
      27             :   final _keys = <String, GlobalKey<_RowMenuItemState>>{};
      28             : 
      29           1 :   @override
      30             :   void didChangeDependencies() {
      31           1 :     super.didChangeDependencies();
      32           2 :     if (_grid == null && _error == null) {
      33           1 :       _loadGrid();
      34             :     }
      35             :   }
      36             : 
      37           1 :   @override
      38             :   void dispose() {
      39           3 :     for (final controller in _controllers.values) {
      40           1 :       controller.dispose();
      41             :     }
      42           2 :     _headerController?.dispose();
      43           1 :     _headerController = null;
      44           1 :     super.dispose();
      45             :   }
      46             : 
      47           1 :   void _loadGrid() {
      48           2 :     setState(() {
      49           1 :       _error = null;
      50           1 :       _grid = null;
      51             :     });
      52           2 :     ApptiveGrid.getClient(context, listen: false)
      53           5 :         .loadGrid(gridUri: widget.component.data.gridUri)
      54           2 :         .then((value) {
      55           2 :       for (final row in value.rows) {
      56           3 :         _controllers[row.id]?.dispose();
      57           5 :         _controllers[row.id] = _scrollControllerGroup.addAndGet();
      58             : 
      59           4 :         _keys[row.id] ??= GlobalKey();
      60             :       }
      61           1 :       _headerController?.dispose();
      62           3 :       _headerController = _scrollControllerGroup.addAndGet();
      63           2 :       setState(() {
      64           1 :         _error = null;
      65           1 :         _grid = value;
      66             :       });
      67           2 :     }).catchError((error) {
      68           2 :       setState(() {
      69           1 :         _error = error;
      70           1 :         _grid = null;
      71             :       });
      72             :     });
      73             :   }
      74             : 
      75           1 :   @override
      76             :   Widget build(BuildContext context) {
      77           1 :     return GridRowDropdownButtonFormField<GridRowDropdownDataItem?>(
      78             :       isExpanded: true,
      79           1 :       items: _items(),
      80           1 :       onChanged: (newValue) {
      81           1 :         if (newValue?.entityUri != null) {
      82           2 :           setState(() {
      83           5 :             widget.component.data.value = newValue?.displayValue;
      84           5 :             widget.component.data.entityUri = newValue?.entityUri;
      85             :           });
      86             :         }
      87             :       },
      88           1 :       validator: (value) {
      89           3 :         if (widget.component.required &&
      90           2 :             (value?.entityUri == null || value?.displayValue == null)) {
      91           1 :           return ApptiveGridLocalization.of(context)!
      92           4 :               .fieldIsRequired(widget.component.property);
      93             :         } else {
      94             :           return null;
      95             :         }
      96             :       },
      97           1 :       selectedItemBuilder: _selectedItems,
      98             :       autovalidateMode: AutovalidateMode.onUserInteraction,
      99           4 :       value: widget.component.data.entityUri != null &&
     100           4 :               widget.component.data.value != null
     101           1 :           ? GridRowDropdownDataItem(
     102           4 :               entityUri: widget.component.data.entityUri,
     103           4 :               displayValue: widget.component.data.value,
     104             :             )
     105             :           : null,
     106           1 :       decoration: InputDecoration(
     107           4 :         helperText: widget.component.options.description,
     108             :         helperMaxLines: 100,
     109           7 :         labelText: widget.component.options.label ?? widget.component.property,
     110           2 :         errorText: _error?.toString(),
     111             :       ),
     112             :     );
     113             :   }
     114             : 
     115           1 :   List<GridRowDropdownMenuItem<GridRowDropdownDataItem?>>? _items() {
     116           2 :     if (_error != null || _grid == null) {
     117             :       return null;
     118             :     } else {
     119           2 :       final localization = ApptiveGridLocalization.of(context)!;
     120           1 :       final searchBox = GridRowDropdownMenuItem(
     121             :         enabled: false,
     122             :         value: null,
     123           1 :         child: Padding(
     124             :           padding: const EdgeInsets.symmetric(horizontal: 16.0),
     125           1 :           child: TextField(
     126           1 :             decoration: InputDecoration(
     127             :               icon: const Icon(Icons.search),
     128           1 :               hintText: localization.crossRefSearch,
     129             :               border: InputBorder.none,
     130             :             ),
     131           1 :             onChanged: (input) {
     132           3 :               for (final key in _keys.values) {
     133           2 :                 key.currentState?.updateFilter(input);
     134             :               }
     135             :             },
     136             :           ),
     137             :         ),
     138             :       );
     139             : 
     140           1 :       final headerRow = GridRowDropdownMenuItem(
     141             :         enabled: false,
     142             :         value: null,
     143           1 :         child: HeaderRowWidget(
     144           2 :           fields: _grid!.fields,
     145           1 :           controller: _headerController,
     146             :         ),
     147             :       );
     148             : 
     149           4 :       final items = _grid!.rows.map((row) {
     150           4 :         final gridUri = widget.component.data.gridUri;
     151           1 :         final entityUri = EntityUri(
     152           1 :           user: gridUri.user,
     153           1 :           space: gridUri.space,
     154           1 :           grid: gridUri.grid,
     155           1 :           entity: row.id,
     156             :         );
     157           1 :         return GridRowDropdownMenuItem(
     158           1 :           value: GridRowDropdownDataItem(
     159             :             entityUri: entityUri,
     160           5 :             displayValue: row.entries.first.data.value.toString(),
     161             :           ),
     162           1 :           child: _RowMenuItem(
     163           3 :             key: _keys[row.id],
     164           1 :             grid: _grid!,
     165             :             row: row,
     166           3 :             controller: _controllers[row.id],
     167             :           ),
     168             :         );
     169           1 :       }).toList();
     170           2 :       return [searchBox, headerRow, ...items];
     171             :     }
     172             :   }
     173             : 
     174           1 :   List<Widget> _selectedItems(BuildContext context) {
     175           1 :     final localization = ApptiveGridLocalization.of(context)!;
     176           1 :     if (_error != null) {
     177           1 :       return [
     178             :         const Center(
     179             :           child: Text('ERROR'),
     180             :         )
     181             :       ];
     182           1 :     } else if (_grid == null) {
     183           1 :       return [
     184           1 :         Center(
     185           2 :           child: Text(localization.loadingGrid),
     186             :         )
     187             :       ];
     188             :     } else {
     189           2 :       final pleaseSelect = Text(localization.selectEntry);
     190           1 :       return [
     191           1 :         ...[pleaseSelect, pleaseSelect],
     192           2 :         ..._grid!.rows
     193           1 :             .map(
     194           2 :               (row) => Text(
     195           5 :                 row.entries.first.data.value?.toString() ?? '',
     196             :                 maxLines: 1,
     197             :                 overflow: TextOverflow.ellipsis,
     198             :               ),
     199             :             )
     200           1 :             .toList()
     201             :       ];
     202             :     }
     203             :   }
     204             : }
     205             : 
     206             : class _RowMenuItem extends StatefulWidget {
     207           1 :   const _RowMenuItem({
     208             :     Key? key,
     209             :     required this.grid,
     210             :     required this.row,
     211             :     this.controller,
     212           1 :   }) : super(key: key);
     213             : 
     214             :   final Grid grid;
     215             :   final GridRow row;
     216             :   final ScrollController? controller;
     217             : 
     218           1 :   @override
     219           1 :   _RowMenuItemState createState() => _RowMenuItemState();
     220             : }
     221             : 
     222             : class _RowMenuItemState extends State<_RowMenuItem> {
     223             :   String? _filter;
     224             : 
     225           1 :   void updateFilter(String filter) {
     226           2 :     setState(() {
     227           1 :       _filter = filter;
     228             :     });
     229             :   }
     230             : 
     231           1 :   @override
     232             :   Widget build(BuildContext context) {
     233           4 :     if (!widget.row.matchesFilter(_filter)) {
     234             :       return const SizedBox();
     235             :     } else {
     236           3 :       final index = widget.grid.rows
     237           4 :           .where((row) => row.matchesFilter(_filter))
     238           1 :           .toList()
     239           3 :           .indexOf(widget.row);
     240           1 :       return GridRowWidget(
     241           2 :         row: widget.row,
     242           2 :         controller: widget.controller,
     243           2 :         color: index % 2 != 0
     244           3 :             ? Theme.of(context).hintColor.withOpacity(0.1)
     245             :             : null,
     246             :       );
     247             :     }
     248             :   }
     249             : }
     250             : 
     251             : extension _GridRowX on GridRow {
     252           1 :   bool matchesFilter(String? filter) {
     253           1 :     if (filter == null || filter.isEmpty) return true;
     254             : 
     255           1 :     return entries
     256           1 :         .where(
     257           1 :           (entry) =>
     258           2 :               entry.data.schemaValue
     259           1 :                   ?.toString()
     260           1 :                   .toLowerCase()
     261           2 :                   .contains(filter.toLowerCase()) ??
     262             :               false,
     263             :         )
     264           1 :         .isNotEmpty;
     265             :   }
     266             : }

Generated by: LCOV version 1.15