Code Monkey home page Code Monkey logo

flutter_keyboard_actions's Introduction

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: "^4.1.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: [
        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: _nodeText3,
          onTapAction: () {
            showDialog(
                context: context,
                builder: (context) {
                  return AlertDialog(
                    content: Text("Custom Action"),
                    actions: <Widget>[
                      FlatButton(
                        child: Text("OK"),
                        onPressed: () => Navigator.of(context).pop(),
                      )
                    ],
                  );
                });
          },
        ),
        KeyboardActionsItem(
          focusNode: _nodeText4,
          displayCloseWidget: false,
        ),
          KeyboardActionsItem(
          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),
                  ),
                ),
              );
            }
          ],
        ),
        KeyboardActionsItem(
          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: [
        KeyboardActionsItem(
          focusNode: _nodeText7,
          footerBuilder: (_) => CounterKeyboard(
            notifier: custom1Notifier,
          ),
        ),
        KeyboardActionsItem(
          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

flutter_keyboard_actions's People

Contributors

aimsugita avatar alex-min avatar arenukvern avatar ashim-kr-saha avatar chinitadelrey avatar codingwithtashi avatar crizant avatar diegoveloper avatar f-person avatar fujidaiti avatar jaysephjw avatar lzhuor avatar monster555 avatar pedromorgan avatar peter-gy avatar raphire08 avatar roaa94 avatar shinriyo avatar sidrao2006 avatar stevenspiel avatar themanuz avatar urusai88 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flutter_keyboard_actions's Issues

customize position of close widget + keyboard doesn't close on second time

Hello,

First of all, thanks for this Widget 👍

I'd like to align the close widget on the left as it is getting collapsed by the floating button. (is it possible to have a closeWidgetAlignment ? If the closeWidget is in a Container, you can bind the closeWidgetAlignment to the Row mainAxisAlignment, isn't it ?

Also, the panel shouldn't inherit of any margin or padding in my humble opinion. It would make it more easy because right now, if your root widget has a margin, it will be applied to the panel. (see screenshot below).

Ask me if you need further information.

image

I was obliged to remove the margin/padding and move them to the content of the FormKeyboardActions widget.
image

Final result (I faced an issue where tapping on the close, then focus again the TextField, which open the keyboard again : then tapping the close widget only close the KeyboardAction panel but not the keyboard : second screenshot)
image
image

Skip next TextField when it's disabled

I have a situation where one of my TextFields can be disabled some times.
The problem I have is that the focus keep being put in that field when the user navigates with keyboard actions.

The image below shows that behavior.
Focus was in the Nome field and CPF field was disabled. When I navigate through the arrows, the focus is put inside CPF.

image

animation down to up

Can you animate the parts from down to up?
It appears before keyboard is finish moving.

スクリーンショット 2019-12-18 16 06 33

Feature Request: ExtraWidgets..

Thanks for the plugin from a flutter newbie ;-))

Would it be possible to also add ExtraWidgets ?
Atmo there is only the 'closeWidget'...

For my purpose, I want to add a "Validate" and "Info" widgets in addition to Close...
ie
Prev/Next .. space ... Validate/Info/Close

Doesn't work with ListView

Hi,
I try to use FormKeyboardActions with a ListView inside like in this example:

class StockInventoriesNewPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: SafeArea(
        child: FormKeyboardActions(
          child: Form(
            child: _buildListView()
          ),
        ),
      ),
    );
  }

  Widget _buildListView() {
    return ListView(
      children: [
        Text("1"),
        Text("2"),
        Text("3"),
      ],
    );
  }
}

I have an error:


════════ Exception caught by rendering library ═════════════════════════════════
The following assertion was thrown during performResize():
Vertical viewport was given unbounded height.

Viewports expand in the scrolling direction to fill their container.In this case, a vertical viewport was given an unlimited amount of vertical space in which to expand. This situation typically happens when a scrollable widget is nested inside another scrollable widget.
If this widget is always nested in a scrollable widget there is no need to use a viewport because there will always be enough vertical space for the children. In this case, consider using a Column instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size the height of the viewport to the sum of the heights of its children.
User-created ancestor of the error-causing widget was
    ListView 
lib/…/new/new.dart:35
When the exception was thrown, this was the stack
#0      RenderViewport.performResize.<anonymous closure> 
package:flutter/…/rendering/viewport.dart:1165
#1      RenderViewport.performResize 
package:flutter/…/rendering/viewport.dart:1218
#2      RenderObject.layout 
package:flutter/…/rendering/object.dart:1686
#3      RenderProxyBoxMixin.performLayout 
package:flutter/…/rendering/proxy_box.dart:105
#4      RenderObject.layout 
package:flutter/…/rendering/object.dart:1701
...
The following RenderObject was being processed when the exception was fired: RenderViewport#c7fe8 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
RenderObject: RenderViewport#c7fe8 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
    needs compositing
    parentData: <none> (can use size)
    constraints: BoxConstraints(w=414.0, 762.0<=h<=Infinity)
    size: MISSING
    axisDirection: down
    crossAxisDirection: right
    offset: ScrollPositionWithSingleContext#6726f(offset: 0.0, range: null..null, viewport: null, ScrollableState, AlwaysScrollableScrollPhysics -> BouncingScrollPhysics, IdleScrollActivity#d7e2b, ScrollDirection.idle)
    anchor: 0.0
    center child: RenderSliverPadding#931d8 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
        parentData: paintOffset=Offset(0.0, 0.0)
        constraints: MISSING
        geometry: null
        padding: EdgeInsets.zero
        textDirection: ltr
        child: RenderSliverList#02f24 NEEDS-LAYOUT NEEDS-PAINT
            parentData: paintOffset=Offset(0.0, 0.0)
            constraints: MISSING
            geometry: null
            no children current live

