LCOV - code coverage report
Current view: top level - form_widget - cross_reference_form_widget.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 124 124 100.0 %
Date: 2021-10-26 11:03:55 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           4 :           return '${widget.component.property} is required';
      92             :         } else {
      93             :           return null;
      94             :         }
      95             :       },
      96           1 :       selectedItemBuilder: _selectedItems,
      97             :       autovalidateMode: AutovalidateMode.onUserInteraction,
      98           4 :       value: widget.component.data.entityUri != null &&
      99           4 :               widget.component.data.value != null
     100           1 :           ? GridRowDropdownDataItem(
     101           4 :               entityUri: widget.component.data.entityUri,
     102           4 :               displayValue: widget.component.data.value,
     103             :             )
     104             :           : null,
     105           1 :       decoration: InputDecoration(
     106           4 :         helperText: widget.component.options.description,
     107             :         helperMaxLines: 100,
     108           7 :         labelText: widget.component.options.label ?? widget.component.property,
     109           2 :         errorText: _error?.toString(),
     110             :       ),
     111             :     );
     112             :   }
     113             : 
     114           1 :   List<GridRowDropdownMenuItem<GridRowDropdownDataItem?>>? _items() {
     115           2 :     if (_error != null || _grid == null) {
     116             :       return null;
     117             :     } else {
     118           1 :       final searchBox = GridRowDropdownMenuItem(
     119             :         enabled: false,
     120             :         value: null,
     121           1 :         child: Padding(
     122             :           padding: const EdgeInsets.symmetric(horizontal: 16.0),
     123           1 :           child: TextField(
     124             :             decoration: const InputDecoration(
     125             :               icon: Icon(Icons.search),
     126             :               hintText: 'Search',
     127             :               border: InputBorder.none,
     128             :             ),
     129           1 :             onChanged: (input) {
     130           3 :               for (final key in _keys.values) {
     131           2 :                 key.currentState?.updateFilter(input);
     132             :               }
     133             :             },
     134             :           ),
     135             :         ),
     136             :       );
     137             : 
     138           1 :       final headerRow = GridRowDropdownMenuItem(
     139             :         enabled: false,
     140             :         value: null,
     141           1 :         child: HeaderRowWidget(
     142           2 :           fields: _grid!.fields,
     143           1 :           controller: _headerController,
     144             :         ),
     145             :       );
     146             : 
     147           4 :       final items = _grid!.rows.map((row) {
     148           4 :         final gridUri = widget.component.data.gridUri;
     149           1 :         final entityUri = EntityUri(
     150           1 :           user: gridUri.user,
     151           1 :           space: gridUri.space,
     152           1 :           grid: gridUri.grid,
     153           1 :           entity: row.id,
     154             :         );
     155           1 :         return GridRowDropdownMenuItem(
     156           1 :           value: GridRowDropdownDataItem(
     157             :             entityUri: entityUri,
     158           5 :             displayValue: row.entries.first.data.value.toString(),
     159             :           ),
     160           1 :           child: _RowMenuItem(
     161           3 :             key: _keys[row.id],
     162           1 :             grid: _grid!,
     163             :             row: row,
     164           3 :             controller: _controllers[row.id],
     165             :           ),
     166             :         );
     167           1 :       }).toList();
     168           2 :       return [searchBox, headerRow, ...items];
     169             :     }
     170             :   }
     171             : 
     172           1 :   List<Widget> _selectedItems(BuildContext context) {
     173           1 :     if (_error != null) {
     174           1 :       return [
     175             :         const Center(
     176             :           child: Text('ERROR'),
     177             :         )
     178             :       ];
     179           1 :     } else if (_grid == null) {
     180           1 :       return [
     181             :         const Center(
     182             :           child: Text('Loading Grid...'),
     183             :         )
     184             :       ];
     185             :     } else {
     186             :       const pleaseSelect = Text('Select an entry');
     187           1 :       return [
     188           1 :         ...[pleaseSelect, pleaseSelect],
     189           2 :         ..._grid!.rows
     190           1 :             .map(
     191           2 :               (row) => Text(
     192           5 :                 row.entries.first.data.value?.toString() ?? '',
     193             :                 maxLines: 1,
     194             :                 overflow: TextOverflow.ellipsis,
     195             :               ),
     196             :             )
     197           1 :             .toList()
     198             :       ];
     199             :     }
     200             :   }
     201             : }
     202             : 
     203             : class _RowMenuItem extends StatefulWidget {
     204           1 :   const _RowMenuItem({
     205             :     Key? key,
     206             :     required this.grid,
     207             :     required this.row,
     208             :     this.controller,
     209           1 :   }) : super(key: key);
     210             : 
     211             :   final Grid grid;
     212             :   final GridRow row;
     213             :   final ScrollController? controller;
     214             : 
     215           1 :   @override
     216           1 :   _RowMenuItemState createState() => _RowMenuItemState();
     217             : }
     218             : 
     219             : class _RowMenuItemState extends State<_RowMenuItem> {
     220             :   String? _filter;
     221             : 
     222           1 :   void updateFilter(String filter) {
     223           2 :     setState(() {
     224           1 :       _filter = filter;
     225             :     });
     226             :   }
     227             : 
     228           1 :   @override
     229             :   Widget build(BuildContext context) {
     230           4 :     if (!widget.row.matchesFilter(_filter)) {
     231             :       return const SizedBox();
     232             :     } else {
     233           3 :       final index = widget.grid.rows
     234           4 :           .where((row) => row.matchesFilter(_filter))
     235           1 :           .toList()
     236           3 :           .indexOf(widget.row);
     237           1 :       return GridRowWidget(
     238           2 :         row: widget.row,
     239           2 :         controller: widget.controller,
     240           2 :         color: index % 2 != 0
     241           3 :             ? Theme.of(context).hintColor.withOpacity(0.1)
     242             :             : null,
     243             :       );
     244             :     }
     245             :   }
     246             : }
     247             : 
     248             : extension _GridRowX on GridRow {
     249           1 :   bool matchesFilter(String? filter) {
     250           1 :     if (filter == null || filter.isEmpty) return true;
     251             : 
     252           1 :     return entries
     253           1 :         .where(
     254           1 :           (entry) =>
     255           2 :               entry.data.schemaValue
     256           1 :                   ?.toString()
     257           1 :                   .toLowerCase()
     258           2 :                   .contains(filter.toLowerCase()) ??
     259             :               false,
     260             :         )
     261           1 :         .isNotEmpty;
     262             :   }
     263             : }

Generated by: LCOV version 1.15