Flutter scrollbar on textfield - flutter

I have full screen TextField that grow in size when the user added more lines/text. I want to add scrollbar for the user to know the size the TextField and where is he.
This is the code the my screen and TextField:
import 'package:flutter/material.dart';
class TextEditor extends StatefulWidget {
TextEditor();
#override
_TextEditorState createState() => _TextEditorState();
}
class _TextEditorState extends State<TextEditor> {
TextEditingController _editTextController = TextEditingController();
#override
Widget build(BuildContext context) {
var scaffold = Scaffold(
appBar: AppBar(
leading: IconButton(
icon: Icon(Icons.arrow_back),
onPressed: () => _backButton(),
),
title: Text("File name"),
),
body: _buildTextEditor(),
resizeToAvoidBottomInset: true,
);
return scaffold;
}
Widget _buildTextEditor() {
return TextField(
autofocus: true,
keyboardType: TextInputType.multiline,
maxLines: null,
autocorrect: true,
onChanged: (s) => {},
decoration: InputDecoration(
border: InputBorder.none,
isDense: true,
),
);
}
_backButton() {}
}
This is what I have
This is what I want, the scrollbar on the right
I could not find a way to do this and I don't an expert on Flutter to know how to do this on my own

Fortunately, the TextField widget accepts a scroll controller. This can be used to generate a scrollbar. See the example below.
TextEditingController _editTextController = TextEditingController();
// Initialise a scroll controller.
ScrollController _scrollController = ScrollController();
Then, add it to the TextField widget and wrap this widget in a Scrollbar!
return Scrollbar(
controller: _scrollController,
isAlwaysShown: true,
child: TextField(
scrollController: _scrollController,
autofocus: true,
keyboardType: TextInputType.multiline,
maxLines: null,
autocorrect: true,
onChanged: (s) => {},
decoration: InputDecoration(
border: InputBorder.none,
isDense: true,
),
),
);

MediaQuery.removePadding(
context: context,
removeTop: true,
removeBottom: true,
child: Scrollbar(
thumbVisibility: true,
child: TextField( //If you want initial value use TextFormField
maxLines: 4,
minLines: 1,
decoration: const InputDecoration(contentPadding: EdgeInsets.zero),
),
),
);
After multiple tries, this helped in my case.
If you want to use with static read only text, user TextFormField with initialValue and readOnly true.

Related

adding form with globalKey inside PageView.Builder