It works without FormKeyboardActions

class StockInventoriesNewPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: SafeArea(
        child: Form(child: _buildListView()),
      ),
    );
  }

  Widget _buildListView() {
    return ListView(
      children: [
        Text("1"),
        Text("2"),
        Text("3"),
      ],
    );
  }
}

If I used the ListView directly as a child (which is not what I need in my project) I have another error maybe more explicit:


════════ Exception caught by widgets library ═══════════════════════════════════
'package:keyboard_actions/external/bottom_area_avoider.dart': Failed assertion: line 49 pos 16: 'child is ScrollView ? child.controller != null : true': is not true.
User-created ancestor of the error-causing widget was
    SafeArea 
lib/…/new/new.dart:26
════════════════════════════════════════════════════════════════════════════════

FocusNode dispose generate error

App Structure:

Home
| -> Register Page
| -> Login Page

Replicate the Error:
To replicate this error, you will need to create a home page and the home screen will link to register screen and setup the FormKeyboardActions just like the example, when clicking back button from the Register Screen to navigate back to Home Screen, the following error occur.

Code that cause the error:

void dispose() {
    _nodeText1.dispose();
    _nodeText2.dispose();
    _nodeText3.dispose();
    _nodeText4.dispose();
    super.dispose();
}

Error:

flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown while finalizing the widget tree:
flutter: A FocusNode was used after being disposed.
flutter: Once you have called dispose() on a FocusNode, it can no longer be used.

Note: From what i understand, we should be disposing the FocusNode at dispose(), and this cause the error to show up, please correct me if i am wrong.

Unsupported operation: Infinity or NaN toint

I got this error when I added the keyboard actions setup (based from Readme)

image

When I removed FormKeyboardActions everything worked no more error. Any ideas?

Scaffold(
  child: FormKeyboardActions(
     child: Form()
  )
)

Flutter doctor

$ flutter doctor -v
[✓] Flutter (Channel stable, v1.5.4-hotfix.2, on Mac OS X 10.14.5 18F132, locale en-PH)
    • Flutter version 1.5.4-hotfix.2 at /Users/jongbanaag/Documents/Files/flutter
    • Framework revision 7a4c33425d (5 weeks ago), 2019-04-29 11:05:24 -0700
    • Engine revision 52c7a1e849
    • Dart version 2.3.0 (build 2.3.0-dev.0.5 a1668566e5)

 
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    • Android SDK at /Users/jongbanaag/Library/Android/sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-28, build-tools 28.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1343-b01)
    • All Android licenses accepted.

[✓] iOS toolchain - develop for iOS devices (Xcode 10.2.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 10.2.1, Build version 10E1001
    • ios-deploy 1.9.4
    • CocoaPods version 1.6.1

[✓] Android Studio (version 3.4)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 35.3.1
    • Dart plugin version 183.6270
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1343-b01)

