keyboard_actions 3.2.1+1

  • Readme
  • Changelog
  • Example
  • Installing
  • 98

Keyboard Actions #

pub package

Add features to the Android / iOS keyboard in a simple way.

Because the keyboard that Android / iOS offers us specifically when we are in numeric mode, does not bring the button to hide the keyboard. This causes a lot of inconvenience for users, so this package allows adding functionality to the existing keyboard.

Features #

  • Done button for the keyboard (You can customize the button).
  • Move up/down between your Textfields (You can hide for set nextFocus: false).
  • Keyboard Bar customization.
  • Custom footer widget below keyboard bar
  • Create your own Keyboard in an easy way
  • You can use it for Android, iOS or both platforms.
  • Compatible with Dialog.

Example of the custom footer:

Screen Shot 2019-05-22 at 5 46 50 PM

For more fun, use that widget as a custom keyboard with your custom input:

Screen Shot 2019-05-22 at 5 46 54 PM

Even more fun:

Watch the video

Getting started #

You should ensure that you add the dependency in your flutter project.

dependencies:
  keyboard_actions: "^3.2.0"

You should then run flutter packages upgrade or update your packages in IntelliJ.

Example Project #

There is an example project in the example folder where you can get more information. Check it out. Otherwise, keep reading to get up and running.

Usage #

import  'package:flutter/material.dart';
import  'package:keyboard_actions/keyboard_actions.dart';


class Content extends StatefulWidget {
  const Content({
    Key key,
  }) : super(key: key);

  @override
  _ContentState createState() => _ContentState();
}

class _ContentState extends State<Content> {
  final FocusNode _nodeText1 = FocusNode();
  final FocusNode _nodeText2 = FocusNode();
  final FocusNode _nodeText3 = FocusNode();
  final FocusNode _nodeText4 = FocusNode();
  final FocusNode _nodeText5 = FocusNode();
  final FocusNode _nodeText6 = FocusNode();

