Flutter force reformat input - flutter

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,
);
}
}

Related

How to change the text color which is in a different widget on switch with a flutter provider?

How to change the text color which is in a different widget on switch with a flutter provider?
When switch is on change text color to red else change to green. Bu don't merge first and second widgets.
When clicked switch button change other widget's text.
`
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(const SwitchApp());
class SwitchApp extends StatelessWidget {
const SwitchApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Switch Sample')),
body: const Center(
child: SwitchExample(),
),
),
);
}
}
class SwitchExample extends StatefulWidget {
const SwitchExample({super.key});
#override
State<SwitchExample> createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool light = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
// This bool value toggles the switch.
value: light,
activeColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
setState(() {
light = value;
});
},
),
MyText()
],
);
}
}
class MyText extends StatelessWidget {
const MyText({super.key});
#override
Widget build(BuildContext context) {
return const Text('Change my color',
style: TextStyle(color: Colors.green));
}
}
`
The easiest way would be to pass the color down into the constructor of MyText widget, since MyText widget is being built as a child of SwitchExample which is handling the switch state.
import 'package:provider/provider.dart';
void main() => runApp(const SwitchApp());
class SwitchApp extends StatelessWidget {
const SwitchApp({super.key});
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Switch Sample')),
body: const Center(
child: SwitchExample(),
),
),
);
}
}
class SwitchExample extends StatefulWidget {
const SwitchExample({super.key});
#override
State<SwitchExample> createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool light = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
// This bool value toggles the switch.
value: light,
activeColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
setState(() {
light = value;
});
},
),
MyText(light ? Colors.green : Colors.blue)
],
);
}
}
class MyText extends StatelessWidget {
final Color color;
const MyText(this.color, {super.key});
#override
Widget build(BuildContext context) {
return Text('Change my color',
style: TextStyle(color: color));
}
}
But, if you wanted to use provider so that MyText could be a child widget anywhere below the provider widget in the tree you could use Provider with a ChangeNotifier:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(const SwitchApp());
class ColorModel extends ChangeNotifier {
Color color = Colors.green;
void setColor(Color color) {
this.color = color;
notifyListeners();
}
}
class SwitchApp extends StatelessWidget {
const SwitchApp({super.key});
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider<ColorModel>(
create: (context) => ColorModel(),
child: MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Switch Sample')),
body: const Center(
child: SwitchExample(),
),
),
),
);
}
}
class SwitchExample extends StatefulWidget {
const SwitchExample({super.key});
#override
State<SwitchExample> createState() => _SwitchExampleState();
}
class _SwitchExampleState extends State<SwitchExample> {
bool light = false;
#override
Widget build(BuildContext context) {
return Column(
children: [
Switch(
// This bool value toggles the switch.
value: light,
activeColor: Colors.red,
onChanged: (bool value) {
// This is called when the user toggles the switch.
setState(() {
light = value;
});
Provider.of<ColorModel>(context, listen: false)
.setColor(value ? Colors.green : Colors.blue);
},
),
MyText()
],
);
}
}
class MyText extends StatelessWidget {
const MyText({super.key});
#override
Widget build(BuildContext context) {
return Consumer<ColorModel>(builder: (context, state, _) {
return Text('Change my color', style: TextStyle(color: state.color));
});
}
}
Check out flutter's docs for more info: https://docs.flutter.dev/development/data-and-backend/state-mgmt/simple

How to TextFormField in flutter when state changes, using Provider/ChangeNotifier?

