diff --git a/README.md b/README.md index 60ec0fa..2cf62db 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ This causes a lot of inconvenience for users, so this package allows adding func - You can use it for Android, iOS or both platforms. - Compatible with Dialog. -Example of the custom footer: +Example of the custom footer: Screen Shot 2019-05-22 at 5 46 50 PM @@ -84,16 +84,14 @@ class _ContentState extends State { KeyboardActionsItem( focusNode: _nodeText1, ), - KeyboardActionsItem(focusNode: _nodeText2, toolbarButtons: [ - (node) { - return GestureDetector( - onTap: () => node.unfocus(), - child: Padding( - padding: EdgeInsets.all(8.0), - child: Icon(Icons.close), - ), - ); - } + KeyboardActionsItem(focusNode: _nodeText2, toolbarButtons: (node, _, __, ___) => [ + GestureDetector( + onTap: () => node.unfocus(), + child: Padding( + padding: EdgeInsets.all(8.0), + child: Icon(Icons.close), + ), + ), ]), KeyboardActionsItem( focusNode: _nodeText3, @@ -117,37 +115,33 @@ class _ContentState extends State { focusNode: _nodeText4, displayCloseWidget: false, ), - KeyboardActionsItem( + KeyboardActionsItem( focusNode: _nodeText5, - toolbarButtons: [ + toolbarButtons: (node, _, __, ___) => [ //button 1 - (node) { - return GestureDetector( - onTap: () => node.unfocus(), - child: Container( - color: Colors.white, - padding: EdgeInsets.all(8.0), - child: Text( - "CLOSE", - style: TextStyle(color: Colors.black), - ), + GestureDetector( + onTap: () => node.unfocus(), + child: Container( + color: Colors.white, + padding: EdgeInsets.all(8.0), + child: Text( + "CLOSE", + style: TextStyle(color: Colors.black), ), - ); - }, + ), + ), //button 2 - (node) { - return GestureDetector( - onTap: () => node.unfocus(), - child: Container( - color: Colors.black, - padding: EdgeInsets.all(8.0), - child: Text( - "DONE", - style: TextStyle(color: Colors.white), - ), + GestureDetector( + onTap: () => node.unfocus(), + child: Container( + color: Colors.black, + padding: EdgeInsets.all(8.0), + child: Text( + "DONE", + style: TextStyle(color: Colors.white), ), - ); - } + ), + ) ], ), KeyboardActionsItem( @@ -413,4 +407,4 @@ class CounterKeyboard extends StatelessWidget -You can follow me on twitter [@diegoveloper](https://www.twitter.com/diegoveloper) +You can follow me on twitter [@diegoveloper](https://www.twitter.com/diegoveloper) \ No newline at end of file diff --git a/example/lib/content.dart b/example/lib/content.dart index 64ed171..d910fa2 100644 --- a/example/lib/content.dart +++ b/example/lib/content.dart @@ -14,29 +14,18 @@ class Content extends StatefulWidget { class _ContentState extends State { final FocusNode _nodeText1 = FocusNode(); - final FocusNode _nodeText2 = FocusNode(); - final FocusNode _nodeText3 = FocusNode(); - final FocusNode _nodeText4 = FocusNode(); - final FocusNode _nodeText5 = FocusNode(); - final FocusNode _nodeText6 = FocusNode(); - final FocusNode _nodeText7 = FocusNode(); - final FocusNode _nodeText8 = FocusNode(); - final FocusNode _nodeText9 = FocusNode(); - final FocusNode _nodeText10 = FocusNode(); final custom1Notifier = ValueNotifier("0"); - final custom2Notifier = ValueNotifier(Colors.blue); - final custom3Notifier = ValueNotifier(""); /// Creates the [KeyboardActionsConfig] to hook up the fields @@ -50,16 +39,14 @@ class _ContentState extends State { KeyboardActionsItem( focusNode: _nodeText1, ), - KeyboardActionsItem(focusNode: _nodeText2, toolbarButtons: [ - (node) { - return GestureDetector( - onTap: () => node.unfocus(), - child: Padding( - padding: EdgeInsets.all(8.0), - child: Icon(Icons.close), - ), - ); - } + KeyboardActionsItem(focusNode: _nodeText2, toolbarButtons: (node, _, __, ___) => [ + GestureDetector( + onTap: () => node.unfocus(), + child: Padding( + padding: EdgeInsets.all(8.0), + child: Icon(Icons.close), + ), + ), ]), KeyboardActionsItem( focusNode: _nodeText3, @@ -85,35 +72,31 @@ class _ContentState extends State { ), KeyboardActionsItem( focusNode: _nodeText5, - toolbarButtons: [ + toolbarButtons: (node, _, __, ___) => [ //button 1 - (node) { - return GestureDetector( - onTap: () => node.unfocus(), - child: Container( - color: Colors.white, - padding: EdgeInsets.all(8.0), - child: Text( - "CLOSE", - style: TextStyle(color: Colors.black), - ), + GestureDetector( + onTap: () => node.unfocus(), + child: Container( + color: Colors.white, + padding: EdgeInsets.all(8.0), + child: Text( + "CLOSE", + style: TextStyle(color: Colors.black), ), - ); - }, + ), + ), //button 2 - (node) { - return GestureDetector( - onTap: () => node.unfocus(), - child: Container( - color: Colors.black, - padding: EdgeInsets.all(8.0), - child: Text( - "DONE", - style: TextStyle(color: Colors.white), - ), + GestureDetector( + onTap: () => node.unfocus(), + child: Container( + color: Colors.black, + padding: EdgeInsets.all(8.0), + child: Text( + "DONE", + style: TextStyle(color: Colors.white), ), - ); - } + ), + ) ], ), KeyboardActionsItem( diff --git a/example/lib/sample.dart b/example/lib/sample.dart index b5618a3..779fae5 100644 --- a/example/lib/sample.dart +++ b/example/lib/sample.dart @@ -33,6 +33,7 @@ class Sample extends StatelessWidget { tapOutsideBehavior: TapOutsideBehavior.opaqueDismiss, config: KeyboardActionsConfig( keyboardSeparatorColor: Colors.purple, + keyboardSeparatorThickness: 5, actions: [ KeyboardActionsItem( focusNode: _focusNodeName, diff --git a/example/lib/sample3.dart b/example/lib/sample3.dart index 308df75..a9c89a1 100644 --- a/example/lib/sample3.dart +++ b/example/lib/sample3.dart @@ -3,8 +3,7 @@ import 'package:keyboard_actions/keyboard_actions.dart'; /// Sample [Widget] demonstrating the usage of [KeyboardActionsConfig.defaultDoneWidget]. class Sample3 extends StatelessWidget { - final _focusNodes = - Iterable.generate(7).map((_) => FocusNode()).toList(); + final _focusNodes = Iterable.generate(7).map((_) => FocusNode()).toList(); @override Widget build(BuildContext context) { @@ -19,10 +18,8 @@ class Sample3 extends StatelessWidget { tapOutsideBehavior: TapOutsideBehavior.translucentDismiss, config: KeyboardActionsConfig( // Define ``defaultDoneWidget`` only once in the config - defaultDoneWidget: _buildMyDoneWidget(), - actions: _focusNodes - .map((focusNode) => KeyboardActionsItem(focusNode: focusNode)) - .toList(), + defaultDoneWidget: _buildMyDoneWidget, + actions: _focusNodes.map((focusNode) => KeyboardActionsItem(focusNode: focusNode)).toList(), ), child: ListView.separated( itemBuilder: (ctx, idx) => TextField( @@ -42,14 +39,20 @@ class Sample3 extends StatelessWidget { } /// Returns the custom [Widget] to be rendered as the *"Done"* button. - Widget _buildMyDoneWidget() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('My Done Widget'), - const SizedBox(width: 10.0), - Icon(Icons.arrow_drop_down, size: 20.0), - ], + Widget _buildMyDoneWidget(void Function() closeAction) { + return Padding( + padding: const EdgeInsets.all(5.0), + child: InkWell( + onTap: closeAction, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('My Done Widget'), + const SizedBox(width: 10.0), + Icon(Icons.arrow_drop_down, size: 20.0), + ], + ), + ), ); } } diff --git a/example/lib/sample4.dart b/example/lib/sample4.dart index e7c2e01..c6c4ae5 100644 --- a/example/lib/sample4.dart +++ b/example/lib/sample4.dart @@ -23,31 +23,23 @@ class Sample4 extends StatelessWidget { toolbarAlignment: MainAxisAlignment.spaceAround, focusNode: _focusSample, displayArrows: false, - toolbarButtons: [ - (_) { - return IconButton( - icon: Icon(Icons.format_bold), - onPressed: () {}, - ); - }, - (_) { - return IconButton( - icon: Icon(Icons.format_italic), - onPressed: () {}, - ); - }, - (_) { - return IconButton( + toolbarButtons: (_, __, ___, ____) => [ + IconButton( + icon: Icon(Icons.format_bold), + onPressed: () {}, + ), + IconButton( + icon: Icon(Icons.format_italic), + onPressed: () {}, + ), + IconButton( icon: Icon(Icons.format_underline), onPressed: () {}, - ); - }, - (_) { - return IconButton( - icon: Icon(Icons.format_strikethrough), - onPressed: () {}, - ); - }, + ), + IconButton( + icon: Icon(Icons.format_strikethrough), + onPressed: () {}, + ), ], ), ], diff --git a/example/lib/sample5.dart b/example/lib/sample5.dart index a993e0c..8f55843 100644 --- a/example/lib/sample5.dart +++ b/example/lib/sample5.dart @@ -1,10 +1,10 @@ import 'package:flutter/material.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; -/// Sample [Widget] demonstrating the usage of [KeyboardActionsConfig.defaultDoneWidget]. +/// Sample [Widget] demonstrating the usage of [KeyboardActionsConfig.defaultDoneWidget], +/// [KeyboardActionsItem.toolbarButtons] and other customizations. class Sample5 extends StatelessWidget { - final _focusNodes = - Iterable.generate(7).map((_) => FocusNode()).toList(); + final _focusNodes = Iterable.generate(7).map((_) => FocusNode()).toList(); @override Widget build(BuildContext context) { @@ -21,12 +21,47 @@ class Sample5 extends StatelessWidget { child: KeyboardActions( tapOutsideBehavior: TapOutsideBehavior.translucentDismiss, config: KeyboardActionsConfig( + defaultBarHeight: 70, + defaultDoneWidget: _buildMyDoneWidget, // Define ``defaultDoneWidget`` only once in the config - defaultDoneWidget: _buildMyDoneWidget(), - actions: _focusNodes - .map((focusNode) => - KeyboardActionsItem(focusNode: focusNode)) - .toList(), + defaultPreviousWidget: (defaultPrevious) => _buildMyPreviousWidget(defaultPrevious), + defaultNextWidget: (defaultNext) => _buildMyNextWidget(defaultNext), + actions: _focusNodes.map((focusNode) { + //For the last field, we want different arrows as well, different from the default we built + if (_focusNodes.indexOf(focusNode) == 0) { + return KeyboardActionsItem( + focusNode: focusNode, + displayArrows: false, + displayDoneButton: false, + toolbarAlignment: MainAxisAlignment.center, + toolbarButtons: (node, closeAction, previousAction, nextAction) => [ + SizedBox( + width: 80, + child: IconButton( + icon: Icon(Icons.keyboard_arrow_up), + tooltip: 'Custom Previous', + iconSize: 38, + color: Colors.orange, + disabledColor: Colors.red.shade900, + onPressed: previousAction, + ), + ), + SizedBox( + width: 80, + child: IconButton( + icon: Icon(Icons.keyboard_arrow_down), + tooltip: 'Custom Previous', + iconSize: 38, + color: Colors.red, + disabledColor: Colors.red.shade900, + onPressed: nextAction, + ), + ), + ], + ); + } + return KeyboardActionsItem(focusNode: focusNode); + }).toList(), ), child: ListView.separated( itemBuilder: (ctx, idx) => TextField( @@ -38,8 +73,7 @@ class Sample5 extends StatelessWidget { labelText: "Field ${idx + 1}", ), ), - separatorBuilder: (ctx, idx) => - const SizedBox(height: 10.0), + separatorBuilder: (ctx, idx) => const SizedBox(height: 10.0), itemCount: _focusNodes.length, ), ), @@ -62,14 +96,44 @@ class Sample5 extends StatelessWidget { } /// Returns the custom [Widget] to be rendered as the *"Done"* button. - Widget _buildMyDoneWidget() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('My Done Widget'), - const SizedBox(width: 10.0), - Icon(Icons.arrow_drop_down, size: 20.0), - ], + Widget _buildMyDoneWidget(void Function() closeAction) { + return Padding( + padding: const EdgeInsets.all(5.0), + child: InkWell( + onTap: closeAction, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('My Done Widget'), + const SizedBox(width: 10.0), + Icon(Icons.arrow_drop_down, size: 20.0), + ], + ), + ), + ); + } + + /// Returns the custom [Widget] to be rendered as the *"Previous"* button. + Widget _buildMyPreviousWidget(void Function() previousAction) { + return IconButton( + icon: Icon(Icons.arrow_upward), + tooltip: 'New Default Previous', + iconSize: 24, + color: Colors.green, + disabledColor: Colors.blueGrey, + onPressed: previousAction, //You're able to do other things here before calling default Previous + ); + } + + /// Returns the custom [Widget] to be rendered as the *"Next"* button. + Widget _buildMyNextWidget(void Function() nextAction) { + return IconButton( + icon: Icon(Icons.arrow_downward), + tooltip: 'New Default Next', + iconSize: 24, + color: Colors.green, + disabledColor: Colors.blueGrey, + onPressed: nextAction, ); } } diff --git a/lib/keyboard_actions.dart b/lib/keyboard_actions.dart index 310133b..787652f 100644 --- a/lib/keyboard_actions.dart +++ b/lib/keyboard_actions.dart @@ -411,7 +411,7 @@ class KeyboardActionstate extends State } double newOffset = _currentAction!.displayActionBar - ? _kBarSize + ? (config?.defaultBarHeight ?? _kBarSize) : 0; // offset for the actions bar final keyboardHeight = EdgeInsets.fromWindowPadding( @@ -498,18 +498,27 @@ class KeyboardActionstate extends State /// Build the keyboard action bar based on the current [config]. Widget _buildBar(bool displayArrows) { + final closeAction = () { + if (_currentAction?.onTapAction != null) { + _currentAction!.onTapAction!(); + } + _clearFocus(); + }; + final previousAction = _previousIndex != null ? _onTapUp : null; + final nextAction = _nextIndex != null ? _onTapDown : null; + return AnimatedCrossFade( duration: _timeToDismiss, crossFadeState: _isShowing ? CrossFadeState.showFirst : CrossFadeState.showSecond, firstChild: Container( - height: _kBarSize, + height: config!.defaultBarHeight ?? _kBarSize, width: MediaQuery.of(context).size.width, decoration: BoxDecoration( border: Border( top: BorderSide( - color: widget.config.keyboardSeparatorColor, - width: 1.0, + color: config!.keyboardSeparatorColor, + width: config!.keyboardSeparatorThickness, ), ), ), @@ -521,55 +530,47 @@ class KeyboardActionstate extends State _currentAction?.toolbarAlignment ?? MainAxisAlignment.end, children: [ if (config!.nextFocus && displayArrows) ...[ - IconButton( + config!.defaultPreviousWidget?.call(previousAction) ?? IconButton( icon: Icon(Icons.keyboard_arrow_up), tooltip: 'Previous', iconSize: IconTheme.of(context).size!, color: IconTheme.of(context).color, disabledColor: Theme.of(context).disabledColor, - onPressed: _previousIndex != null ? _onTapUp : null, + onPressed: previousAction, ), - IconButton( + config!.defaultNextWidget?.call(nextAction) ?? IconButton( icon: Icon(Icons.keyboard_arrow_down), tooltip: 'Next', iconSize: IconTheme.of(context).size!, color: IconTheme.of(context).color, disabledColor: Theme.of(context).disabledColor, - onPressed: _nextIndex != null ? _onTapDown : null, + onPressed: nextAction, ), const Spacer(), ], if (_currentAction?.displayDoneButton != null && _currentAction!.displayDoneButton && (_currentAction!.toolbarButtons == null || - _currentAction!.toolbarButtons!.isEmpty)) - Padding( + _currentAction!.toolbarButtons!(_currentAction!.focusNode, closeAction, previousAction, nextAction).isEmpty)) + config?.defaultDoneWidget?.call(closeAction) ?? Padding( padding: const EdgeInsets.all(5.0), child: InkWell( - onTap: () { - if (_currentAction?.onTapAction != null) { - _currentAction!.onTapAction!(); - } - _clearFocus(); - }, + onTap: closeAction, child: Container( - padding: + padding: EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0), - child: config?.defaultDoneWidget ?? - Text( - "Done", - style: TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.w500, - ), + child:Text( + "Done", + style: TextStyle( + fontSize: 16.0, + fontWeight: FontWeight.w500, ), + ), ), ), ), if (_currentAction?.toolbarButtons != null) - ..._currentAction!.toolbarButtons! - .map((item) => item(_currentAction!.focusNode)) - .toList() + ..._currentAction!.toolbarButtons!(_currentAction!.focusNode, closeAction, previousAction, nextAction) ], ), ), diff --git a/lib/keyboard_actions_config.dart b/lib/keyboard_actions_config.dart index 10309bd..4af0f80 100644 --- a/lib/keyboard_actions_config.dart +++ b/lib/keyboard_actions_config.dart @@ -19,12 +19,24 @@ class KeyboardActionsConfig { /// Elevation of the Custom keyboard buttons final double? keyboardBarElevation; + /// Thickness of the line separator between keyboard and content, defaults to 1.0 + final double keyboardSeparatorThickness; + /// Color of the line separator between keyboard and content final Color keyboardSeparatorColor; + /// The height to be optionally used instead of the Default bar height, defaults to 45 + final double? defaultBarHeight; + /// A [Widget] to be optionally used instead of the "Done" button /// which dismisses the keyboard. - final Widget? defaultDoneWidget; + final Widget? Function(void Function()? closeAction)? defaultDoneWidget; + + /// A [Widget] to be optionally used instead of the "Previous" button. + final Widget? Function(void Function()? previousAction)? defaultPreviousWidget; + + /// A [Widget] to be optionally used instead of the "Next" button. + final Widget? Function(void Function()? nextAction)? defaultNextWidget; const KeyboardActionsConfig({ this.keyboardActionsPlatform = KeyboardActionsPlatform.ALL, @@ -32,7 +44,11 @@ class KeyboardActionsConfig { this.actions, this.keyboardBarColor, this.keyboardBarElevation, + this.keyboardSeparatorThickness = 1.0, this.keyboardSeparatorColor = Colors.transparent, + this.defaultBarHeight, this.defaultDoneWidget, + this.defaultPreviousWidget, + this.defaultNextWidget, }); } diff --git a/lib/keyboard_actions_item.dart b/lib/keyboard_actions_item.dart index 9c0a248..7992e1b 100644 --- a/lib/keyboard_actions_item.dart +++ b/lib/keyboard_actions_item.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -typedef ButtonBuilder = Widget Function(FocusNode focusNode); +typedef ButtonsBuilder = List Function(FocusNode focusNode, void Function() closeAction, void Function()? previousAction, void Function()? nextAction); ///Class to define the `focusNode` that you pass to your `TextField` too and other params to customize ///the bar that will appear over your keyboard @@ -10,7 +10,7 @@ class KeyboardActionsItem { /// Optional widgets to display to the right of the bar/ /// NOTE: `toolbarButtons` override the Done button by default - final List? toolbarButtons; + final ButtonsBuilder? toolbarButtons; /// true [default] to display the Done button final bool displayDoneButton;