[✓] VS Code (version 1.34.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.0.2

[✓] Connected device (1 available)
    • iPhone Xs • 5E0528FE-478A-423E-AA2B-478469E20552 • ios • com.apple.CoreSimulator.SimRuntime.iOS-12-2 (simulator)

• No issues found!

How do I use this inside a child widget?

tried to use this inside a child widget where my input is at, the button is not showing up in the right position I guess it is because my child widget doesn't have a full-screen height.

Can you add a way to use this inside a child widget instead of the scaffold body?

Text in TextField scrolls up after opening the keyboard

Hello, I faced with the following problem after adding your keyboard: when I open the keyboard text in TextField scrolls up and isn't shown until I close the keyboard. Here are the screenshots:

Before opening of the keyboard:

Screen Shot 2019-11-25 at 14 17 42

After opening:

Screen Shot 2019-11-25 at 14 18 21

Text in textfield didn't disappear but it only scrolled up that it's not visible now, unfortunately I can't show it. So, here's the way how I implemented keyboard to my widget:

class InputGoalWidget extends StatefulWidget {
  final bool isValid = true;

  final String title;
  final String hint;
  final TextInputType tit;
  final String emoji;
  final TextEditingController controller;

  InputGoalWidget({this.title, this.hint, this.emoji, this.controller, this.tit});

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

class _InputGoalWidgetState extends State<InputGoalWidget> with SingleTickerProviderStateMixin {
  AnimationController animationController;
  Animation<double> animation;
  FocusNode _node = FocusNode();

  KeyboardActionsConfig _buildConfig(BuildContext context) {
    return KeyboardActionsConfig(
      keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
      keyboardBarColor: Colors.white,
      nextFocus: false,
      actions: [
        KeyboardAction(
          focusNode: _node,
          displayCloseWidget: true,
        ),
      ],
    );
  }

  @override
  void initState() {
    super.initState();
    animationController = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 600),
    )..addListener(() => setState(() {}));

    animation = Tween<double>(
      begin: 50.0,
      end: 120.0,
    ).animate(CurvedAnimation(
      parent: animationController,
      curve: Interval(0.8, 1.0, curve: Curves.fastOutSlowIn),
    ));

    animationController.forward();
  }

  @override
  Widget build(BuildContext context) {
    return buildContainer(context);
  }

  Container buildContainer(BuildContext context) {
    return Container(
      margin: EdgeInsets.only(top: 3),
      color: Color(AppColors.white),
      child: Padding(
        padding: const EdgeInsets.only(top: 15, bottom: 10),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Padding(
              padding: const EdgeInsets.only(left: 20.0, right: 16),
              child: Text(
                widget.emoji,
                textAlign: TextAlign.justify,
                style: TextStyle(fontSize: 32),
              ),
            ),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Text(widget.title, style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
                Container(
                  height: MediaQuery.of(context).size.height * 0.06,
                  width: MediaQuery.of(context).size.width * 0.7,
                  child: Center(
                    child: Padding(
                      padding: const EdgeInsets.only(bottom: 0),
                        child: FormKeyboardActions(
                          config: _buildConfig(context),
                          child: TextField(
                            textCapitalization: TextCapitalization.sentences,
                            focusNode: _node,
                            style: TextStyle(
                              fontSize: 18,
                              color: Color(AppColors.brandViolet),
                              fontWeight: FontWeight.w500,
                            ),
                            keyboardType: widget.tit,
                            inputFormatters: widget.tit == TextInputType.number ? [WhitelistingTextInputFormatter.digitsOnly] : null,
                            decoration: InputDecoration(
                              hintText: widget.hint,
                              border: InputBorder.none,
                            ),
                            controller: widget.controller,
                          ),
                        ),
                    ),
                  ),
                )
              ],
            )
          ],
        ),
      ),
    );
  }
}

And here's the way how this widgets are used in the screen:

 Flexible(
            flex: 1,
            child: ListView(
              children: <Widget>[
                _nameWidget,
                _calorieWidget,
                _stepsWidget,
                _waterWidget,
              ],
            ),
          ),

So. _nameWidget, _calorieWidget, _stepsWidget and _waterWidget are InputGoalWidgets. Why such issue can happen? Thanks in advance for any help!

BoxConstraints exception when wrapping inside a column and having AutoScroll turned on.

I have a few issues using the keyboard actions. I have created a simple startup project that extends from your main sample - my app is much more complex and has a deep nesting of scrollable widgets but I have tried to abstract that functionality away by creating a small app that shows the issue. Hopefully I am missing something very basic.

Note
If autoscroll is turned off then for the simple use case below, keyboard actions functions - however - within our own live app which has many scrollable widgets nested deeply - we need to enable autoscroll on a per case basis.

Additionally - you will see that we have code such as :


