Last updated:
0 purchases
flutter smkt
Simple mobile keyboard toolbar for Flutter applications. #
Simple mobile keyboard toolbar for flutter.
Other awesome & popular flutter keyboard packages:
keyboard_actions
math_keyboard
Honorable mention: Medium: Stop fighting the native iOS keypad and build a custom number pad for Flutter. -by Casey Henson
Unlike math_keyboard or Casey Henson's implementation, flutter_smkt like keyboard_actions, simply puts a toolbar on top of the system keyboard.
If you would like to replace the system keyboard with your own custom keyboard please reference the other packages.
The name flutter simple mobile keyboard toolbar was chosen as a commitment device, not just for the developers, but for the broader open source community.
So if you can make it simpler or better or greater, please let us know! Pull requests are welcome.
Getting started #
flutter_smkt: ^0.0.2
copied to clipboard
Usage #
Set up #
Wrap the main app widget (or the widget that needs the toolbar) with the KeyboardToolbar widget. However KeyboardToolbar relies on MediaQuery so need to make sure a global platform widget such as MaterialApp, CupertinoApp, or PlatformApp is an ancestor of KeyboardToolbar.
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:flutter_smkt/flutter_smkt.dart';
void main() {
runApp(
PlatformApp(
home: KeyboardToolbar(
child: MainApp()
),
)
);
}
copied to clipboard
Reference the Troubleshooting section for additional debugging related information.
Example using DoneButton #
Then for example can use the toolbar with a DoneButton (from flutter_smkt) for a multiline TextField widget where the system keyboard would only have a 'Newline Button' but not a 'Done' button, such as:
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
import 'package:flutter_smkt/flutter_smkt.dart';
import 'package:flutter/widgets.dart';
typedef EditOnChangedCallback = void Function(String text);
class MyApp extends StafulWidget {
@override
State<StatefulWidget> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool editMode = false;
String text = 'Hi';
String? editedText;
void editOnChangedCallback(String text) => setState(() => editedText = text);
void onPressedCallback() {
setState(() {
text = editText;
editMode = false;
});
}
@override
Widget build(BuildContext context) {
if (editMode)
return _TextInput(
initialText: text,
editOnChangedCallback: editOnChangedCallback,
onPress: onPressedCallback
);
return Row(
children: [
PlatformText(text),
PlatformButton(
child: PlatformText('EDIT'),
onPressed: () => setState(() => editMode = true)
)
]
);
}
}
class _TextInput extends StatefulWidget {
final String initialText;
final EditOnChangedCallback editOnChangedCallback;
final OnToolbarButtonPressed onPress; // OnToolbarButtonPressed typedef provided by fsmkt
_TextInput({
required this.initialText,
required this.EditOnChangedCallback,
required this.onPress
});
@override
State<StatefulWidget> createState() => _TextInputState();
}
class _TextInputState extends State<_TextInput> {
late TextEditingController _controller;
final String initialText;
final EditOnChangedCallback editOnChangedCallback;
final OnToolbarButtonPressed onPress; // OnToolbarButtonPressed typedef provided by fsmkt
_TextInputState({
required this.initialText,
required this.EditOnChangedCallback,
required this.onPress
});
// Need initialize a controller for interaction & the toolbar
@override
void initState() {
super.initState();
// initialize the TextEditingController
_controller = TextEditingController(text: initialText);
_controller.addListener(() => editOnChangedCallback(_controller.text));
// Add a frame callback to show toolbar
WidgetsBinding.instance!
.addPostFrameCallback(
(_) {
KeyboardToolbar.of(context).makeVisible(
toolbarButtons: [
DoneButton(onPressed)
]
);
}
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return PlatformTextFormField(
autofocus: true,
controller: _controller,
onFieldSubmitted: ,
maxLines: null,
validator: validator,
inputFormatters: inputFormatters,
);
}
}
copied to clipboard
The keyboard toolbar gets built by this step, and is hidden right after onPressed is called in the DoneButton widget, with the call KeyboardToolbar.of(context).hide().
// Add a frame callback to show toolbar
WidgetsBinding.instance!
.addPostFrameCallback(
(_) {
KeyboardToolbar.of(context).makeVisible(
toolbarButtons: [
DoneButton(onPressed)
]
);
}
);
copied to clipboard
Custom toolbar buttons #
By extending the onPress callback you can use a custom DoneButton too.
// Add a frame callback to show toolbar
WidgetsBinding.instance!
.addPostFrameCallback(
(_) {
KeyboardToolbar.of(context).makeVisible(
toolbarButtons: [
PlatformTextButton(
child: PlatformText('Done'),
onPressed: () {
onPressed();
KeyboardToolbar.of(context).hide();
}
)
]
);
}
);
copied to clipboard
Custom toolbar view #
Similarly you can extend KeyboardToolbarView to customize the whole toolbar view:
void main() {
runApp(
PlatformApp(
home: KeyboardToolbar(
child: MainApp(),
toolbarViewBuilder: () => CustomToolbarView()
),
)
);
}
class CustomToolbarView extends KeyboardToolbarView {
final List<Widget> toolbarButtons;
const CustomToolbarView({required this.toolbarButtons}):
super(toolbarButtons: toolbarButtons);
@override
Widget build(BuildContext context) {
return Container(
color: Colors.white,
child: Row(
children: [PlatformText('ʕ·͡ᴥ·ʔ'), ...toolbarButtons],
mainAxisAlignment: MainAxisAlignment.end,
),
);
}
}
copied to clipboard
Where CustomToolbarView extends KeyboardToolbarView
Troubleshooting #
inactive InputConnection
Currently the following lines are printed when the toolbar is activated. Don't panic it's not an error on your end, the messages seem to be generated for brief time period when the toolbar is being built.
W/IInputConnectionWrapper(32148): beginBatchEdit on inactive InputConnection
W/IInputConnectionWrapper(32148): getTextBeforeCursor on inactive InputConnection
W/IInputConnectionWrapper(32148): getTextAfterCursor on inactive InputConnection
W/IInputConnectionWrapper(32148): getSelectedText on inactive InputConnection
W/IInputConnectionWrapper(32148): endBatchEdit on inactive InputConnection
W/IInputConnectionWrapper(32148): beginBatchEdit on inactive InputConnection
W/IInputConnectionWrapper(32148): endBatchEdit on inactive InputConnection
W/IInputConnectionWrapper(32148): beginBatchEdit on inactive InputConnection
W/IInputConnectionWrapper(32148): endBatchEdit on inactive InputConnection
copied to clipboard
Please let us know if you can fix this.
TextField's view is hidden below the toolbar's view.
TextField actually hidden below the virtual keyboard not the toolbar.
If the TextField is hidden below the virtual keyboard without the toolbar.
Then try:
Wrapping the whole view in a Scaffold with resizeToAvoidBottomInset property.
Adding a ListView allowing the user to scroll up.
Reference:
Flutter Docs - resizeToAvoidBottomInset
StackOverflow - Answer to "Keyboard slides up and covers the TextField in Flutter"
`TextField actually hidden by the toolbar's view.
The KeyboardToolbar class exposes the toolbarHeight as a stream as KeyboardToolbar.of(context).toolbarHeight.
The toolbarHeight stream can then be used with a StreamBuilder (& ListView or ListView.builder) to create additional padding, allowing the user to scroll and access the TextField.
For example can wrap the originalView with the TextField as:
Widget belowToolbarPadding = StreamBuilder(
stream: KeyboardToolbar.of(context).toolbarHeight,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) return Container();
return Padding(padding: EdgeInsets.only(bottom: snapshot.data));
}
);
Widget wrappedOriginalView = ListView(
children: const <Widget>[
originalView,
belowToolbarPadding
]
);
copied to clipboard
Note: It is very necessary to provide the Padding separately to avoid rebuilding the originalView on toolbarHeight change.
Let us know if there is a better solution.
Additional information #
The name 'flutter_simple_mobile_keyboard_toolbar' was chosen as a commitment device, not just for the developers, but for the broader open source community.
So if you can make it simpler or better or greater, please let us know! Pull requests are welcome.
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.