  /// Creates the [KeyboardActionsConfig] to hook up the fields
  /// and their focus nodes to our [FormKeyboardActions].
  KeyboardActionsConfig _buildConfig(BuildContext context) {
    return KeyboardActionsConfig(
      keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
      keyboardBarColor: Colors.grey[200],
      nextFocus: true,
      actions: [
        KeyboardAction(
          focusNode: _nodeText1,
        ),
        KeyboardAction(focusNode: _nodeText2, toolbarButtons: [
          (node) {
            return GestureDetector(
              onTap: () => node.unfocus(),
              child: Padding(
                padding: EdgeInsets.all(8.0),
                child: Icon(Icons.close),
              ),
            );
          }
        ]),
        KeyboardAction(
          focusNode: _nodeText3,
          onTapAction: () {
            showDialog(
                context: context,
                builder: (context) {
                  return AlertDialog(
                    content: Text("Custom Action"),
                    actions: <Widget>[
                      FlatButton(
                        child: Text("OK"),
                        onPressed: () => Navigator.of(context).pop(),
                      )
                    ],
                  );
                });
          },
        ),
        KeyboardAction(
          focusNode: _nodeText4,
          displayCloseWidget: false,
        ),
          KeyboardAction(
          focusNode: _nodeText5,
          toolbarButtons: [
            //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),
                  ),
                ),
              );
            },
            //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),
                  ),
                ),
              );
            }
          ],
        ),
        KeyboardAction(
          focusNode: _nodeText6,
          footerBuilder: (_) => PreferredSize(
              child: SizedBox(
                  height: 40,
                  child: Center(
                    child: Text('Custom Footer'),
                  )),
              preferredSize: Size.fromHeight(40)),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return KeyboardActions(
      config: _buildConfig(context),
      child: Center(
        child: Padding(
          padding: const EdgeInsets.all(15.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              TextField(
                keyboardType: TextInputType.number,
                focusNode: _nodeText1,
                decoration: InputDecoration(
                  hintText: "Input Number",
                ),
              ),
              TextField(
                keyboardType: TextInputType.text,
                focusNode: _nodeText2,
                decoration: InputDecoration(
                  hintText: "Input Text with Custom Done Button",
                ),
              ),
              TextField(
                keyboardType: TextInputType.number,
                focusNode: _nodeText3,
                decoration: InputDecoration(
                  hintText: "Input Number with Custom Action",
                ),
              ),
              TextField(
                keyboardType: TextInputType.text,
                focusNode: _nodeText4,
                decoration: InputDecoration(
                  hintText: "Input Text without Done button",
                ),
              ),
              TextField(
                keyboardType: TextInputType.number,
                focusNode: _nodeText5,
                decoration: InputDecoration(
                  hintText: "Input Number with Toolbar Buttons",
                ),
              ),
              TextField(
                keyboardType: TextInputType.number,
                focusNode: _nodeText6,
                decoration: InputDecoration(
                  hintText: "Input Number with Custom Footer",
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Using Custom Keyboard #

import  'package:flutter/material.dart';
import  'package:keyboard_actions/keyboard_actions.dart';

class Content extends StatelessWidget {
  final FocusNode _nodeText7 = FocusNode();
  final FocusNode _nodeText8 = FocusNode();
  //This is only for custom keyboards
  final custom1Notifier = ValueNotifier<String>("0");
  final custom2Notifier = ValueNotifier<Color>(Colors.blue);

  /// Creates the [KeyboardActionsConfig] to hook up the fields
  /// and their focus nodes to our [FormKeyboardActions].
  KeyboardActionsConfig _buildConfig(BuildContext context) {
    return KeyboardActionsConfig(
      keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
      keyboardBarColor: Colors.grey[200],
      nextFocus: true,
      actions: [
        KeyboardAction(
          focusNode: _nodeText7,
          footerBuilder: (_) => CounterKeyboard(
            notifier: custom1Notifier,
          ),
        ),
        KeyboardAction(
          focusNode: _nodeText8,
          footerBuilder: (_) => ColorPickerKeyboard(
            notifier: custom2Notifier,
          ),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return KeyboardActions(
      config: _buildConfig(context),
      child: Center(
        child: Container(
          padding: const EdgeInsets.all(15.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: <Widget>[
              KeyboardCustomInput<String>(
                focusNode: _nodeText7,
                height: 65,
                notifier: custom1Notifier,
                builder: (context, val, hasFocus) {
                  return Container(
                    alignment: Alignment.center,
                    color: hasFocus ? Colors.grey[300] : Colors.white,
                    child: Text(
                      val,
                      style:
                          TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
                    ),
                  );
                },
              ),
              KeyboardCustomInput<Color>(
                focusNode: _nodeText8,
                height: 65,
                notifier: custom2Notifier,
                builder: (context, val, hasFocus) {
                  return Container(
                    width: double.maxFinite,
                    color: val ?? Colors.transparent,
                  );
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}


/// A quick example "keyboard" widget for picking a color.
class ColorPickerKeyboard extends StatelessWidget
    with KeyboardCustomPanelMixin<Color>
    implements PreferredSizeWidget {
  final ValueNotifier<Color> notifier;
  static const double _kKeyboardHeight = 200;

  ColorPickerKeyboard({Key key, this.notifier}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final double rows = 3;
    final double screenWidth = MediaQuery.of(context).size.width;
    final int colorsCount = Colors.primaries.length;
    final int colorsPerRow = (colorsCount / rows).ceil();
    final double itemWidth = screenWidth / colorsPerRow;
    final double itemHeight = _kKeyboardHeight / rows;

    return Container(
      height: _kKeyboardHeight,
      child: Wrap(
        children: <Widget>[
          for (final color in Colors.primaries)
            GestureDetector(
              onTap: () {
                updateValue(color);
              },
              child: Container(
                color: color,
                width: itemWidth,
                height: itemHeight,
              ),
            )
        ],
      ),
    );
  }

  @override
  Size get preferredSize => Size.fromHeight(_kKeyboardHeight);
}

/// A quick example "keyboard" widget for counter value.
class CounterKeyboard extends StatelessWidget
    with KeyboardCustomPanelMixin<String>
    implements PreferredSizeWidget {
  final ValueNotifier<String> notifier;

  CounterKeyboard({Key key, this.notifier}) : super(key: key);

  @override
  Size get preferredSize => Size.fromHeight(200);

  @override
  Widget build(BuildContext context) {
    return Container(
      height: preferredSize.height,
      child: Row(
        children: [
          Expanded(
            child: InkWell(
              onTap: () {
                int value = int.tryParse(notifier.value) ?? 0;
                value--;
                updateValue(value.toString());
              },
              child: FittedBox(
                child: Text(
                  "-",
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ),
            ),
          ),
          Expanded(
            child: InkWell(
              onTap: () {
                int value = int.tryParse(notifier.value) ?? 0;
                value++;
                updateValue(value.toString());
              },
              child: FittedBox(
                child: Text(
                  "+",
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}


You can follow me on twitter @diegoveloper

[3.2.1+1] #

  • Bug fixed when Custom Keyboard has an area above footer builder. Thanks @lzhuor

[3.2.1] #

  • Exposed overscroll property in KeyboardActions in case you want an extra scroll below your focused input. (default: 12).

[3.2.0] BREAKING CHANGE #

  • displayArrows property was added in KeyboardAction.
  • closeWidget and displayCloseWidget were removed. Now you can add multiple toolbar buttons using toolbarButtons property from KeyboardAction (check the sample updated).
  • Set displayDoneButton to false if you don't want the DONE button by default.

[3.1.3] #

  • Now you can change the size of the arrow buttons using the Theme. (Check the sample.dart file from the example folder to get more info)
  • keyboardSeparatorColor was added in KeyboardActionsConfig to change the color of the line separator between keyboard and content.

[3.1.2] #

  • fixed issue with keyboardActionsPlatform.

[3.1.1] #

  • added tapOutsideToDismiss property inside KeyboardActions in case you want to press outside the keyboard to dismiss it.

[3.1.0] BREAKING CHANGE #

  • API improved
  • FormKeyboardActions was renamed to KeyboardActions.
  • KeyboardCustomInput was added to help you to create custom keyboards in an easy way.
  • added enabled property inside KeyboardActions in case you don't want to use KeyboardActions widget (tablets for example).
  • added displayActionBar property inside KeyboardAction in case you want to display/hide the keyboard bar (E.g: if you use footerBuilder and add your own done button inside that)
  • added isDialog property inside KeyboardActions.
  • Material color is transparent to avoid issues with the parent container.

[3.0.0] BREAKING CHANGE #

  • Restore the old API with some bug fixing

[2.1.2+2] #

  • Keyboard dismissed when press back on Android.

[2.1.2+1] #

  • Fixed issue when using CupertinoPageScaffold.

[2.1.2] #

  • Now you can use the IconTheme.of(context).color and Theme.of(context).disabledColor to set the colors of the arrow icons (up/down).

[2.1.0 - 2.1.1+1] #

  • Custom footer widget below keyboard bar
  • Now you can add your custom keyboard!!
  • Thanks @jayjwarrick again for the contribution

[2.0.1] #

  • Disable next & previous buttons when there is none

[2.0.0] ** Breaking change ** #

  • Now KeyboardActions works on Dialogs
  • Add KeyboardActionsConfig to make parameters easily swappable
  • Add FormKeyboardActions.setKeyboardActions and FormKeyboardActions to allow changing the config from anywhere in the child widget tree. (Check the sample)
  • Thanks @jayjwarrick for the contribution

[1.0.4] #

  • Added enabled attribute for KeyboardAction to skip the prev/next when the TextField is disabled

[1.0.3] #

  • Fixed android issue when return from background

[1.0.0 - 1.0.2] #

  • First release.

example/lib/main.dart

import 'package:example/content.dart';
import 'package:flutter/material.dart';
import 'sample.dart';
import 'sample2.dart';

// Application entry-point
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key key}) : super(key: key);

  _openWidget(BuildContext context, Widget widget) =>
      Navigator.of(context).push(
        MaterialPageRoute(builder: (_) => widget),
      );

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Scaffold(
        backgroundColor: Colors.amber,
        body: Builder(
          builder: (myContext) => Center(
            child: Padding(
              padding: const EdgeInsets.all(18.0),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  RaisedButton(
                    child: Text("Full Screen form"),
                    onPressed: () => _openWidget(
                      myContext,
                      ScaffoldTest(),
                    ),
                  ),
                  const SizedBox(
                    height: 25,
                  ),
                  RaisedButton(
                    child: Text("Dialog form"),
                    onPressed: () => _openWidget(
                      myContext,
                      DialogTest(),
                    ),
                  ),
                  const SizedBox(
                    height: 25,
                  ),
                  RaisedButton(
                    child: Text("Custom Sample 1"),
                    onPressed: () => _openWidget(
                      myContext,
                      Sample(),
                    ),
                  ),
                  const SizedBox(
                    height: 25,
                  ),
                  RaisedButton(
                    child: Text("Custom Sample 2"),
                    onPressed: () => _openWidget(
                      myContext,
                      Sample2(),
                    ),
                  )
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

/// Displays our [TextField]s in a [Scaffold] with a [FormKeyboardActions].
class ScaffoldTest extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Keyboard Actions Sample"),
      ),
      body: Content(),
    );
  }
}

/// Displays our [FormKeyboardActions] nested in a [AlertDialog].
class DialogTest extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Keyboard Actions Sample"),
      ),
      body: Center(
        child: FlatButton(
          color: Colors.blue,
          child: Text('Launch dialog'),
          onPressed: () => _launchInDialog(context),
        ),
      ),
    );
  }

  void _launchInDialog(BuildContext context) async {
    final height = MediaQuery.of(context).size.height / 3;
    await showDialog(
      context: context,
      builder: (context) {
        return AlertDialog(
          title: Text('Dialog test'),
          content: SizedBox(
            height: height,
            child: Content(
              isDialog: true,
            ),
          ),
          actions: [
            FlatButton(
              child: Text('Ok'),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
          ],
        );
      },
    );
  }
}

Use this package as a library

1. Depend on it

Add this to your package's pubspec.yaml file:


dependencies:
  keyboard_actions: ^3.2.1+1

2. Install it

You can install packages from the command line:

with Flutter:


$ flutter pub get

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

3. Import it

Now in your Dart code, you can use:


import 'package:keyboard_actions/keyboard_actions.dart';
  
Popularity:
Describes how popular the package is relative to other packages. [more]
95
Health:
Code health derived from static analysis. [more]
100
Maintenance:
Reflects how tidy and up-to-date the package is. [more]
100
Overall:
Weighted score of the above. [more]
98
Learn more about scoring.

We analyzed this package on Jul 8, 2020, and provided a score, details, and suggestions below. Analysis was completed with status completed using:

  • Dart: 2.8.4
  • pana: 0.13.14
  • Flutter: 1.17.5

Analysis suggestions

Package not compatible with SDK dart

Because:

  • keyboard_actions that is a package requiring null.

Package not compatible with runtime flutter-web on web

Because:

  • package:keyboard_actions/keyboard_actions.dart that imports:
  • dart:io

Health suggestions

Fix lib/external/bottom_area_avoider.dart. (-0.50 points)

Analysis of lib/external/bottom_area_avoider.dart reported 1 hint:

line 171 col 10: The member 'describeSemanticsConfiguration' can only be used within instance members of subclasses of 'package:flutter/src/rendering/object.dart'.

Format lib/keyboard_actions.dart.

Run flutter format to format lib/keyboard_actions.dart.

Dependencies

Package Constraint Resolved Available
Direct dependencies
Dart SDK >=2.3.0 <3.0.0
flutter 0.0.0
Transitive dependencies
collection 1.14.12 1.14.13
meta 1.1.8 1.2.1
sky_engine 0.0.99
typed_data 1.1.6 1.2.0
vector_math 2.0.8