I have form and TextFromFilde inside PageView.builder, everytime chinge page it show me this error Duplicate GlobalKey detected in widget tree.
and some time that TextFormFilde is hideing.
all problome is GlobalKey, if I delete it every thing is working perfict but text filde is unfocused in every page I had to tap agin on it to type data
import 'package:flutter/material.dart';
void main() => runApp(Myapp());
class Myapp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: PageViewTest(),
);
}
}
class PageViewTest extends StatefulWidget {
#override
State<PageViewTest> createState() => _PageViewTestState();
}
List<TextEditingController> tecList;
var _formKey;
List controller = [
TextEditingController(),
TextEditingController(),
TextEditingController(),
];
List<String> _signing_hint_text = [
'type your domain',
'type your email',
'type your password',
];
List<String> _signing_input_label = [
'Domain',
'Email',
'Password',
];
Size mDeviceSize(BuildContext context) {
return MediaQuery.of(context).size;
}
PageController _pageController = PageController(initialPage: 0);
class _PageViewTestState extends State<PageViewTest> {
#override
void initState() {
// TODO: implement initState
_formKey = GlobalKey<FormState>();
// _pageController = PageController();
tecList = List.generate(3, (index) {
return TextEditingController();
});
super.initState();
}
#override
void dispose() {
// TODO: implement dispose
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
margin: EdgeInsets.all(30),
child: PageView.builder(
controller: _pageController,
itemCount: 3,
itemBuilder: (context, index) {
return Column(
children: [
Form(
autovalidateMode: AutovalidateMode.onUserInteraction,
key: _formKey,
child: TextFormField(
autofocus: true,
textAlign: TextAlign.right,
textInputAction: TextInputAction.next,
style: TextStyle(color: Color(0xff030303)),
cursorColor: Color(0xff5e6593),
controller: tecList[index],
keyboardType: TextInputType.text,
decoration: InputDecoration(
hintText: _signing_hint_text[index],
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xff5e6593),
),
),
contentPadding:
EdgeInsets.symmetric(vertical: 5.0, horizontal: 10),
labelText: _signing_input_label[index],
labelStyle: TextStyle(color: Color(0xff5e6593)),
floatingLabelBehavior: FloatingLabelBehavior.auto,
border: OutlineInputBorder(),
alignLabelWithHint: true,
hintStyle: TextStyle(
color: Color(0xff5e6593),
fontSize: 15,
fontWeight: FontWeight.normal),
),
),
),
ElevatedButton(
onPressed: () {
_pageController.nextPage(
duration: Duration(milliseconds: 800),
curve: Curves.ease);
},
child: Text('click')),
],
);
}),
),
),
);
}
}
You can not use one formKey for all Form widgets in each page, you should define formKey for each page of your page view and use index to know which one is for which page. For example define three different formKey and use index like this:
List<GlobalKey<FormState>> formKeys = [GlobalKey<FormState>(),GlobalKey<FormState>(),GlobalKey<FormState>()];
and inside your PageView.builder, do this:
Form(
autovalidateMode: AutovalidateMode.onUserInteraction,
key: formKeys[index],
...
)
But For focus each TextFormField when page change, first define a list like this:
List<FocusNode> focusList = [FocusNode(), FocusNode(), FocusNode()];
then do this inside PageView.builder:
PageView.builder(
onPageChanged: (value) {
FocusScope.of(context).requestFocus(focusList[value]);
},
controller: _pageController,
itemCount: 3,
...
}
And also do not forget to pass those focusNode to TextFormField:
child: TextFormField(
focusNode: focusList[index],
autofocus: true,
textAlign: TextAlign.right,
...
)

TextField with long text, horizontal scroll reset to start - Flutter

Building a stateful widget with a textfield wrapped within a Container of specific width. Now, when I type a long text in the textfield, the view scrolls right as you type (works as expected). But when I tap outside of the textfield, I've unfocused the textField. When I unfocus, how do I programatically reset the view of the textfield back to the beginning of the text? i.e. Move the horizontal scroll back to the beginning?
Here's the textField widget
textContainer = Container(
width: MediaQuery.of(context).size.width * 0.68,
child: Focus(
child: TextField(
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.sentences,
onSubmitted: onSubmit,
controller: textController,
focusNode: widget.focusNode,
maxLines: 1,
autofocus: widget.isNew,
cursorColor: Colors.white,
style: const TextStyle(
fontSize: 16.0,
fontFamily: 'Inter',
fontWeight: FontWeight.normal,
color: Colors.white
),
decoration: const InputDecoration(
border: InputBorder.none,
),
onChanged: (text) {
Task task = taskBox.get(widget.task.id);
task.text = text;
taskBox.put(widget.task.id, task);
},
),
onFocusChange: (hasFocus) {
showDeleteBtn(hasFocus);
},
)
);
See the mid part of the gif, when I unfocus the textfield, I'd like to push the view/scroll back to the beginning of the widget. Tried rebuild the widget, but doesn't work.
You can use ScrollController inside TextField and while textfiled get unfocused jump to start.
class _TOSState extends State<TOS> {
late final FocusNode textInputFocusNode = FocusNode()
..addListener(() {
debugPrint(textInputFocusNode.hasFocus.toString());
if (!textInputFocusNode.hasFocus) {
controller.jumpTo(0);
}
});
ScrollController controller = ScrollController();
#override
void dispose() {
textInputFocusNode.removeListener(() {});
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus();
},
child: Column(
children: [
TextField(
scrollController: controller,
focusNode: textInputFocusNode,
maxLines: 1,
decoration: const InputDecoration(
hintText: "TYpe",
),
),
],
),
));
}
}

Error: Expected a value of type 'TextEditingController', but got one of type 'TextEditingValue'