showBody() {
    List<Widget> list = [];
    // Run with keyboard actions causes issues (Uncomment)

    list.add(
      FormKeyboardActions(
        child: TextField2(),
      ),
    );

Which adds one or more widgets encased within a FormKeyboardActions - in our app, the user can create as many TextField widgets as they desire - each textfield are multilined and can contain any number of lines. Scrolling between these textFields is important.

Here is the app running without using Keyboard Actions
sc1

From the gif above, we can see that the on screen keyboard allows newline entries and that we have a multiline textfield. Everything works perfectly except there is no close button (Hence the desire to use keyboard Action)

We are wrapping a TextField in a custom widget :

TextField2 (Which uses KeyboardAction)
TextField3 (which doesnt use KeyboardAction)

Build method showing how these widgets are used

Widget build(BuildContext context) {
    return Scaffold(
        resizeToAvoidBottomInset: false,
        appBar: AppBar(title: Text('keyboardActions')),
        body: showBody());
  }

  showBody() {
    List<Widget> list = [];
    // Run with keyboard actions causes issues (Uncomment)

    // list.add(
    //   FormKeyboardActions(
    //     child: TextField2(
    //     ),
    //   ),
    // );

// Run without keyboard actions shows the text field

    list.add(
      TextField3(),
    );

    return Column(
      children: list,
    );
  }

Running the app with keyboard Actions causes exception

image

Source

Two small simple files :

Main.dart

import 'package:example/textfield2.dart';
import 'package:flutter/material.dart';
import 'package:keyboard_actions/keyboard_actions.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(
          appBar: AppBar(
            title: Text('Test'),
          ),
          backgroundColor: Colors.amber,
          body: _myBody(context)),
    );
  }

  _myBody(BuildContext context) {
    return Builder(
      builder: (context) => Center(
        child: Padding(
          padding: const EdgeInsets.all(18.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              RaisedButton(
                child: Text("Full Screen form"),
                onPressed: () => _openWidget(
                  context,
                  ScaffoldTest(),
                ),
              ),
              SizedBox(
                height: 25,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class ScaffoldTest extends StatefulWidget {
  @override
  _ScaffoldTestState createState() => _ScaffoldTestState();
}

class _ScaffoldTestState extends State<ScaffoldTest> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        resizeToAvoidBottomInset: false,
        appBar: AppBar(title: Text('keyboardActions')),
        body: showBody());
  }

  showBody() {
    List<Widget> list = [];
    // Run with keyboard actions causes issues (Uncomment)

    // list.add(
    //   FormKeyboardActions(
    //     child: TextField2(),
    //   ),
    // );

// Run without keyboard actions shows the text field

    list.add(
      TextField3(),
    );

    return Column(
      children: list,
    );
  }
}

TextField2

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

class TextField2 extends StatefulWidget {
  bool callInit;
  TextField2();
  @override
  _TextField2State createState() => _TextField2State();
}

class _TextField2State extends State<TextField2> {
  final FocusNode _nodeText1 = FocusNode();

  KeyboardActionsConfig _buildConfig(BuildContext context) {
    return KeyboardActionsConfig(
      keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
      keyboardBarColor: Colors.grey[200],
      nextFocus: false,
      actions: [
        KeyboardAction(
          focusNode: _nodeText1,
          closeWidget: Padding(
            padding: EdgeInsets.all(8.0),
            child: Icon(Icons.close),
          ),
        ),
      ],
    );
  }

  @override
  void initState() {
    FormKeyboardActions.setKeyboardActions(context, _buildConfig(context));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Padding(
          padding: const EdgeInsets.all(15.0),
          child: Container(
              color: Colors.lightGreen.withOpacity(.4),
              child: Column(children: <Widget>[
                TextField(
                  maxLines: null,
                  keyboardType: TextInputType.multiline,
                  textInputAction: TextInputAction.newline,
                  focusNode: _nodeText1,
                  decoration: InputDecoration(
                    hintText: "Text with custom close",
                  ),
                ),
              ]))),
    );
  }
}

class TextField3 extends StatefulWidget {
 
  TextField3();
  @override
  _TextField3State createState() => _TextField3State();
}

class _TextField3State extends State<TextField3> {
  final FocusNode _nodeText2 = FocusNode();

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Padding(
          padding: const EdgeInsets.all(15.0),
          child: Container(
              color: Colors.lightGreen.withOpacity(.4),
              child: Column(children: <Widget>[
                TextField(
                  maxLines: null,
                  keyboardType: TextInputType.multiline,
                  textInputAction: TextInputAction.newline,
                  focusNode: _nodeText2,
                  decoration: InputDecoration(
                    hintText: "Text with custom close",
                  ),
                ),
              ]))),
    );
  }
}

Context does not contain a FormKeyboardAction ancestor

I got this error.

image

My widget tree is

ModalProgressHUD

  • SingleChildScrollView
    • Form
      • TextField
@override
  void initState() {
    FormKeyboardActions.setKeyboardActions(context, _buildConfig(context));
    super.initState();

    referenceController.addListener(() => onChange(referenceController));
  }
$ flutter doctor
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, v1.5.4-hotfix.2, on Mac OS X 10.14.4 18E226)
 
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
[✓] iOS toolchain - develop for iOS devices (Xcode 10.2.1)
[✓] Android Studio (version 3.3)
[✓] VS Code (version 1.33.1)
[✓] Connected device (1 available)

• No issues found!

Context does not contain a FormKeyboardActions ancestor: see Scaffold.of for reference.

use like this:

class KeyboardTest extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _KeyboardTestState();
  }
}

class _KeyboardTestState extends State<KeyboardTest> {
  final FocusNode _nodeText1 = FocusNode();

  KeyboardActionsConfig _buildConfig(BuildContext context) {
    return KeyboardActionsConfig(
      keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
      keyboardBarColor: Colors.grey[200],
      nextFocus: true,
      actions: [
        KeyboardAction(
          focusNode: _nodeText1,
        ),
      ],
    );
  }