I have an issue with updating text inside TextFormField when using Provider as state management.
I reduced my problem to an abstract one (I removed all the clutter code) and here how it works:
there is a someValue in AppState
the someValue can be edited via Form->TextFormField
the someValue is to be reflected as a title of the AppBar when typing (onChange)
the someValue can be updated from external source (in the example it is a button that updates it)
when someValue is updated from external source, it MUST be updated in text Form->TextFormField as well
The last one is causing me the problem. Consider the following code:
AppState.dart
import 'package:flutter/foundation.dart';
class AppState extends ChangeNotifier{
String someValue = '';
updateSomeValue(String newValue){
someValue = newValue;
notifyListeners();
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:text_ctrl_issue/app_state.dart';
void main() {
runApp(ChangeNotifierProvider(create: (_) => AppState(), child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
late TextEditingController _controller;
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
_controller = TextEditingController();
}
#override
Widget build(BuildContext context) {
final provider = Provider.of<AppState>(context);
// following line of code makes it possible for text to be changed by button
// and reflected in TextFormField
// but it causes nasty side effect, that when typing, cursor always goes to beginning of the line
_controller.text = provider.someValue;
return Scaffold(
appBar: AppBar(
title: Text(provider.someValue),
),
body: Center(
child: Form(
key: _formKey,
child: Column(children: [
TextFormField(
controller: _controller,
onChanged: (value) {
provider.updateSomeValue(value);
},
),
ElevatedButton(
onPressed: () {
provider.updateSomeValue('foo_bar');
},
child: Text('change text external source'))
])),
),
);
}
}
The problem:
When I added the line _controller.text = provider.someValue; it fixed the issue of updating TextFormField when button is clicked, but it create new issue, that when typing in TextFormField, it is also triggered, cause carret of text field to move to the beginning of the text field.
How to make it work so the text (value) of a TextFormField can be updated externally, without causing carret issue when typing?
EDIT
The answer of Yeasin Sheikh using addListener doesn't quite work (it is hacky) because:
it listens to every event (e.g. onFocus or cursor changed)
it does not take into account situation that EleveatedButton is in different scope than _controller (e.g. is in different widget).
An easy way of doing this by listening TextEditingController, while the TextFormField is the ruler here.
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
late TextEditingController _controller;
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
void initState() {
super.initState();
_controller = TextEditingController()
..addListener(() {
Provider.of<AppState>(context, listen: false)
.updateSomeValue(_controller.text);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(context.watch<AppState>().someValue),
),
body: Center(
child: Form(
key: _formKey,
child: Column(
children: [
TextFormField(
controller: _controller,
),
ElevatedButton(
onPressed: () {
_controller.text = 'foo_bar';
},
child: Text('change text external source'))
],
),
),
),
);
}
}
Also, you can check riverpod
import 'package:flutter/foundation.dart';
class AppState extends ChangeNotifier
{
TextEditingController _controller=TextEditingController();
TextEditingController get controller=>_controller();
String someValue = '';
updateSomeValue(String newValue)
{
someValue = newValue;
notifyListeners();
}
}
main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:text_ctrl_issue/app_state.dart';
void main() {
runApp(ChangeNotifierProvider(create: (_) => AppState(), child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
#override
void dispose() {
super.dispose();
}
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
final provider = Provider.of<AppState>(context);
return Scaffold(
appBar: AppBar(
title: Text(provider.someValue),
),
body: Center(
child: Form(
key: _formKey,
child: Column(children: [
TextFormField(
controller: Provider.controller,
onChanged: (v) {
provider.updateSomeValue(v);
},
),
ElevatedButton(
onPressed: () {
provider.updateSomeValue('foo_bar');
},
child: Text('change text external source'))
])),
),
);
}
}

Animated moveable list in flutter?

any tips or help how can I make this on tap moveable list in flutter?
https://files.fm/f/txdn29dg3
The provided component is exactly what CupertinoPicker could offer you.
Also, as suggested in the documentation, you should combine the CupertinoPicker with showCupertinoModalPopup to display the picker modally at the bottom of the screen.
This is how the code could look like:
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: const Scaffold(
body: Center(
child: PickerPage(),
),
),
);
}
}
class PickerPage extends StatefulWidget {
const PickerPage();
#override
_PickerPageState createState() => _PickerPageState();
}
class _PickerPageState extends State<PickerPage> {
final _items = [
'Flat Rate',
'Hourly',
'Request for Price',
];
int _selectedItem = 0;
void _onSelectedItemChanged(int value) => setState(
() => _selectedItem = value,
);
void _showPicker() {
showCupertinoModalPopup(
context: context,
builder: (_) => PickerExample(
items: _items,
selectedItem: _selectedItem,
onSelectedItemChanged: _onSelectedItemChanged,
),
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_items[_selectedItem]),
const SizedBox(height: 10.0),
ElevatedButton(
child: const Text('Show picker'),
onPressed: _showPicker,
),
],
);
}
}
class PickerExample extends StatefulWidget {
final List<String> items;
final int selectedItem;
final ValueSetter<int> onSelectedItemChanged;
const PickerExample({
required this.items,
required this.selectedItem,
required this.onSelectedItemChanged,
});
#override
_PickerExampleState createState() => _PickerExampleState();
}
class _PickerExampleState extends State<PickerExample> {
late final FixedExtentScrollController _controller;
#override
void initState() {
super.initState();
_controller = FixedExtentScrollController(initialItem: widget.selectedItem);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SizedBox(
height: 300,
child: CupertinoPicker(
scrollController: _controller,
backgroundColor: Colors.white,
itemExtent: 30.0,
children: [
for (final item in widget.items) Center(child: Text(item)),
],
onSelectedItemChanged: widget.onSelectedItemChanged,
),
);
}
}
You could also find an interactive example in this DartPad.

Flutter - How to trigger an animation inside a child widget from its parent widget [duplicate]