I'm trying to wrap a ValueListenableBuilder around a Textfield which has a functionality of taking input text and returning the same text. The original purpose is to persist the input data through a database. But while implementing the basic code given below, I'm getting the error "Expected a value of type 'TextEditingController', but got one of type 'TextEditingValue'". Could you please enlighten me on the error?
import 'package:flutter/material.dart';
void main() => runApp(MyTextFieldApp());
class MyTextFieldApp extends StatelessWidget {
final _controller = TextEditingController();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.white,
body: Container(
padding: EdgeInsets.all(24.0),
child: Center(
child: ValueListenableBuilder(
valueListenable: _controller,
builder: (BuildContext context, _controller, _ ) {
return TextField(
autofocus: true,
maxLines: 6,
controller: _controller,
decoration: InputDecoration(
labelText: "Note",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
);
},
),
)
)
)
);
}
}
You are not pass a TextEditingController to the TextField
ValueListenableBuilder(
valueListenable: _controller,
builder: (BuildContext context, _controller, _ ) {
// this _controller is not equal to the valueListenable: _controller above, it means _controller.value
return TextField(
autofocus: true,
maxLines: 6,
controller: _controller,
decoration: InputDecoration(
labelText: "Note",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
);
},
),
ValueListenableBuilder(
valueListenable: _controller,
builder: (BuildContext context, _value, _ ) {
return TextField(
autofocus: true,
maxLines: 6,
controller: _controller,// assign the TextEditingController
decoration: InputDecoration(
labelText: "Note",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
);
},
),
You should do the following:
import 'package:flutter/material.dart';
void main() => runApp(MyTextFieldApp());
class MyTextFieldApp extends StatelessWidget {
final _controller = TextEditingController();
final ValueNotifier valueNotifier = ValueNotifier("initial");
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar( title : Text("Title")),
backgroundColor: Colors.white,
body: Container(
padding: EdgeInsets.all(24.0),
child: Center(
child: ValueListenableBuilder(
valueListenable: valueNotifier,
builder: (BuildContext context, values, child ) {
return Column(
children : <Widget>[
TextField(
autofocus: true,
maxLines: 6,
controller: _controller,
decoration: InputDecoration(
labelText: "Note",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0),
),
),
),
RaisedButton(child : Text("click me"),onPressed : (){
valueNotifier.value = _controller.text;
}),
Text(values),
],
);
},
),
)
)
)
);
}
}
From the docs:
ValueListenableBuilder<T>
A widget whose content stays synced with a ValueListenable.
Given a ValueListenable and a builder which builds widgets from concrete values of T, this class will automatically register itself as a listener of the ValueListenable and call the builder with updated values when the value changes.
The valueListenable property is of type ValueListenable<T>, which is an interface implemented by ValueNotifier<T>.
Therefore you need to create an instance of ValueNotifier<T>:
final ValueNotifier valueNotifier = ValueNotifier("initial");
In this case, I created it with type String with an intial value initial. Then assign this instance to the property valueListenable:
valueListenable: valueNotifier,
The builder which is of type ValueWidgetBuilder<T> will only get called when valueNotifier is updated.
Therefore you can create a RaisedButton and onPressed, you can update the valueNotifier value, which will call the builder and update the Text widget.
https://api.flutter.dev/flutter/widgets/ValueListenableBuilder-class.html
If you wanna use TextEditingController with ValueNotifier to handle real time when user input anything in TextField/TextFormField like picture below:
Ex: For my prj, I'm handling to display or not the Text('This email not match format ...') below the Email TextField when the user input from keyboard.
If matched with the email format: hide the Text (red Color).
If not match: show the Text (red Color).
So, in order to handle realtime, you can wrap your TextFormField (with TextEditingController related) by ValueListenableBuilder with valueListenable is editingController corresponding.
CustomTextField(
hintText: R.strings.emailHint!,
labelTextField: "Email",
isRequired: true,
textController: editEmailController, // use editEmailController for TextField related.
focusNode: editEmailFocusNode,
textInputType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
),
ValueListenableBuilder(
valueListenable: editEmailController, // no need to define the variable of ValueNotifier, use directly the textEditingController variable of TextFormField related.
builder: (_, value, __) {
return editEmailController.text.trim().isNotEmpty &&
validateEmail(editEmailController.text.trim()) ==
false
? Align(
alignment: Alignment.centerLeft,
child: Text(
R.strings.emailNotMatchFormat!,
style: const TextStyle(
fontWeight: FontWeight.w400,
fontSize: 12,
color: Colors.redAccent,
),
),
)
: const SizedBox();
},
),