  @override
  void initState() {
    // Configure keyboard actions
    FormKeyboardActions.setKeyboardActions(context, _buildConfig(context));
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
        title: Text("Keyboard Actions Sample"),
      ),
      body: IconTheme(
        data: IconTheme.of(context).copyWith(color: Colors.black),
        child: FormKeyboardActions(
          child: Center(
            child: Padding(
              padding: EdgeInsets.all(15),
              child: Column(
                children: <Widget>[
                  TextField(
                    keyboardType: TextInputType.number,
                    focusNode: _nodeText1,
                    decoration: InputDecoration(
                      hintText: "Input Number",
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

open this page:

    Navigator.of(context).push(
      MaterialPageRoute(builder: (_) => KeyboardTest()),
    );

error:

The following assertion was thrown building Builder:
Context does not contain a FormKeyboardActions ancestor: see Scaffold.of for reference.

why use like this?
because my project has many page like this,now i want add your lib to my code.
please help

Firing Keyboard with custom form by pressing a fab

Hello, As the title says, I'm trying to open up the keyboard with custom actions that will ask the user to enter some info and then will be saved in the database,

The problem is that I want the keyboard to be shown when the user presses a Fab - or a button ,

In the example I found that keyboard will be shown if a user presses a TextField.

is there any way to open up the keyboard with custom actions using a button? any hint will be great so I can achieve it.

this is an example to the thing I'm trying to explain

WhatsApp Image 2019-12-20 at 10 11 48 PM

Thank you for this amazing Lib.

improvement: support custom footer widget

Hey, so my most recent project has been to add a custom keyboard :) Thought I would open an issue to track if we wanted to support it here. I've been working on a change to allow a KeyboardAction to specify a custom overlay widget.

The widget would be shown below below the keyboard action bar. My use case is to implement a custom keyboard, for inputs that don't use the system keyboard, e.g. a color input:

custom_keyboard

It could also be used along with the system keyboard for things like providing some instructions, an error message, or input suggestions.

The getter 'position' was called on null.

Hi,
I'm getting the below exception every time the keyboard changes from number only to text. I'm using the text fields in a list view which is a child of KeyboardActions and KeyboardActionsConfig.
Please help.
════════ Exception caught by scheduler library
The following NoSuchMethodError was thrown during a scheduler callback:
The getter 'position' was called on null.
Receiver: null
Tried calling: position

When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1 scrollToObject (package:keyboard_actions/external/bottom_area_avoider.dart:189:33)
#2 BottomAreaAvoiderState._paddingAnimationStatusChanged. (package:keyboard_actions/external/bottom_area_avoider.dart:153:7)
#3 SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1102:15)
#4 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1049:9)
...

Keyboard is hidden when I tap Android Home or Overview button but not keyboard actions header

Hi @diegoveloper,

Thanks for writing this great package!

The only problem I've experienced is when the keyboard is displayed and:

  1. I switch out of an app, using the Android Home button or the Overview button (that's the third button at the bottom from the left)
  2. I can see that the keyboard is hidden
  3. When I return to the app the keyboard actions are sitting at the bottom of the screen without the keyboard being displayed.

The problem occurs in the example app and the apps that I've used the package in.

It seems like the package is not able to detect the keyboard being hidden in this situation.

I'm hoping it's simple for you to reproduce.

I've tried it on a few different versions of Android. My set up is fairly standard:

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel beta, v1.0.0, on Mac OS X 10.14 18A391, locale en-AU)
[✓] Android toolchain - develop for Android devices (Android SDK 28.0.3)
[✓] iOS toolchain - develop for iOS devices (Xcode 10.1)
[✓] Android Studio (version 3.2)
[✓] Connected device (2 available)

Thanks

A FocusNode was used after being disposed.

I'm hitting an error with version 2.1.1 (upgrading from 1.0.3):

logs
flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
flutter: The following assertion was thrown while finalizing the widget tree:
flutter: A FocusNode was used after being disposed.
flutter: Once you have called dispose() on a FocusNode, it can no longer be used.
flutter:
flutter: When the exception was thrown, this was the stack:
flutter: #0      ChangeNotifier._debugAssertNotDisposed.<anonymous closure> (package:flutter/src/foundation/change_notifier.dart:105:9)
flutter: #1      ChangeNotifier._debugAssertNotDisposed (package:flutter/src/foundation/change_notifier.dart:111:6)
flutter: #2      ChangeNotifier.removeListener (package:flutter/src/foundation/change_notifier.dart:166:12)
flutter: #3      FormKeyboardActionState._dismissListeningFocus.<anonymous closure> (package:keyboard_actions/keyboard_actions.dart:261:38)
flutter: #4      Iterable.forEach (dart:core/iterable.dart:277:30)
flutter: #5      FormKeyboardActionState._dismissListeningFocus (package:keyboard_actions/keyboard_actions.dart:260:17)
flutter: #6      FormKeyboardActionState.clearConfig (package:keyboard_actions/keyboard_actions.dart:166:5)
flutter: #7      FormKeyboardActionState.dispose (package:keyboard_actions/keyboard_actions.dart:342:5)
flutter: #8      StatefulElement.unmount (package:flutter/src/widgets/framework.dart:3916:12)
flutter: #9      _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1697:13)
flutter: #10     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #11     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #12     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #13     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #14     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #15     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #16     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #17     MultiChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4965:16)
flutter: #18     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #19     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #20     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #21     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #22     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #23     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #24     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #25     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #26     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #27     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #28     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #29     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4864:14)
flutter: #30     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #31     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #32     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #33     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #34     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #35     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4864:14)
flutter: #36     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #37     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #38     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #39     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #40     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #41     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #42     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #43     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #44     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #45     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #46     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #47     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #48     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #49     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #50     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #51     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #52     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #53     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #54     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #55     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #56     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4864:14)
flutter: #57     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #58     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #59     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #60     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #61     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #62     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4864:14)
flutter: #63     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #64     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #65     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4864:14)
flutter: #66     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #67     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #68     MultiChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4965:16)
flutter: #69     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #70     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #71     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #72     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #73     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #74     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4864:14)
flutter: #75     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #76     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #77     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #78     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #79     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #80     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4864:14)
flutter: #81     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #82     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #83     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #84     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #85     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #86     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4864:14)
flutter: #87     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #88     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #89     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #90     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #91     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #92     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #93     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #94     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #95     ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #96     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #97     _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #98     SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4864:14)
flutter: #99     _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #100    _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #101    ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #102    _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #103    _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #104    SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4864:14)
flutter: #105    _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #106    _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #107    ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #108    _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #109    _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #110    ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #111    _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #112    _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #113    SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:4864:14)
flutter: #114    _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #115    _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #116    ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #117    _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #118    _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #119    ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #120    _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #121    _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1695:7)
flutter: #122    ComponentElement.visitChildren (package:flutter/src/widgets/framework.dart:3770:14)
flutter: #123    _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1693:13)
flutter: #124    ListIterable.forEach (dart:_internal/iterable.dart:39:13)
flutter: #125    _InactiveElements._unmountAll (package:flutter/src/widgets/framework.dart:1706:25)
flutter: #126    BuildOwner.finalizeTree.<anonymous closure> (package:flutter/src/widgets/framework.dart:2364:27)
flutter: #127    BuildOwner.lockState (package:flutter/src/widgets/framework.dart:2196:15)
flutter: #128    BuildOwner.finalizeTree (package:flutter/src/widgets/framework.dart:2363:7)
flutter: #129    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding&WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:702:18)
flutter: #130    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding&PaintingBinding&SemanticsBinding&RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:286:5)
flutter: #131    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1012:15)
flutter: #132    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:952:9)
flutter: #133    _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:864:5)
flutter: #137    _invoke (dart:ui/hooks.dart:219:10)
flutter: #138    _drawFrame (dart:ui/hooks.dart:178:3)
flutter: (elided 3 frames from package dart:async)

My code follows the example pretty closely. I'm running into it when I navigate to a page with keyboard actions, pop navigation, and then again navigate to the keyboard actions route again.

This basic flutter app reproduces the issue (Github):

  • (just hit the (+) button, then the X in the top left to go back. The error is in the logs, but you see the page blow up when you try to hit the (+) button again).

The problem is that line 259 is trying to remove listeners from focus nodes that were previously disposed of.

The first page looks like this:

import 'package:flutter/material.dart';

import 'form_page.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Navigator.of(context).push(
            MaterialPageRoute(
              builder: (BuildContext context) => FormPage(isEditing: false),
            ),
          );
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

and the form page is this:

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

class FormPage extends StatelessWidget {
  final bool isEditing;

  FormPage({
    Key key,
    @required this.isEditing,
  })  : super(key: key ?? Key(isEditing ? 'editing' : 'creating'));

  static final _scaffoldKey = GlobalKey<ScaffoldState>();
  static final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: _scaffoldKey,
      appBar: AppBar(
        leading: IconButton(
          icon: Icon(Icons.clear),
          onPressed: () => Navigator.of(context).pop(),
        ),
        title: Text(isEditing ? "Edit Herd Record" : "New Herd Record"),
      ),
      body: FormKeyboardActions(
        child: _FormContent(
          isEditing: isEditing,
          formKey: _formKey,
          scaffoldKey: _scaffoldKey,
        ),
      ),
    );
  }
}

class _FormContent extends StatefulWidget {
  final bool isEditing;
  final GlobalKey<ScaffoldState> scaffoldKey;
  final GlobalKey<FormState> formKey;

  _FormContent({
    Key key,
    @required this.isEditing,
    @required this.formKey,
    @required this.scaffoldKey,
  });

  @override _FormContentState createState() => _FormContentState(
    nameTextEditingController: TextEditingController(text: null),
  );
}

class _FormContentState extends State<_FormContent> {
  final FocusNode focusNodeName = FocusNode();

  final TextEditingController nameTextEditingController;

  _FormContentState({
    @required this.nameTextEditingController,
  });

  GlobalKey<FormState> get _formKey => widget.formKey;
  GlobalKey<ScaffoldState> get _scaffoldKey => widget.scaffoldKey;

  String _name;

  @override
  void initState() {
    _name = '';

    // Configure keyboard actions
    FormKeyboardActions.setKeyboardActions(context, _buildConfig(context));
    super.initState();
  }

  KeyboardActionsConfig _buildConfig(BuildContext context) {
    return KeyboardActionsConfig(
      keyboardActionsPlatform: KeyboardActionsPlatform.ALL,
      actions: [
        KeyboardAction(focusNode: focusNodeName),
      ]
    );
  }

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.only(left: 16, right: 16),
      child: Form(
        key: _formKey,
        child: SingleChildScrollView(
          child: Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              SafeArea(bottom: false,
                child: Align(alignment: Alignment.center,
                  child: Container(constraints: BoxConstraints(maxWidth: 600),
                    child: Column(
                      children: <Widget>[
                        const SizedBox(height: 16),
                        _nameField(), const SizedBox(height: 24),
                      ],
                    )
                  )
                ),
              )
            ],
          )
        ),
      ),
    );
  }

  Widget _nameField() {
    return TextFormField(
      focusNode: focusNodeName,
      initialValue: _name,
      decoration: const InputDecoration(labelText: 'Name'),
    );
  }

  @override
  void dispose() {
    focusNodeName.dispose();
    nameTextEditingController.dispose();

    super.dispose();
  }
}

should I not be calling dispose on the focusNode?

Feature request: allow disabling and hiding Up/Down buttons in a specific TextField

Sample form structure:

1. TextField
2. TextField
3. TextField
4. DropdownButtonFormField
5. DropdownButtonFormField
6. DropdownButtonFormField
7. TextField
8. TextField
9. TextField

In the TextField 3, I would like to disable Down-button (or preferably it should work like a Close-button) to prevent jumping directly to TextField 7 (user should go through the DropDownButtonFormFields first).

All or any of the following new features would help:

