LCOV - code coverage report
Current view: top level - src/ui/editor/widgets - editable_textfield.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 79 124 63.7 %
Date: 2020-12-04 18:41:24 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:async';
       2             : 
       3             : import 'package:dotted_border/dotted_border.dart';
       4             : import 'package:flutter/material.dart';
       5             : import 'package:flutter/services.dart';
       6             : import 'package:flutter/widgets.dart';
       7             : import 'package:pal/src/router.dart';
       8             : import 'package:pal/src/ui/editor/pages/helper_editor/font_editor/font_editor.dart';
       9             : import 'package:pal/src/ui/editor/pages/helper_editor/font_editor/font_editor_viewmodel.dart';
      10             : import 'package:pal/src/ui/editor/pages/helper_editor/font_editor/pickers/font_weight_picker/font_weight_picker_loader.dart';
      11             : import 'package:pal/src/ui/editor/pages/helper_editor/helper_editor_notifiers.dart';
      12             : import 'package:pal/src/ui/editor/pages/helper_editor/widgets/color_picker.dart';
      13             : import 'package:pal/src/ui/editor/widgets/edit_helper_toolbar.dart';
      14             : import 'package:pal/src/ui/shared/widgets/overlayed.dart';
      15             : 
      16          78 : enum ToolbarType { text, border }
      17             : 
      18             : typedef OnFieldChanged(String id, String value);
      19             : 
      20             : typedef OnTextStyleChanged(String id, TextStyle style, FontKeys fontkeys);
      21             : 
      22             : class EditableTextField extends StatefulWidget {
      23             :   // Keys
      24             :   // static final GlobalKey<_EditableTextFieldState> globalKey = GlobalKey();
      25             :   final String id;
      26             :   final Key textFormFieldKey;
      27             :   final Key backgroundContainerKey;
      28             :   final Key helperToolbarKey;
      29             : 
      30             :   // Textfield stuff
      31             :   final AutovalidateMode autovalidate;
      32             :   final BoxDecoration backgroundBoxDecoration;
      33             :   final EdgeInsetsGeometry backgroundPadding;
      34             :   final EdgeInsetsGeometry textFormFieldPadding;
      35             :   final OnFieldChanged onChanged;
      36             :   final OnTextStyleChanged onTextStyleChanged;
      37             :   final TextInputType keyboardType;
      38             :   final TextStyle textStyle;
      39             :   final int maxLines;
      40             :   final int maximumCharacterLength;
      41             :   final int minimumCharacterLength;
      42             :   final String hintText;
      43             :   final List<TextInputFormatter> inputFormatters;
      44             :   final Stream<bool> outsideTapStream;
      45             :   final ToolbarType toolbarType;
      46             :   final String initialValue;
      47             :   final String fontFamilyKey;
      48             : 
      49           4 :   EditableTextField({
      50             :     Key key,
      51             :     this.id,
      52             :     this.textFormFieldKey,
      53             :     this.backgroundContainerKey,
      54             :     this.helperToolbarKey,
      55             :     this.outsideTapStream,
      56             :     this.maximumCharacterLength,
      57             :     this.minimumCharacterLength,
      58             :     this.onChanged,
      59             :     this.onTextStyleChanged,
      60             :     this.autovalidate = AutovalidateMode.onUserInteraction,
      61             :     this.backgroundPadding,
      62             :     this.textFormFieldPadding,
      63             :     this.backgroundBoxDecoration,
      64             :     this.maxLines = 1,
      65             :     this.fontFamilyKey,
      66             :     this.inputFormatters,
      67             :     this.hintText = 'Edit me!',
      68             :     this.keyboardType,
      69             :     this.initialValue,
      70             :     this.toolbarType = ToolbarType.text,
      71             :     @required this.textStyle,
      72           4 :   }) : super();
      73             : 
      74           4 :   factory EditableTextField.text({
      75             :     Key key,
      76             :     final String id,
      77             :     final Key textFormFieldKey,
      78             :     final Key backgroundContainerKey,
      79             :     final Key helperToolbarKey,
      80             :     final AutovalidateMode autovalidate = AutovalidateMode.onUserInteraction,
      81             :     final BoxDecoration backgroundBoxDecoration,
      82             :     final EdgeInsetsGeometry backgroundPadding,
      83             :     final EdgeInsetsGeometry textFormFieldPadding,
      84             :     final String Function(String) validator,
      85             :     final OnFieldChanged onChanged,
      86             :     final OnTextStyleChanged onTextStyleChanged,
      87             :     final TextInputType keyboardType,
      88             :     final TextStyle textStyle,
      89             :     final int maxLines = 1,
      90             :     final int maximumCharacterLength,
      91             :     final int minimumCharacterLength,
      92             :     final String hintText = 'Edit me!',
      93             :     final List<TextInputFormatter> inputFormatters,
      94             :     final Stream<bool> outsideTapStream,
      95             :     final String initialValue,
      96             :     final String fontFamilyKey,
      97             :   }) {
      98           4 :     return EditableTextField(
      99             :       key: key,
     100             :       id: id,
     101             :       textFormFieldKey: textFormFieldKey,
     102             :       backgroundContainerKey: backgroundContainerKey,
     103             :       helperToolbarKey: helperToolbarKey,
     104             :       outsideTapStream: outsideTapStream,
     105             :       maximumCharacterLength: maximumCharacterLength,
     106             :       minimumCharacterLength: minimumCharacterLength,
     107             :       onTextStyleChanged: onTextStyleChanged,
     108             :       onChanged: onChanged,
     109             :       fontFamilyKey: fontFamilyKey,
     110             :       autovalidate: autovalidate,
     111             :       backgroundPadding: backgroundPadding,
     112             :       textFormFieldPadding: textFormFieldPadding,
     113             :       backgroundBoxDecoration: backgroundBoxDecoration,
     114             :       maxLines: maxLines,
     115             :       inputFormatters: inputFormatters,
     116             :       hintText: hintText,
     117             :       keyboardType: keyboardType,
     118             :       textStyle: textStyle,
     119             :       toolbarType: ToolbarType.text,
     120             :       initialValue: initialValue,
     121             :     );
     122             :   }
     123             : 
     124             :   // factory EditableTextField.fromTextFormFieldNotifier({
     125             :   //   final Stream<bool> outsideTapStream,
     126             :   //   final TextFormFieldNotifier textFormFieldNotifier,
     127             :   //   final TextStyle mergeWithFonts
     128             :   // }) => EditableTextField.text(
     129             :   //   outsideTapStream: outsideTapStream,
     130             :   //   fontFamilyKey: textFormFieldNotifier.fontFamily?.value,
     131             :   //   initialValue: textFormFieldNotifier.text?.value,
     132             :   //   textStyle: TextStyle(
     133             :   //     color: textFormFieldNotifier.fontColor.value,
     134             :   //     decoration: TextDecoration.none,
     135             :   //     fontSize: textFormFieldNotifier.fontSize?.value?.toDouble(),
     136             :   //     fontWeight: FontWeightMapper.toFontWeight(
     137             :   //       textFormFieldNotifier.fontWeight?.value,
     138             :   //     ),
     139             :   //   ).merge(mergeWithFonts)
     140             :   // );
     141             : 
     142           0 :   factory EditableTextField.border({
     143             :     Key key,
     144             :     final String id,
     145             :     final Key textFormFieldKey,
     146             :     final Key backgroundContainerKey,
     147             :     final Key helperToolbarKey,
     148             :     final AutovalidateMode autovalidate = AutovalidateMode.onUserInteraction,
     149             :     final BoxDecoration backgroundBoxDecoration,
     150             :     final EdgeInsetsGeometry backgroundPadding,
     151             :     final EdgeInsetsGeometry textFormFieldPadding,
     152             :     final String Function(String) validator,
     153             :     final Function(String, String) onChanged,
     154             :     final Function(String, TextStyle, FontKeys) onTextStyleChanged,
     155             :     final TextInputType keyboardType,
     156             :     final TextStyle textStyle,
     157             :     final int maxLines = 1,
     158             :     final int maximumCharacterLength,
     159             :     final int minimumCharacterLength,
     160             :     final String hintText = 'Edit me!',
     161             :     final List<TextInputFormatter> inputFormatters,
     162             :     final Stream<bool> outsideTapStream,
     163             :     final String initialValue,
     164             :     final String fontFamilyKey,
     165             :   }) {
     166           0 :     return EditableTextField(
     167             :       key: key,
     168             :       id: id,
     169             :       textFormFieldKey: textFormFieldKey,
     170             :       backgroundContainerKey: backgroundContainerKey,
     171             :       helperToolbarKey: helperToolbarKey,
     172             :       outsideTapStream: outsideTapStream,
     173             :       maximumCharacterLength: maximumCharacterLength,
     174             :       minimumCharacterLength: minimumCharacterLength,
     175             :       onChanged: onChanged,
     176             :       onTextStyleChanged: onTextStyleChanged,
     177             :       autovalidate: autovalidate,
     178             :       backgroundPadding: backgroundPadding,
     179             :       textFormFieldPadding: textFormFieldPadding,
     180             :       backgroundBoxDecoration: backgroundBoxDecoration,
     181             :       maxLines: maxLines,
     182             :       inputFormatters: inputFormatters,
     183             :       hintText: hintText,
     184             :       keyboardType: keyboardType,
     185             :       textStyle: textStyle,
     186             :       toolbarType: ToolbarType.border,
     187             :       initialValue: initialValue,
     188             :       fontFamilyKey: fontFamilyKey,
     189             :     );
     190             :   }
     191             : 
     192           4 :   @override
     193           4 :   _EditableTextFieldState createState() => _EditableTextFieldState();
     194             : }
     195             : 
     196             : class _EditableTextFieldState extends State<EditableTextField> {
     197             :   bool _isToolbarVisible = false;
     198             :   FocusNode _focusNode = FocusNode();
     199             :   StreamSubscription _outsideSub;
     200             :   TextStyle _textStyle;
     201             :   String _fontFamilyKey;
     202             : 
     203           4 :   @override
     204             :   void initState() {
     205           4 :     super.initState();
     206             :     // Install listener when focus change
     207          12 :     _focusNode.addListener(_onFocusChange);
     208          12 :     _fontFamilyKey = widget.fontFamilyKey ?? 'Montserrat';
     209             :     // Listen on stream when outside tap is detected
     210          15 :     _outsideSub = widget.outsideTapStream?.listen((event) {
     211             :       if (event) {
     212           0 :         this._onClose();
     213             :       }
     214             :     });
     215          12 :     _textStyle = widget.textStyle;
     216             :   }
     217             : 
     218           4 :   @override
     219             :   void dispose() {
     220           7 :     _outsideSub?.cancel();
     221          12 :     _focusNode.removeListener(_onFocusChange);
     222           4 :     super.dispose();
     223             :   }
     224             : 
     225           4 :   @override
     226             :   Widget build(BuildContext context) {
     227           4 :     return Padding(
     228             :       padding: const EdgeInsets.all(1.0),
     229           4 :       child: Column(
     230           4 :         children: [
     231          16 :           if (_isToolbarVisible) _buildToolbar(widget.toolbarType),
     232           4 :           Padding(
     233             :             // FIXME: This is used to show element even if keyboard is shown
     234             :             // should be better to find a way to not use it :/
     235           8 :             padding: widget.backgroundPadding ?? EdgeInsets.zero,
     236           4 :             child: DottedBorder(
     237           4 :               dashPattern: [6, 3],
     238           4 :               color: Colors.white.withAlpha(80),
     239           4 :               child: Container(
     240           8 :                 decoration: widget.backgroundBoxDecoration,
     241           8 :                 key: widget.backgroundContainerKey,
     242           4 :                 child: Padding(
     243           8 :                   padding: widget.textFormFieldPadding ?? EdgeInsets.zero,
     244           4 :                   child: TextFormField(
     245           8 :                     key: widget.textFormFieldKey,
     246           8 :                     autovalidateMode: widget.autovalidate,
     247           4 :                     focusNode: _focusNode,
     248           4 :                     onTap: _onTextFieldTapped,
     249           4 :                     onChanged: (String newValue) {
     250           8 :                       if (widget.onChanged != null) {
     251          12 :                         widget.onChanged(
     252          20 :                             widget.id ?? widget.textFormFieldKey.toString(),
     253             :                             newValue);
     254             :                       }
     255             :                     },
     256           4 :                     validator: (String value) {
     257             :                       String error;
     258           8 :                       if (widget.minimumCharacterLength != null) {
     259             :                         if (value != null &&
     260          16 :                             value.length < widget.minimumCharacterLength) {
     261             :                           error =
     262           6 :                               'Minimum ${widget.minimumCharacterLength} ${widget.minimumCharacterLength <= 1 ? 'character' : 'characters'} allowed';
     263             :                         }
     264             :                       }
     265           8 :                       if (widget.maximumCharacterLength != null) {
     266             :                         if (value != null &&
     267          16 :                             value.length >= widget.maximumCharacterLength) {
     268             :                           error =
     269           6 :                               'Maximum ${widget.maximumCharacterLength} ${widget.maximumCharacterLength <= 1 ? 'character' : 'characters'} allowed';
     270             :                         }
     271             :                       }
     272             :                       return error;
     273             :                     },
     274           8 :                     initialValue: widget.initialValue,
     275           8 :                     keyboardType: widget.keyboardType,
     276           8 :                     maxLines: widget.maxLines ?? 1,
     277             :                     minLines: 1,
     278           4 :                     onFieldSubmitted: _onFieldSubmitted,
     279           7 :                     cursorColor: _textStyle?.color,
     280           8 :                     inputFormatters: widget.inputFormatters,
     281           4 :                     decoration: InputDecoration(
     282             :                       border: InputBorder.none,
     283             :                       enabledBorder: InputBorder.none,
     284           8 :                       hintText: widget.hintText,
     285           7 :                       hintStyle: _textStyle?.merge(
     286           3 :                         TextStyle(
     287           9 :                           color: _textStyle?.color?.withAlpha(80),
     288             :                         ),
     289             :                       ),
     290             :                     ),
     291             :                     textAlign: TextAlign.center,
     292           4 :                     style: _textStyle,
     293             :                   ),
     294             :                 ),
     295             :               ),
     296             :             ),
     297             :           ),
     298             :         ],
     299             :       ),
     300             :     );
     301             :   }
     302             : 
     303           4 :   _buildToolbar(ToolbarType toolbarType) {
     304             :     Widget toolbar;
     305             :     switch (toolbarType) {
     306           4 :       case ToolbarType.text:
     307           4 :         toolbar = EditHelperToolbar.text(
     308           8 :           key: widget.helperToolbarKey,
     309           4 :           onChangeTextColor: _onChangeTextColor,
     310           4 :           onChangeTextFont: _onChangeTextFont,
     311           4 :           onClose: _onClose,
     312             :         );
     313             :         break;
     314           0 :       case ToolbarType.border:
     315           0 :         toolbar = EditHelperToolbar.border(
     316           0 :           key: widget.helperToolbarKey,
     317           0 :           onChangeTextColor: _onChangeTextColor,
     318           0 :           onChangeTextFont: _onChangeTextFont,
     319           0 :           onChangeBorder: _onChangeBorder,
     320           0 :           onClose: _onClose,
     321             :         );
     322             :         break;
     323             :       default:
     324             :     }
     325             :     return toolbar;
     326             :   }
     327             : 
     328             :   // Textfield stuff
     329           4 :   _onTextFieldTapped() {
     330           8 :     _focusNode.requestFocus();
     331           8 :     setState(() {
     332           4 :       _isToolbarVisible = true;
     333             :     });
     334             :   }
     335             : 
     336           4 :   _onFocusChange() {
     337           8 :     if (!_focusNode.hasFocus) {
     338           6 :       setState(() {
     339           3 :         _isToolbarVisible = false;
     340             :       });
     341             :     }
     342             :   }
     343             : 
     344           0 :   _onFieldSubmitted(String newValue) {
     345           0 :     this._onClose();
     346             :   }
     347             : 
     348           0 :   _onChangeTextFont() {
     349           0 :     var widgetBuilder = (context) => FontEditorDialogPage(
     350           0 :       onCancelPicker: () => Navigator.of(context).pop(),
     351           0 :       onValidatePicker: () => Navigator.of(context).pop(),
     352           0 :       actualTextStyle: _textStyle,
     353           0 :       fontFamilyKey: _fontFamilyKey,
     354           0 :       onFontModified: (TextStyle newTextStyle, FontKeys fontKeys) {
     355           0 :         setState(() {
     356           0 :           _textStyle = _textStyle.merge(
     357             :             newTextStyle,
     358             :           );
     359           0 :           _isToolbarVisible = true;
     360             :         });
     361             : 
     362           0 :         if (fontKeys?.fontFamilyNameKey != null &&
     363           0 :           fontKeys.fontFamilyNameKey.length > 0) {
     364           0 :           _fontFamilyKey = fontKeys.fontFamilyNameKey;
     365             :         }
     366           0 :         if (widget.onTextStyleChanged != null) {
     367           0 :           widget.onTextStyleChanged(
     368           0 :             widget.id ?? widget.textFormFieldKey.toString(),
     369           0 :             _textStyle,
     370             :             fontKeys,
     371             :           );
     372             :         }
     373             :       },
     374             :     );
     375           0 :     showDialog(context: context, builder: widgetBuilder);
     376             :   }
     377             : 
     378           0 :   _onChangeTextColor() {
     379           0 :     var widgetBuilder = (context) => ColorPickerDialog(
     380           0 :       placeholderColor: _textStyle?.color,
     381           0 :       onCancel: () => Navigator.of(context).pop(),
     382           0 :       onColorSelected: (Color newColor) {
     383           0 :         setState(() {
     384           0 :           _textStyle = _textStyle.merge(TextStyle(
     385             :             color: newColor,
     386             :           ));
     387           0 :           _isToolbarVisible = true;
     388             :         });
     389           0 :         if (widget.onTextStyleChanged != null) {
     390           0 :           widget.onTextStyleChanged(
     391           0 :             widget.id ?? widget.textFormFieldKey.toString(),
     392           0 :             _textStyle,
     393             :             null,
     394             :           );
     395             :         }
     396           0 :         Navigator.of(context).pop();
     397             :       },
     398             :     );
     399           0 :     showDialog(context: context, builder: widgetBuilder);
     400             :   }
     401             : 
     402           0 :   _onChangeBorder() {}
     403             : 
     404           1 :   _onClose() {
     405           2 :     _focusNode.unfocus();
     406           2 :     setState(() {
     407           1 :       _isToolbarVisible = false;
     408             :     });
     409             :   }
     410             : }

Generated by: LCOV version 1.14