diff --git a/.idea/libraries/Flutter_Plugins.xml b/.idea/libraries/Flutter_Plugins.xml index 53449da..c78d3c9 100644 --- a/.idea/libraries/Flutter_Plugins.xml +++ b/.idea/libraries/Flutter_Plugins.xml @@ -2,6 +2,7 @@ + diff --git a/.idea/workspace.xml b/.idea/workspace.xml index 71b6560..4cadf95 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,14 +1,22 @@ + + + + + + - - + + + - + - - + + - - + + - - + + - - + + - + \ No newline at end of file diff --git a/README.md b/README.md index a1967ce..f9d3a14 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ import 'package:block_input/block_input.dart'; ```dart BlockInput( - blockInputController: blockInputController, - blockInputKeyboardType: BlockInputKeyboardType.number, // Number or Text + controller: blockInputController, + keyboardType: BlockInputKeyboardType.number, // Number or Text axisAlignment: MainAxisAlignment.spaceBetween, // Same as Row/Column MainAxisAlignment blockInputStyle: BlockInputStyle( backgroundColor: Colors.black12, // Color @@ -34,6 +34,32 @@ BlockInput( ) ) ``` +### Cyrillic support +Mongolian cyrillic input is **loosely** supported for now. This feature will help you to build Mongolian Registration No input. Cyrillic input triggers custom keyboard layout. Usage: + +```dart +BlockInput( + keyboardType: BlockInputKeyboardType.mnCyrillic + ... +) +``` +Cyrillic keyboard styling: +```dart +BlockInputStyle( + keyboardStyle: BlockKeyboardStyle( + keyColor: Colors.blueAccent, + backgroundColor: Colors.white, + width: 40, + height: 40, + textStyle: TextStyle(), + keyShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + side: BorderSide(color: Colors.red) + ), + ), + ... +), +``` ## Controller and Listener @@ -60,6 +86,7 @@ blockInputController.dispose(); ``` ## Attributes + ### BlockInput | Attribute | Type | |------------------------|------------------------| @@ -70,6 +97,8 @@ blockInputController.dispose(); | errorMessageStyle | TextStyle | | axisAlignment | MainAxisAlignment | + + ### BlockInputStyle | Attribute | Type | |-----------------|--------------------| @@ -79,6 +108,9 @@ blockInputController.dispose(); | padding | EdgeInsets | | width | double | | textStyle | TextStyle | +| keyboardStyle | BlockKeyboardStyle | + + ## Licence **Apache License version 2.0** diff --git a/example/lib/main.dart b/example/lib/main.dart index b82bdc6..505f6e5 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:block_input/block_input_controller.dart'; import 'package:block_input/block_input_keyboard_type.dart'; import 'package:block_input/block_input_style.dart'; +import 'package:block_input/block_keyboard_style.dart'; import 'package:flutter/material.dart'; import 'package:block_input/block_input.dart'; @@ -44,100 +45,96 @@ class _MyAppState extends State { @override Widget build(BuildContext context) { - return MaterialApp( - home: Scaffold( - appBar: AppBar( - title: const Text('Block Input Example'), - ), - body: SingleChildScrollView( - child: Column( - children: [ - Container( - padding: EdgeInsets.all(10), - child: BlockInput( - blockInputController: blockInputController, - blockInputKeyboardType: BlockInputKeyboardType.number, - axisAlignment: MainAxisAlignment.spaceBetween, - blockInputStyle: BlockInputStyle( - backgroundColor: Colors.black12, - border: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - borderSide: BorderSide(color: Colors.deepOrange, width: 1) - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - borderSide: BorderSide(color: Colors.blueAccent, width: 2) - ) - ), - ), - ), - Container( - padding: EdgeInsets.all(10), - child: Row( - children: [ - RaisedButton( - child: Text('CLEAR'), - onPressed: () { - blockInputController.clear(); - }, - ), - SizedBox(width: 10,), - RaisedButton( - child: Text('RANDOM FILL'), - onPressed: () { - Random rand = Random(); - StringBuffer strBuffer = StringBuffer(); - for(int i = 0; i < blockInputController.size; i++) { - strBuffer.write(rand.nextInt(9).toString()); - } - blockInputController.text = strBuffer.toString(); - }, - ), - SizedBox(width: 10,), - RaisedButton( - child: Text('TEST'), - onPressed: () { - blockInputController.text = 'MARAAAAAAA'; - }, - ) - ], - ), - ), - - // ------ MONGOLIAN REGISTER NO DEMO ------ - Divider(), - Container( - padding: EdgeInsets.all(10), - child: Row( - children: [ - Container( - width: 90, - child: BlockInput( - blockInputController: cyrillicInputController, - blockInputKeyboardType: BlockInputKeyboardType.mnCyrillic, - axisAlignment: MainAxisAlignment.spaceBetween, - blockInputStyle: BlockInputStyle( - backgroundColor: Colors.white, + return GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); + }, + child: MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Block Input Example'), + ), + body: SingleChildScrollView( + child: Column( + children: [ + Container( + padding: EdgeInsets.all(10), + child: BlockInput( + controller: blockInputController, + keyboardType: BlockInputKeyboardType.number, + axisAlignment: MainAxisAlignment.spaceBetween, + style: BlockInputStyle( + backgroundColor: Colors.black12, border: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(7)), - borderSide: BorderSide(color: Colors.blueGrey, width: 1) + borderRadius: BorderRadius.all(Radius.circular(10)), + borderSide: BorderSide(color: Colors.deepOrange, width: 1) ), focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(7)), - borderSide: BorderSide(color: Colors.blue, width: 3) + borderRadius: BorderRadius.all(Radius.circular(10)), + borderSide: BorderSide(color: Colors.blueAccent, width: 2) ) - ), ), ), - SizedBox(width: 10,), - Container( - child: Expanded( - child: TextField( - controller: numberInputController, - keyboardType: TextInputType.number, - decoration: InputDecoration( - contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 5), - enabledBorder: OutlineInputBorder( + ), + Container( + padding: EdgeInsets.all(10), + child: Row( + children: [ + RaisedButton( + child: Text('CLEAR'), + onPressed: () { + blockInputController.clear(); + }, + ), + SizedBox(width: 10,), + RaisedButton( + child: Text('RANDOM FILL'), + onPressed: () { + Random rand = Random(); + StringBuffer strBuffer = StringBuffer(); + for(int i = 0; i < blockInputController.size; i++) { + strBuffer.write(rand.nextInt(9).toString()); + } + blockInputController.text = strBuffer.toString(); + }, + ), + SizedBox(width: 10,), + RaisedButton( + child: Text('TEST'), + onPressed: () { + blockInputController.text = 'MARAAAAAAA'; + }, + ) + ], + ), + ), + + // ------ MONGOLIAN REGISTER NO DEMO ------ + Divider(), + Container( + padding: EdgeInsets.all(10), + child: Row( + children: [ + Container( + width: 90, + child: BlockInput( + controller: cyrillicInputController, + keyboardType: BlockInputKeyboardType.mnCyrillic, + axisAlignment: MainAxisAlignment.spaceBetween, + style: BlockInputStyle( + keyboardStyle: BlockKeyboardStyle( + keyColor: Colors.blueAccent, + backgroundColor: Colors.white, + width: 40, + height: 40, + textStyle: TextStyle(), + keyShape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(18.0), + side: BorderSide(color: Colors.red) + ), + ), + backgroundColor: Colors.white, + border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(7)), borderSide: BorderSide(color: Colors.blueGrey, width: 1) ), @@ -147,27 +144,47 @@ class _MyAppState extends State { ) ), ), + ), + SizedBox(width: 10,), + Container( + child: Expanded( + child: TextField( + controller: numberInputController, + keyboardType: TextInputType.number, + decoration: InputDecoration( + contentPadding: EdgeInsets.symmetric(vertical: 0, horizontal: 5), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(7)), + borderSide: BorderSide(color: Colors.blueGrey, width: 1) + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(7)), + borderSide: BorderSide(color: Colors.blue, width: 3) + ) + ), + ), + ) ) - ) - ], - ), - ), - Container( - padding: EdgeInsets.all(10), - child: Row( - children: [ - RaisedButton( - child: Text('GET REGISTER'), - onPressed: () { - print(cyrillicInputController.text + numberInputController.text); - }, + ], + ), + ), + Container( + padding: EdgeInsets.all(10), + child: Row( + children: [ + RaisedButton( + child: Text('GET REGISTER'), + onPressed: () { + print(cyrillicInputController.text + numberInputController.text); + }, + ), + ], ), - ], - ), + ), + ], ), - ], - ), - ) + ) + ), ), ); } diff --git a/example/pubspec.lock b/example/pubspec.lock index f6e712c..94c6101 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -69,6 +69,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_custom_dialog: + dependency: transitive + description: + name: flutter_custom_dialog + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.20" flutter_test: dependency: "direct dev" description: flutter diff --git a/lib/block_input.dart b/lib/block_input.dart index 41a81da..814bfaa 100644 --- a/lib/block_input.dart +++ b/lib/block_input.dart @@ -1,27 +1,30 @@ import 'package:block_input/block_input_controller.dart'; import 'package:block_input/block_input_style.dart'; +import 'package:block_input/block_keyboard_style.dart'; import 'package:block_input/input/character_input.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_custom_dialog/flutter_custom_dialog.dart'; import 'block_input_keyboard_type.dart'; +import 'input/mn_keyboard.dart'; class BlockInput extends StatefulWidget { final String errorMessage; final TextStyle errorMessageStyle; - final BlockInputController blockInputController; - final BlockInputKeyboardType blockInputKeyboardType; - final BlockInputStyle blockInputStyle; + final BlockInputController controller; + final BlockInputKeyboardType keyboardType; + final BlockInputStyle style; final MainAxisAlignment axisAlignment; const BlockInput({ Key key, this.errorMessage, - this.blockInputKeyboardType = BlockInputKeyboardType.text, - this.blockInputStyle, + this.keyboardType = BlockInputKeyboardType.text, + this.style, this.errorMessageStyle, - this.blockInputController, + this.controller, this.axisAlignment = MainAxisAlignment.spaceBetween, }) : super(key: key); @@ -30,9 +33,9 @@ class BlockInput extends StatefulWidget { return _BlockInputState( errorMessage: errorMessage, errorMessageStyle: errorMessageStyle, - blockInputKeyboardType: blockInputKeyboardType, - blockInputStyle: blockInputStyle, - blockInputController: (blockInputController == null) ? BlockInputController(4) : blockInputController, + keyboardType: keyboardType, + style: style, + controller: (controller == null) ? BlockInputController(4) : controller, axisAlignment: axisAlignment ); } @@ -42,20 +45,21 @@ class _BlockInputState extends State { final String errorMessage; final TextStyle errorMessageStyle; - final BlockInputKeyboardType blockInputKeyboardType; - final BlockInputStyle blockInputStyle; - final BlockInputController blockInputController; + final BlockInputKeyboardType keyboardType; + final BlockInputStyle style; + final BlockInputController controller; final MainAxisAlignment axisAlignment; List _controllerList = List(); List _focusControllerList = List(); List _charInputList = List(); + bool _isKeyboardShowing = false; _BlockInputState({ @required this.errorMessage, - @required this.blockInputKeyboardType, - @required this.blockInputStyle, - this.blockInputController, + @required this.keyboardType, + @required this.style, + this.controller, this.errorMessageStyle, this.axisAlignment, }); @@ -64,64 +68,76 @@ class _BlockInputState extends State { void initState() { super.initState(); - blockInputController.addListener(() { - String textValue = blockInputController.text; + controller.addListener(() { + String textValue = controller.text; for(int i = 0; i < _controllerList.length; i++) { if(textValue.length > i) { - _controllerList[i].text = blockInputController.text[i]; + _controllerList[i].text = controller.text[i]; } else { _controllerList[i].text = ''; } } - if(textValue.length == 0) { - FocusScope.of(context).requestFocus(_focusControllerList[0]); - } else { - FocusScope.of(context).requestFocus(_focusControllerList[textValue.length-1]); + if(keyboardType != BlockInputKeyboardType.mnCyrillic) { + if(textValue.length == 0) { + FocusScope.of(context).requestFocus(_focusControllerList[0]); + } else { + FocusScope.of(context).requestFocus(_focusControllerList[textValue.length-1]); + } } }); - for(int i = 0; i < blockInputController.size; i++) { + for(int i = 0; i < controller.size; i++) { TextEditingController textController = TextEditingController(); FocusNode focusNode = FocusNode(); Function fx = (value) {}; - if(i == 0) { - fx = (value) { - blockInputController.text = _getText(); - if(value.length == 1) { - FocusScope.of(context).requestFocus(_focusControllerList[i+1]); + if(keyboardType == BlockInputKeyboardType.mnCyrillic) { + focusNode.addListener(() { + if(!_isKeyboardShowing) { + _isKeyboardShowing = true; + focusNode.unfocus(); + _buildMNKeyboard(context, style.keyboardStyle, textController, focusNode).show(); } - }; - } else if(i == blockInputController.size - 1) { - fx = (value) { - blockInputController.text = _getText(); - if(value.length == 0) { - FocusScope.of(context).requestFocus(_focusControllerList[i-1]); - } - }; + }); } else { - fx = (value) { - blockInputController.text = _getText(); - if(value.length == 1) { - FocusScope.of(context).requestFocus(_focusControllerList[i+1]); - } else if(value.length == 0) { - FocusScope.of(context).requestFocus(_focusControllerList[i-1]); - } - }; + if(i == 0) { + fx = (value) { + controller.text = _getText(); + if(value.length == 1) { + FocusScope.of(context).requestFocus(_focusControllerList[i+1]); + } + }; + } else if(i == controller.size - 1) { + fx = (value) { + controller.text = _getText(); + if(value.length == 0) { + FocusScope.of(context).requestFocus(_focusControllerList[i-1]); + } + }; + } else { + fx = (value) { + controller.text = _getText(); + if(value.length == 1) { + FocusScope.of(context).requestFocus(_focusControllerList[i+1]); + } else if(value.length == 0) { + FocusScope.of(context).requestFocus(_focusControllerList[i-1]); + } + }; + } } - if(blockInputKeyboardType == BlockInputKeyboardType.mnCyrillic) { + if(keyboardType == BlockInputKeyboardType.mnCyrillic) { _charInputList.add(CharacterInput( textController: textController, focusNode: focusNode, onChange: fx, isCyrillic: true, - blockInputStyle: blockInputStyle, + blockInputStyle: style, )); } else { TextInputType textInputType = - (blockInputKeyboardType == BlockInputKeyboardType.text) ? TextInputType.text : - (blockInputKeyboardType == BlockInputKeyboardType.number) ? TextInputType.number : TextInputType.text; + (keyboardType == BlockInputKeyboardType.text) ? TextInputType.text : + (keyboardType == BlockInputKeyboardType.number) ? TextInputType.number : TextInputType.text; _charInputList.add(CharacterInput( textController: textController, @@ -129,7 +145,7 @@ class _BlockInputState extends State { onChange: fx, isCyrillic: false, keyboardType: textInputType, - blockInputStyle: blockInputStyle, + blockInputStyle: style, )); } _controllerList.add(textController); @@ -139,7 +155,7 @@ class _BlockInputState extends State { @override void dispose() { - for(int i = 0; i < blockInputController.size; i++) { + for(int i = 0; i < controller.size; i++) { _controllerList[i]?.dispose(); _focusControllerList[i]?.dispose(); } @@ -170,8 +186,36 @@ class _BlockInputState extends State { ); } + YYDialog _buildMNKeyboard( + BuildContext context, BlockKeyboardStyle style, + TextEditingController textController, FocusNode focusNode + ) { + YYDialog dialog = YYDialog(); + focusNode.unfocus(); + dialog.build(context) + ..width = MediaQuery.of(context).size.width + ..gravity = Gravity.bottom + ..borderRadius = 12 + ..backgroundColor = style.backgroundColor + ..barrierColor = Color(0x45000000) + ..widget( + Container( + padding: EdgeInsets.symmetric(vertical: 30), + child: MNKeyboard(style: style, controller: textController, dialog: dialog,), + ), + ) + ..dismissCallBack = () { + this.controller.text = _getText(); + _isKeyboardShowing = false; + focusNode.unfocus(); + }; + focusNode.unfocus(); + return dialog; + } } + + Widget _buildErrorMessage(String error, TextStyle errorStyle) { if(error == null) return SizedBox(); return Text(error, style: errorStyle.copyWith(),); diff --git a/lib/block_input_style.dart b/lib/block_input_style.dart index a2804f8..af2116e 100644 --- a/lib/block_input_style.dart +++ b/lib/block_input_style.dart @@ -1,3 +1,4 @@ +import 'package:block_input/block_keyboard_style.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -12,8 +13,9 @@ class BlockInputStyle with Diagnosticable { final TextStyle textStyle; final double width; final bool isExtended; + final BlockKeyboardStyle keyboardStyle; - BlockInputStyle({ + const BlockInputStyle({ this.backgroundColor = Colors.white, this.padding = const EdgeInsets.all(10), this.margin = const EdgeInsets.all(5), @@ -29,7 +31,8 @@ class BlockInputStyle with Diagnosticable { this.textStyle = const TextStyle( fontSize: 19 ), - this.width = 42 + this.width = 42, + this.keyboardStyle, }); BlockInputStyle apply () { diff --git a/lib/block_keyboard_style.dart b/lib/block_keyboard_style.dart new file mode 100644 index 0000000..395d481 --- /dev/null +++ b/lib/block_keyboard_style.dart @@ -0,0 +1,41 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/foundation.dart'; + +@immutable +class BlockKeyboardStyle with Diagnosticable { + final TextStyle textStyle; + final Color keyColor; + final Color backgroundColor; + final ShapeBorder keyShape; + final double width; + final double height; + + const BlockKeyboardStyle({ + this.textStyle, + this.keyColor, + this.backgroundColor, + this.keyShape, + this.width = 40, + this.height = 40, + }); + + BlockKeyboardStyle apply () { + // TODO: implement + throw UnimplementedError(); + } + + BlockKeyboardStyle copyWith() { + // TODO: implement + throw UnimplementedError(); + } + + BlockKeyboardStyle merge() { + // TODO: implement + throw UnimplementedError(); + } + + BlockKeyboardStyle compareTo() { + // TODO: implement + throw UnimplementedError(); + } +} \ No newline at end of file diff --git a/lib/input/mn_keyboard.dart b/lib/input/mn_keyboard.dart new file mode 100644 index 0000000..92b3e17 --- /dev/null +++ b/lib/input/mn_keyboard.dart @@ -0,0 +1,112 @@ +import 'package:block_input/block_keyboard_style.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_custom_dialog/flutter_custom_dialog.dart'; + +class MNKeyboard extends StatelessWidget { + + final BlockKeyboardStyle style; + final TextEditingController controller; + final YYDialog dialog; + + const MNKeyboard({ + Key key, + this.style, + this.controller, + this.dialog + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + _buildKey('А'), + _buildKey('Б'), + _buildKey('В'), + _buildKey('Г'), + _buildKey('Д'), + _buildKey('Е'), + _buildKey('Ё'), + ], + ), + SizedBox(height: 10,), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + _buildKey('Ж'), + _buildKey('З'), + _buildKey('И'), + _buildKey('Й'), + _buildKey('К'), + _buildKey('Л'), + _buildKey('М'), + ], + ), + SizedBox(height: 10,), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + _buildKey('Н'), + _buildKey('О'), + _buildKey('Ө'), + _buildKey('П'), + _buildKey('Р'), + _buildKey('С'), + _buildKey('Т'), + + ], + ), + SizedBox(height: 10,), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + _buildKey('У'), + _buildKey('Ү'), + _buildKey('Ф'), + _buildKey('Х'), + _buildKey('Ц'), + _buildKey('Ч'), + _buildKey('Ш'), + + ], + ), + SizedBox(height: 10,), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + mainAxisSize: MainAxisSize.max, + children: [ + _buildKey('Щ'), + _buildKey('Ы'), + _buildKey('Э'), + _buildKey('Ю'), + _buildKey('Я'), + ], + ), + ], + ); + } + + Widget _buildKey(String char) { + return Container( + width: style.width, + height: style.height, + child: RaisedButton( + color: Colors.blueAccent, + visualDensity: VisualDensity.compact, + shape: style.keyShape, + onPressed: () { + dialog.dismiss(); + controller.text = char; + }, + child: Text(char, style: style.textStyle,), + ), + ); + } +} \ No newline at end of file diff --git a/pubspec.lock b/pubspec.lock index 24007b6..5449860 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -55,6 +55,13 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_custom_dialog: + dependency: "direct main" + description: + name: flutter_custom_dialog + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.20" flutter_test: dependency: "direct dev" description: flutter diff --git a/pubspec.yaml b/pubspec.yaml index f5f2cbb..86f142b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,6 +11,7 @@ environment: dependencies: flutter: sdk: flutter + flutter_custom_dialog: 1.0.20 dev_dependencies: flutter_test: diff --git a/test/block_input_test.dart b/test/block_input_test.dart index 272eb7d..d8a55b5 100644 --- a/test/block_input_test.dart +++ b/test/block_input_test.dart @@ -26,9 +26,9 @@ void main() { BlockInputController controller = BlockInputController(6); Widget blockInput = BlockInput( - blockInputController: controller, - blockInputKeyboardType: BlockInputKeyboardType.number, - blockInputStyle: BlockInputStyle( + controller: controller, + keyboardType: BlockInputKeyboardType.number, + style: BlockInputStyle( backgroundColor: Colors.black12, border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10)),