  • Allow disabling Up/Down buttons in KeyboardAction
  • Allow hiding Up/Down buttons in KeyboardAction
  • Allow customizing Up/Down buttons action in KeyboardAction so that it closes the keyboard instead of navigating Up/Down

Autoscrolling does not work with SingleChildScrollView

If I add a Column with TextFields into SingleChildScrollView, TextFields are not scrolled automatically into view. Using ListView fixes the issue, but it cannot be used due to flutter/flutter#11500.

Another workaround is to replace SingleChildScrollView directly with its child (Column in my case), but this would require extra logic especially if you are planning to use this plugin only with iOS and not with Android.

So as an improvement, shouldn't the related logic starting from here

if (widget.child is ScrollView) {
check if the child is already a SingleChildScrollView? Now it will wrap an existing SingleChildScrollView with another SingleChildScrollView. I guess this causes the issue.

How to activate a new KeyboardActions for a dynamically created widget that requires a FormKeyboardAction

Assuming you have code that displays the same Widget (say 'x' times and can be triggered by a user tapping a + icon) - running this code below will add the three text fields but only the last one will have a close button and not three which is the intended action :

This code will add 3 textfields and clicking the + will add further text fields :

Widget build(BuildContext context) {
    return Scaffold(
        resizeToAvoidBottomInset: false,
        appBar: AppBar(actions: <Widget>[
          IconButton(
            icon: Icon(Icons.add),
            onPressed: () => _add(),
          )
        ], title: Text('keyboardActions')),
        body: FormKeyboardActions(child: showBody()));
  }

  showBody() {
    // Initially 3 textfields that call _buildConfig(...) 
    list.add(
      TextField2(),
    );
    list.add(
      TextField2(),
    );
    list.add(
      TextField2(),
    );
    return ValueListenableBuilder(
      valueListenable: _addNotifier,
      builder: (BuildContext context, int value, Widget child) {
        return Column(
          children: list,
        );
      },
    );
  }

  _add() {
    list.add(
      TextField2(),
    );
    _addNotifier.value += 1;
  }

Only the last TextField has the close button

image

How to use with Column as a parent and multiline TextField

Hey @diegoveloper

I have tweaked your sample.dart to illustrate the issue.

I'm getting BoxConstraints forces an infinite height.
If you remove the Column, everything works as expected.
If I wrap FormKeyboardActions in Container with the fixed height it also works.

My question is - is it possible to make it work without setting the fixed height?
Thanks!

import 'package:flutter/material.dart';
import 'package:keyboard_actions/keyboard_actions.dart';
//https://github.com/hackiftekhar/IQKeyboardManager

class Sample extends StatelessWidget {
  final _focusNodeQuantity = FocusNode();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("KeyboardActions"),
      ),
      body: Column(
        children: <Widget>[
          FormKeyboardActions(
            config: KeyboardActionsConfig(actions: [
              KeyboardAction(
                focusNode: _focusNodeQuantity,
              ),
            ]),
            child: TextField(
              focusNode: _focusNodeQuantity,
              keyboardType: TextInputType.multiline,
              maxLines: null,
            ),
          ),
        ],
      ),
    );
  }
}

