What this code does is print the position of the cursor in the text when you click on it.
TextEditingController controller = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
),
),
body: TextField(
controller: controller,
keyboardType: TextInputType.multiline,
maxLines: null,
onTap: () {
int pos = controller.selection.start;
print(
"Position: $pos",
);
},
),
);
}
But the problem is that this is for web so I need it to say the position of the cursor whenever it moves not only when I tap on it.
I mean if you move the cursor with the keyboard it should also print the position.
I already tried with GestureDetector and Focus and nothing. Any idea what I can use?
UPDATE:
The solution I found was the following and it works perfectly, for what I need:
TextEditingController controller = TextEditingController();
int cursorPosition = 0;
#override
void initState() {
controller.addListener(() {
setState(() {
cursorPosition = controller.selection.base.offset;
print("cursorPosition: $cursorPosition");
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
),
),
body: TextField(
controller: controller,
keyboardType: TextInputType.multiline,
maxLines: null,
),
);
}
Full Code:
import 'package:flutter/material.dart';
import 'package:flutter/services.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 StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController controller = TextEditingController();
int cursorPosition = 0;
#override
void initState() {
controller.addListener(() {
setState(() {
cursorPosition = controller.selection.base.offset;
print("cursorPosition: $cursorPosition");
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
),
),
body: TextField(
controller: controller,
keyboardType: TextInputType.multiline,
maxLines: null,
),
);
}
}
The solution I found was the following and it works perfectly, for what I need:
TextEditingController controller = TextEditingController();
int cursorPosition = 0;
#override
void initState() {
controller.addListener(() {
setState(() {
cursorPosition = controller.selection.base.offset;
print("cursorPosition: $cursorPosition");
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
),
),
body: TextField(
controller: controller,
keyboardType: TextInputType.multiline,
maxLines: null,
),
);
}
Full Code:
import 'package:flutter/material.dart';
import 'package:flutter/services.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 StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController controller = TextEditingController();
int cursorPosition = 0;
#override
void initState() {
controller.addListener(() {
setState(() {
cursorPosition = controller.selection.base.offset;
print("cursorPosition: $cursorPosition");
});
});
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
widget.title,
),
),
body: TextField(
controller: controller,
keyboardType: TextInputType.multiline,
maxLines: null,
),
);
}
}
onChanged: (value) {
int pos = controller.selection.start;
print(
"Position: $pos",
);
},
Related
enter image description here
Here I want to change the button which depends on a text field, like when the text field is filled then show the button C, and when clicked the C button then change the button name C to AC and also need to change text field fill to empty.
check this example to demonstrate the output you need
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Location',
theme: ThemeData(
primarySwatch: Colors.amber,
),
home: const MyHomePage(title: 'Flutter Location Demo'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, this.title}) : super(key: key);
final String? title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _controller = TextEditingController();
String? buttonText;
#override
void initState() {
_controller.addListener(_checkTextIsEmpty);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title!),
),
body: Center(
child: Column(
children: [
TextField(
controller: _controller,
onChanged: (value) {},
),
Text(buttonText ?? ''),
],
),
),
);
}
void _checkTextIsEmpty() {
final value = _controller.text.isEmpty ? "AC" : "C";
setState(() {
buttonText = value;
});
}
}
I am having this issue github link. What happens is if a widget uses TickerProviderStateMixin then it gets rebuilt when a page navigation occurs. I have a very complex page and rebuilding the whole page causes a UI jank on page navigation. If I do not rebuild then everything is fine no janks. Is there a workaround for this? It seems to me that this is some sort of an internal flutter bug or unexpected behaviour?
Example:
import 'package:flutter/material.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: PageA(title: 'Flutter Demo Home Page'),
);
}
}
class PageA extends StatefulWidget {
PageA({Key key, this.title}) : super(key: key);
final String title;
#override
_PageAState createState() => _PageAState();
}
class _PageAState extends State<PageA>
with SingleTickerProviderStateMixin {
TabController tabController;
#override
void initState() {
super.initState();
tabController = TabController(length: 2, vsync: this);
}
void toPageB() {
//tabController.animateTo(1);
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
return PageB();
}));
}
#override
Widget build(BuildContext context) {
print("Page A");
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: TabBar(
tabs: [
Text(
"Tab A",
style: Theme.of(context).textTheme.bodyText1,
),
Text(
"Tab B",
style: Theme.of(context).textTheme.bodyText1,
)
],
controller: tabController,
),
),
floatingActionButton: FloatingActionButton(
onPressed: toPageB,
child: Icon(Icons.add),
),
);
}
}
class PageB extends StatelessWidget {
#override
Widget build(BuildContext context) {
print("Page B");
return Scaffold(
appBar: AppBar(
title: Text("Page A"),
),
body: Container(
child: Center(
child: Text("Page A"),
),
));
}
}
#override
// ignore: must_call_super
void didChangeDependencies() {}
just add the code to prevent the rebuild, I dont know the side effect, but this walk around works for my app.
This is the solution I used before.
Change your Page A like
class PageA extends StatefulWidget {
PageA({Key key, this.title}) : super(key: key);
final String title;
#override
_PageAState createState() => _PageAState();
}
class _PageAState extends State<PageA> {
TabController tabController;
// #override
// void initState() {
// super.initState();
// tabController = TabController(length: 2, vsync: this);
// }
void toPageB() {
tabController.animateTo(1);
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
return PageB();
}));
}
#override
Widget build(BuildContext context) {
print("Page A");
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: CustomTabBar(
tabs: [
Text(
"Tab A",
style: Theme.of(context).textTheme.bodyText1,
),
Text(
"Tab B",
style: Theme.of(context).textTheme.bodyText1,
)
],
controller: (controller) {
tabController = controller;
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: toPageB,
child: Icon(Icons.add),
),
);
}
}
And add a new class CustomTabBar
class CustomTabBar extends StatefulWidget {
const CustomTabBar({
this.controller,
this.tabs,
Key? key,
}) : super(key: key);
final Function(TabController)? controller;
final List<Widget>? tabs;
#override
_CustomTabBarState createState() => _CustomTabBarState();
}
class _CustomTabBarState extends State<CustomTabBar>
with SingleTickerProviderStateMixin {
late TabController tabController;
#override
void initState() {
super.initState();
tabController =
TabController(length: widget.tabs?.length ?? 0, vsync: this);
if (widget.controller != null) {
widget.controller!(tabController);
}
}
#override
void dispose() {
tabController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return TabBar(
tabs: widget.tabs ?? [],
controller: tabController,
);
}
}
It should fix the issue that Page A rebuild
By setting _mytexteditingcontroller.value , We can update value of TextField, But inputFormatters is not running
How can I force inputFormatters to reformat value?
Here is a minimal example , I use LengthLimitingTextInputFormatter(3) to limit length of input, by running _controller.text = '12345678' I want to tell flutter to reformat input again
CONSIDER THAT IT IS A MININAL EXAMPLE, DONT TELL ME USE SUBSTRING TO FIX IT
/// Flutter code sample for TextField
// This sample shows how to get a value from a TextField via the [onSubmitted]
// callback.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(MyApp());
/// This is the main application widget.
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
/// This is the stateful widget that the main application instantiates.
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
/// This is the private State class that goes with MyStatefulWidget.
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
TextEditingController _controller;
void initState() {
super.initState();
_controller = TextEditingController();
}
void dispose() {
_controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
RaisedButton(
child: Text('Set Text'),
onPressed: () {
_controller.text = '12345678';
}),
TextField(
controller: _controller,
inputFormatters: [
LengthLimitingTextInputFormatter(3),
],
onSubmitted: (String value) async {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Thanks!'),
content: Text('You typed "$value".'),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('OK'),
),
],
);
},
);
},
),
],
),
),
);
}
}
As mentioned above this is a currently open known issue https://github.com/flutter/flutter/issues/30369. However you may try using an extension on TextInputFormatter to achieve a similar result.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
final Color darkBlue = Color.fromARGB(255, 18, 32, 47);
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
static const String _title = 'Flutter Code Sample';
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
MyStatefulWidget({Key key}) : super(key: key);
#override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
TextEditingController _controller;
void initState() {
super.initState();
_controller = TextEditingController();
}
void dispose() {
_controller.dispose();
super.dispose();
}
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
children: [
RaisedButton(
child: Text('3 Length Limit'),
onPressed: () {
_controller.text = LengthLimitingTextInputFormatter(3).format('12345678');
}),
RaisedButton(
child: Text('Upper case'),
onPressed: () {
_controller.text = UpperCaseTextFormatter().format('upper');
}),
RaisedButton(
child: Text('Length Limit & Upper chained'),
onPressed: () {
_controller.text = LengthLimitingTextInputFormatter(3).format(UpperCaseTextFormatter().format('upper'));
}),
TextField(
controller: _controller,
inputFormatters: [
LengthLimitingTextInputFormatter(3),
UpperCaseTextFormatter(),
],
),
],
),
),
);
}
}
extension on TextInputFormatter {
String format(String text) {
return formatEditUpdate(
const TextEditingValue(),
TextEditingValue(
text: text,
selection: TextSelection(
extentOffset: text.length,
baseOffset: text.length,
),
),
).text;
}
}
class UpperCaseTextFormatter extends TextInputFormatter {
#override
TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
return TextEditingValue(
text: newValue.text?.toUpperCase(),
selection: newValue.selection,
);
}
}
I am new to Flutter.
If I click a check box action should be performed.
Eg: Click a checkbox, enable the other checkbox and enable a text field
disable or enable widget only for button click is available.
I don't know how to do it in flutter
The idea is to use ternary operator ( ? : ) this works same as if does. Most basic explanation about code below when the checkbox is triggered checkBox1 changes and widget rebuilds with checkBox1 equals true now so instead of empty Container currently we are building new CheckBox.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool checkBox1 = false;
bool checkBox2 = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: [
CheckboxListTile(
title: Text("title text"),
value: checkBox1,
onChanged: (newValue) {
setState(() {
checkBox1 = newValue;
});
},
),
checkBox1
? CheckboxListTile(
title: Text("title text2"),
value: checkBox2,
onChanged: (newValue) {
setState(() {
checkBox2 = newValue;
});
},
)
: Container(),
],
),
);
}
}
final TextEditingController _controllerTE =
TextEditingController();
bool cbFlag = false;
TextField(
readOnly: !cbFlag,
controller: _controllerTE,
decoration: InputDecoration(
labelText: 'TE disabled till CB checked',
prefixIcon: Checkbox(
value: cbFlag,
onChanged: (bool? value) {
setState(() {
cbFlag = value!;
});
},
),
),
),
I m working on a web project using Flutter web, I understand currently Flutter web is only in beta version.
Basically I m implementing a web code editor using textfield, If the user press TAB key I want to make an indent as usual. However when I press tab it either move to next element (let say I have a button below the textarea) or it do not indent at all. Anyone know how to solve this?
example code below :
Widget build(BuildContext context) {
return Container(
color: Colors.black87,
child: Padding(
padding: EdgeInsets.all(8.0),
child:TextField(
controller: controller,
onEditingComplete: (){print('editing complete');},
onTap: (){},
onChanged: (text){print(text);},
style: TextStyle(fontStyle: FontStyle.italic,color: Colors.white),
maxLines: 20,
autofocus: true,
decoration: InputDecoration.collapsed(hintText: "write code for the formation"),
),
)
);
}
}
I've accomplished adding tabs with something like this:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData.dark(),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class InsertTabIntent extends Intent {
const InsertTabIntent(this.numSpaces, this.textController);
final int numSpaces;
final TextEditingController textController;
}
class InsertTabAction extends Action {
#override
Object invoke(covariant Intent intent) {
if (intent is InsertTabIntent) {
final oldValue = intent.textController.value;
final newComposing = TextRange.collapsed(oldValue.composing.start);
final newSelection = TextSelection.collapsed(
offset: oldValue.selection.start + intent.numSpaces);
final newText = StringBuffer(oldValue.selection.isValid
? oldValue.selection.textBefore(oldValue.text)
: oldValue.text);
for (var i = 0; i < intent.numSpaces; i++) {
newText.write(' ');
}
newText.write(oldValue.selection.isValid
? oldValue.selection.textAfter(oldValue.text)
: '');
intent.textController.value = intent.textController.value.copyWith(
composing: newComposing,
text: newText.toString(),
selection: newSelection,
);
}
return '';
}
}
class _MyHomePageState extends State<MyHomePage> {
TextEditingController textController;
#override
void initState() {
super.initState();
textController = TextEditingController();
}
#override
void dispose() {
textController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Actions(
actions: {InsertTabIntent: InsertTabAction()},
child: Shortcuts(
shortcuts: {
LogicalKeySet(LogicalKeyboardKey.tab):
InsertTabIntent(2, textController)
},
child: TextField(
controller: textController,
textInputAction: TextInputAction.newline,
maxLines: 30,
keyboardType: TextInputType.multiline,
),
),
),
],
),
),
);
}
}