How to disable mobile keyboard action key using Flutter?

I just need to disable the action button while the search query is empty. I'm not sure if this is possible with native Flutter.
Does this need to be done with each platform specifically? (iOS / Android)
You have a couple of options.
You can create a new focus:
FocusScope.of(context).requestFocus(new FocusNode())
Example:
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: ....,
)
)
)
}
The other option is to release the existing focus:
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
There is also a package: keyboard_dismisser
You just need to add a FocusNode to your TextFormField and request focus for it if the user presses the submit button on the keyboard when the field is empty. Here is a complete example:
class KeyboardKeeper60943209 extends StatefulWidget {
#override
_KeyboardKeeper60943209State createState() => _KeyboardKeeper60943209State();
}
class _KeyboardKeeper60943209State extends State<KeyboardKeeper60943209> {
List<String> items = List.generate(20, (index) => 'item $index');
TextEditingController _textEditingController = TextEditingController();
FocusNode _focusNode = FocusNode();
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: TextFormField(
focusNode: _focusNode,
controller: _textEditingController,
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
labelText: 'Search',
hasFloatingPlaceholder: false,
),
// This is the key part
onFieldSubmitted: (value) {
if(value == ''){
_focusNode.requestFocus();
}
},
),
),
FlatButton(onPressed: search, child: Text('Search'))
],
),
Expanded(
child: Container(
child: ListView.builder(
itemBuilder: (context, index){
return ListTile(
title: Text(items[index]),
subtitle: Text(items[index]),
);
}
),
),
),
],
);
}
void search(){
print('search');
}
}
TextFormField(
readOnly: true,
showCursor: false,
controller: consName,
decoration: InputDecoration(
hintText: 'Enter Name',
labelText: 'Username',
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.deepPurple,
width: 2,
),
),
),
),
Very Simple & Easy Use "readOnly: true" for disabling keyboard and if you don't want pointer or cursor then "showCursor: false". That's it, hope this'll work. Eat Sleep Workout Code Repeat. Happy Coding😊.
You can simply use textInputAction: TextInputAction.none on the TextField.

TextField with labelText on right

I'm trying to put the label of an TextField on right, but I don't know how.
The text is ok, but the problem is about the label.
TextField(
enabled: this.enable,
enableInteractiveSelection: false,
autofocus: false,
textAlign: TextAlign.right,
keyboardType: TextInputType.number,
decoration: InputDecoration(
labelText: this.label,
hintText: this.hint,
alignLabelWithHint: true,
errorText: snapshot.hasError ? snapshot.error : null),
obscureText: this.obscure,
controller: _controller,
);
Does someone could help me how to put the labelText on the right as well?
you can use Directionality.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _MyHomePageState();
}
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Padding(
padding: EdgeInsets.all(30),
child: Directionality(
textDirection: TextDirection.rtl,
child: TextField(
textAlign: TextAlign.right,
autofocus: true,
decoration: new InputDecoration(
border: OutlineInputBorder(),
labelText: "ایمیل",
hintText: "ایمیل خود را وارد کنید"
),
)
)
),
)
);
}
}
I think the TextField widget doesn't have a property to do that, so you have 2 options:
1- Create a custom TextField with your custom properties and include a property to align the label text.
2- Remove the label text and use only the hint value, that's pretty common in material design, so your code should be something like this:
TextFormField(
enableInteractiveSelection: false,
autofocus: false,
textAlign: TextAlign.right,
keyboardType: TextInputType.number,
decoration: InputDecoration(
hintText: "Hello",
alignLabelWithHint: true,
),
)