Platform selection

Platform selection does not appear to be working, and I cannot see where you are checking which platform was selected.

I was able to fix it by adding _isAvailable to _focusChanged as below

  _focusChanged(bool showBar) {
    if (showBar && !_isShowing && _isAvailable) {
      _insertOverlay();
    } else if (!showBar && _isShowing && _isAvailable) {
      _removeOverlay();
    } else if (showBar && _isShowing && _isAvailable) {
      _overlayEntry.markNeedsBuild();
    }

    WidgetsBinding.instance.addPostFrameCallback((_) {
      _updateOffset();
    });
  }

support multiple actions for a given text fields

The iOS api allows to add multiple arbitrary keyboard actions (e.g. Save or Go and Cancel), but flutter_keyboard_actions is restricting it to only one action per field, so I don't see a way to have both cancel and save actions, especially for a numeric-only keyboard on the phone.

Please extend api to allow multiple actions, e.g. introduce another constructor in KeyboardAction that would accept list of focusNodes and have child instead of closeWidget.

driver.scrollUntilVisible dosnt scroll Scaffold with FormKeyboardActions

Im using flutter_driver to test my application.
My test should scroll down and find an element and tap it. But the scroll dosnt work with FormKeyboardActions widget in the tree.

return Scaffold(
      key: Key('pageKey'),
      appBar: CustomAppBar(
        title: "Profile",
        showBorder: true,
        action: _buildSubmitButton(),
      ),
      body: FormKeyboardActions(
        child: _buildContent();
      ),
    );