Let said I have a widget "mySonWidget" inside this widget I have a function "updateIconColor", I want to call that function from the father of "mySonWidget"
I did something similiar with callback but this is not the same scenario.
I saw people other widgets doing similar thing with controllers, but I don't know how create a custom controller.
How can I do it?
HELP
class mySonWidget extends StatefulWidget {
#override
_mySonWidgetState createState() => _mySonWidgetState();
}
class _mySonWidgetState extends State<mySonWidget> {
var _iconColor = Colors.red[500];
void updateIconColor() {
setState(() {
print('updateIconColor was called');
_iconColor = Colors.green;
});
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (Icon(Icons.star)),
color: _iconColor,
onPressed: () {},
),
);
}
}
father example:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(children: <Widget>[
mySonWidget(),
FlatButton(
onPressed: () {
/*..Call Function inside mySonWidget (updateIconColor) ..*/
},
child: Text(
"Change Color",
),
),
]),
),
);
}
}
You can copy paste run full code below
You can use GlobalKey can use _key.currentState to call updateIconColor()
code snippet
GlobalKey _key = GlobalKey();
...
mySonWidget(key: _key),
FlatButton(
onPressed: () {
final _mySonWidgetState _state = _key.currentState;
_state.updateIconColor();
},
...
class mySonWidget extends StatefulWidget {
mySonWidget({Key key}) : super(key: key);
working demo
full code
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
GlobalKey _key = GlobalKey();
#override
Widget build(BuildContext context) {
return MaterialApp(
//theme: ThemeData.dark().copyWith(scaffoldBackgroundColor: darkBlue),
debugShowCheckedModeBanner: false,
home: SafeArea(
child: Scaffold(
body: Column(children: <Widget>[
mySonWidget(key: _key),
FlatButton(
onPressed: () {
final _mySonWidgetState _state = _key.currentState;
_state.updateIconColor();
},
child: Text(
"Change Color",
),
),
]),
),
),
);
}
}
class mySonWidget extends StatefulWidget {
mySonWidget({Key key}) : super(key: key);
#override
_mySonWidgetState createState() => _mySonWidgetState();
}
class _mySonWidgetState extends State<mySonWidget> {
var _iconColor = Colors.red[500];
void updateIconColor() {
setState(() {
print('updateIconColor was called');
_iconColor = Colors.green;
});
}
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(0),
child: IconButton(
icon: (Icon(Icons.star)),
color: _iconColor,
onPressed: () {},
),
);
}
}

Constructor of screen is called every time a change occurs in that screen

When tapping a textformfield in a pushed screen, the constructor of the screen is called again and the textformfield loses its value. Also, I think that every change happens in that screen causes its constructor to be called again, and I don't know the reason at all.
Here is a sample code that generates the error:
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
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> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Hello',
style: TextStyle(color: Colors.black, fontSize: 30.0),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => NextScreen(Bloc())));
},
child: Icon(Icons.add),
),
);
}
}
And here is the screen to be pushed
class NextScreen extends StatefulWidget {
final _bloc;
NextScreen(this._bloc);
#override
_NextScreenState createState() => _NextScreenState();
}
class _NextScreenState extends State<NextScreen> {
#override
void dispose() {
widget._bloc.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.all(100.0),
child: IconButton(
onPressed: () {
Navigator.of(context).pop();
},
icon: Icon(Icons.arrow_back),
iconSize: 20.0,
),
),
StreamBuilder<String>(
stream: widget._bloc.stream,
builder: (context, snapshot) {
return TextFormField(
controller: widget._bloc.controller,
onFieldSubmitted: widget._bloc.submitData(),
decoration: InputDecoration(
hintText: 'Enter your name..',
errorText: snapshot.data,
),
);
})
],
),
);
}
}
A bloc that validates the user input
class Bloc {
TextEditingController _controller;
TextEditingController get controller => _controller;
BehaviorSubject<String> _subject;
BehaviorSubject<String> _validatorSubject;
Stream<String> get stream => _validatorSubject.stream;
void submitData() {
_subject.sink.add(controller.text);
}
void _validate(String text) {
if (!RegExp(r'[0-9]').hasMatch(text)) {
_validatorSubject.sink.add('numbers only');
} else {
_validatorSubject.sink.add(null);
}
}
Bloc() {
_controller = TextEditingController();
_subject = BehaviorSubject<String>();
_validatorSubject = BehaviorSubject<String>();
_subject.stream.listen(_validate);
}
void dispose() {
_subject.close();
_validatorSubject.close();
}
}
Opening and closing a keyboard will rebuild the whole screen.
The real culprit here is the textController :
controller: widget._bloc.controller,
The solution which worked for me is to remove this line.
Also to get and validate the changed text you can use onChanged in the text field, which return a String.
Like this :
.
.
.
return TextFormField(
onChanged: widget._bloc.submitData,
decoration: InputDecoration
.
.
.
And you submitData() method would go like this :
void submitData(String data) {
_subject.sink.add(data);
}