Future<void> scrollDown(
        SerializableFinder area, SerializableFinder element) async {
      await driver.scrollUntilVisible(
        area,
        element,
        dyScroll: -300.0,
      );
    }

BoxConstraints forces an infinite height

I try to implement this: https://pub.dev/packages/keyboard_actions

But I get this error:

I/flutter (30378): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════ I/flutter (30378): The following assertion was thrown during performLayout(): I/flutter (30378): BoxConstraints forces an infinite height. I/flutter (30378): These invalid constraints were provided to RenderPadding's layout() function by the following I/flutter (30378): function, which probably computed the invalid constraints in question: I/flutter (30378): RenderConstrainedBox.performLayout (package:flutter/src/rendering/proxy_box.dart:259:13) I/flutter (30378): The offending constraints were: I/flutter (30378): BoxConstraints(w=360.0, h=Infinity) I/flutter (30378): I/flutter (30378): When the exception was thrown, this was the stack: I/flutter (30378): #0 BoxConstraints.debugAssertIsValid.<anonymous closure>.throwError (package:flutter/src/rendering/box.dart:507:9) I/flutter (30378): #1 BoxConstraints.debugAssertIsValid.<anonymous closure> (package:flutter/src/rendering/box.dart:550:21) I/flutter (30378): #2 BoxConstraints.debugAssertIsValid (package:flutter/src/rendering/box.dart:554:6)

It occurs when I give FormKeyboardActions() a child, the child is:

Padding -> Row[Expanded->Theme->TextFormField]

The FormKeyboardActions() is a child of Dismissible, Dismissible is a child of SizeTransition, which is a child of Column

Exception when going back to previous page

Hi,
When the input is in editing and keyboard is showing, if I click Back button and going back to the previous page, I got this exception. Could you help to check it?

flutter: When the exception was thrown, this was the stack:
flutter: #0      State.setState.<anonymous closure> (package:flutter/src/widgets/framework.dart:1090:9)
flutter: #1      State.setState (package:flutter/src/widgets/framework.dart:1116:6)
flutter: #2      FormKeyboardActionState._updateOffset (package:keyboard_actions/keyboard_actions.dart:304:7)
flutter: #3      FormKeyboardActionState._focusChanged.<anonymous closure> (package:keyboard_actions/keyboard_actions.dart:242:7)
flutter: #4      _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1012:15)
flutter: #5      _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:960:9)
flutter: #6      _WidgetsFlutterBinding&BindingBase&GestureBinding&ServicesBinding&SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:864:5)
flutter: #10     _invoke (dart:ui/hooks.dart:219:10)
flutter: #11     _drawFrame (dart:ui/hooks.dart:178:3)
flutter: (elided 3 frames from package dart:async)

flutter: Another exception was thrown

flutter: Another exception was thrown: 'package:flutter/src/rendering/box.dart': Failed assertion: line 2193 pos 12: 'child.parent == this': is not true.

Flutter 1.7.8+hotfix.4 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 20e59316b8 (9 days ago) • 2019-07-18 20:04:33 -0700
Engine • revision fee001c93f
Tools • Dart 2.4.0

what is footerBuilder error?

════════ Exception caught by services library ══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown during a platform message callback:
The getter 'footerBuilder' was called on null.
Receiver: null
Tried calling: footerBuilder

When the exception was thrown, this was the stack: 
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:51:5)
#1      KeyboardActionstate._focusChanged (package:keyboard_actions/keyboard_actions.dart:219:26)
#2      KeyboardActionstate.didChangeAppLifecycleState (package:keyboard_actions/keyboard_actions.dart:373:9)
#3      WidgetsBinding.handleAppLifecycleStateChanged (package:flutter/src/widgets/binding.dart:536:16)
#4      SchedulerBinding._handleLifecycleMessage (package:flutter/src/scheduler/binding.dart:285:5)
...
════════════════════════════════════════════════════════════════════════════════════════════════════

Looking up a deactivated widget's ancestor is unsafe.

error when Navigator.pop(context)


  
════════ Exception Caught By scheduler library ═════════════════════════════════
The following assertion was thrown during a scheduler callback:
Looking up a deactivated widget's ancestor is unsafe.

At this point the state of the widget's element tree is no longer stable.

To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling inheritFromWidgetOfExactType() in the widget's didChangeDependencies() method.


Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.