How to show a constant text in textfield in Flutter along with typed data like in this picture.
You can use String Interpolation. Using $ to access variable in Text Widget.
#override
Widget build(BuildContext context) {
double quantity = 1;
return Row(
children: [
Icon(Icons.production_quantity_limits_outlined),
SizedBox(
width: 20,
),
Text("$quantity (qty)"),
],
);
}
class Example extends StatelessWidget {
const Example({super.key});
#override
Widget build(BuildContext context) {
double quantity = 1;
return TextField(
decoration: InputDecoration(
label: Text("$quantity (qty)"),
border: OutlineInputBorder()
),
);
}
}
you can use like this:
class TextFieldExample extends StatelessWidget {
const TextFieldExample({Key? key}) : super(key: key);
Widget build(BuildContext context) {
TextEditingController? controller;
return Scaffold(
body: Padding(
padding: EdgeInsets.all(20),
child: TextField(
controller: controller,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15.0),
),
filled: true,
hintText: '1(qty)',
prefixIcon: Icon(Icons.watch),
),
),
),
);
}
}
Related
Is Any Way How to pass a widget function to another page that is without any stateless/stateful? The file only includes widgets such as textfields, buttons and etc. I am trying not to cluster every fields in one page. Any helps/ideas would be appreciated!
Main.dart
class MainPage extends StatefulWidget {
const MainPage({super.key});
#override
State<Main Page> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
// bool for toggling password
bool isSecuredPasswordField = true;
#override
Widget build(BuildContext context) {
return Container();
}
// widget function that I need to pass on widget_fields.dart
Widget togglePassword() {
return IconButton(
onPressed: () {
setState(() {
isSecuredPasswordField = !isSecuredPasswordField;
});
},
icon: isSecuredPasswordField
? const Icon(Icons.visibility)
: const Icon(Icons.visibility_off),
);
}
}
widget_fields.dart
Widget userPasswordField(_passwordUserCtrlr) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: TextFormField(
obscureText: true,
controller: _passwordUserCtrlr,
keyboardType: TextInputType.visiblePassword,
decoration: InputDecoration(
isDense: true,
suffixIcon: togglePassword(), //<-- I wanna call that function here
prefixIcon: const Icon(Icons.lock),
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Color(0xFFCECECE)),
borderRadius: BorderRadius.circular(12),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFFCECECE)),
),
hintText: 'Password',
hintStyle: const TextStyle(
fontFamily: 'Poppins',
fontSize: 14,
),
fillColor: const Color(0xFFFEFEFE),
filled: true,
),
validator: (value) {
if (value!.isEmpty) {
return "Please enter your password.";
} else if (value.length < 8) {
return "Password should be min. 8 characters.";
} else {
return null;
}
},
),
);
}
You can pass functions like any other variable. I made a full working example that's different than yours to show a more minimal example but you can apply the same logic for your code
main.dart
import 'package:flutter/material.dart';
import 'column.dart';
void main() {
runApp(const MaterialApp(home: MyApp()));
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
Widget returnSomeText() {
return const Text("test");
}
#override
Widget build(BuildContext context) {
return Scaffold(body: createColumn(returnSomeText));
}
}
column.dart
import 'package:flutter/material.dart';
Widget createColumn(Function widgetFunction) {
return Column(
children: [widgetFunction(), widgetFunction()],
);
}
As you can see the togglePassword from your code corresponds to returnSomeText in mine. and userPasswordField is like createColumn. But it must be said that it's not recommended to use helper functions like createColumn here but to turn it into a StatelessWidget, like this for example:
import 'package:flutter/material.dart';
class CreateColumn extends StatelessWidget {
final Function widgetFunction;
const CreateColumn({Key? key, required this.widgetFunction}) : super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: [widgetFunction(), widgetFunction()],
);
}
}
And then in main.dart:
return Scaffold(body: CreateColumn(widgetFunction: returnSomeText));
See also this YouTube video: Widgets vs helper methods
This is Example that how you call Widget in another class:
class MainApge extends StatefulWidget {
const MainApge({Key? key}) : super(key: key);
#override
State<MainApge> createState() => _MainApgeState();
}
class _MainApgeState extends State<MainApge> {
#override
Widget build(BuildContext context) {
return Column(
children: [
ContainerTextFields.customsTextField(
"User Name",
'enter name',
userNameController,
),
],
);
}
}
This is Custom Widget Class:
class ContainerTextFields {
static Widget customsTextField(
String label, String cusHintText, TextEditingController _controller) {
return Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
Padding(
padding: EdgeInsets.only(
left: SizeConfig.screenHeight! * 0.05,
top: SizeConfig.screenHeight! * 0.03),
child: Text(
label,
style: AppStyle.kUnSyncedDialogeText.copyWith(
color: AppColors.kTextFieldLabelColorGrey,
fontWeight: FontWeight.bold),
)),
Padding(
padding: EdgeInsets.only(
top: SizeConfig.screenHeight! * 0.02,
left: SizeConfig.screenHeight! * 0.042,
right: SizeConfig.screenWidth! * 0.042),
child: SingleChildScrollView(
child: Container(
height: SizeConfig.screenHeight! * 0.065,
child: TextFormField(
controller: _controller,
decoration: InputDecoration(
hintText: cusHintText,
hintStyle: TextStyle(
color: AppColors.kLoginPopUpColor,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
),
),
)
]);
}
}
You can pass the widget as parameter to child widget:
class MyTextField extends StatelessWidget {
const MyTextField({Key? key,
this.togglePassword,
this.passwordUserCtrlr
})
: super(key: key);
final Widget? togglePassword;
final TextEditingController? passwordUserCtrlr;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: TextFormField(
obscureText: true,
controller: passwordUserCtrlr,
keyboardType: TextInputType.visiblePassword,
decoration: InputDecoration(
isDense: true,
suffixIcon: togglePassword, //<-- I wanna call that function here
prefixIcon: const Icon(Icons.lock),
enabledBorder: OutlineInputBorder(
borderSide: const BorderSide(color: Color(0xFFCECECE)),
borderRadius: BorderRadius.circular(12),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: Color(0xFFCECECE)),
),
hintText: 'Password',
hintStyle: const TextStyle(
fontFamily: 'Poppins',
fontSize: 14,
),
fillColor: const Color(0xFFFEFEFE),
filled: true,
),
validator: (value) {
if (value!.isEmpty) {
return "Please enter your password.";
} else if (value.length < 8) {
return "Password should be min. 8 characters.";
} else {
return null;
}
},
),
);
}
}
And can easily call from main widget:
class MainPage extends StatefulWidget {
const MainPage({super.key});
#override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
// bool for toggling password
bool isSecuredPasswordField = true;
TextEditingController? passwordUserCtrlr = TextEditingController();
#override
Widget build(BuildContext context) {
return MyTextField(
togglePassword: togglePassword(),
passwordUserCtrlr: passwordUserCtrlr,
);
}
// widget function that I need to pass on widget_fields.dart
Widget togglePassword() {
return IconButton(
onPressed: () {
setState(() {
isSecuredPasswordField = !isSecuredPasswordField;
});
},
icon: isSecuredPasswordField
? const Icon(Icons.visibility)
: const Icon(Icons.visibility_off),
);
}
}
You can create class like GlobalWidget for example, like this:
class GlobalWidget {
// widget function that I need to pass on widget_fields.dart
Widget togglePassword(Function()? onPressed, bool value) {
return IconButton(
onPressed: onPressed,
icon: value
? const Icon(Icons.visibility)
: const Icon(Icons.visibility_off),
);
}
}
And You can call the Widget like that :
GlobalWidget().togglePassword(() => setState(() {
isSecuredPasswordField = !isSecuredPasswordField;
}), isSecuredPasswordField)
What you are trying to do is impossible in the Flutter framework. You cannot call methods belonging to other widgets
Also, it is discouraged to use function to return widgets as this impacts the framework's ability to optimize the build process.
One possible solution is to package your complete password entry in a set of custom (statefull) widgets. You can collect those into a single source file if you like. Be sure to create a class for every widget.
So I am making a sign up page in Flutter. I'm gonna use this "flutter_form_builder" and "form_builder_validators" packages. I will make the textfields reusable to save time, and everything works, except the validators.
This is my reusable widget:
class MyFormBuilderTextField extends StatelessWidget {
final String name;
final Color inputTextColor;
final String labelText;
final Color labelColor;
final bool filled;
final Color fillColor;
final double borderRadius;
final Color enabledBorderColor;
final Color focusedBorderColor;
final FormFieldValidator<String> validators;
MyFormBuilderTextField({
required this.name,
this.inputTextColor = const Color(0xFFFFFFFF),
required this.labelText,
this.labelColor = Colors.white54,
this.filled = true,
this.fillColor = const Color(0xFF131313),
this.borderRadius = 10.0,
this.enabledBorderColor = Colors.white12,
this.focusedBorderColor = const Color(0xFFF57B3B),
required this.validators,
});
#override
Widget build(BuildContext context) {
return FormBuilderTextField(
name: name,
style: TextStyle(color: inputTextColor),
decoration: InputDecoration(
labelText: labelText,
labelStyle: TextStyle(color: labelColor),
filled: filled,
fillColor: fillColor,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(borderRadius),
borderSide: BorderSide(color: enabledBorderColor),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(borderRadius),
borderSide: BorderSide(color: focusedBorderColor),
),
),
validator:validators,
);
}
}
This is how I tried implementing it:
class MyFormBuilder extends StatefulWidget {
#override
State<MyFormBuilder> createState() => _MyFormBuilderState();
}
class _MyFormBuilderState extends State<MyFormBuilder> {
final _formKey = GlobalKey<FormBuilderState>();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF070707),
body: _buildContent(),
);
}
Widget _buildContent() {
return SingleChildScrollView(
padding: EdgeInsets.all(24.0),
child: FormBuilder(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(height: 30.0),
MyFormBuilderTextField(
name: 'password',
labelText: 'Password',
validators: [
FormBuilderValidators.min(8),
FormBuilderValidators.required(),
],
),
],
),
)
);
}
}
I am getting this error:
The argument type 'String? Function(String?)' can't be assigned to the parameter type 'List<String? Function(String?)>'.dartargument_type_not_assignable
How can I make the validators reusable?
How can I update the PinInput Boxes with input from the on-screen keyboard? From what I understand, whenever there's a state change, the widget will be rebuild. Hence, from below, what I did was updating the text whenever the on-screen keyboard detects tap. Then since the state is changed, I assumed it will rebuild all the widget which include the PinInput widget, and this is true since I tested the text whenever there's changes. I then did _pinPutController.text = text; to change the input of PinInput, however it is not working.
When I hardcode _pinPutController.text = '123', it works. So the problem is that it is not rebuilding. Am I understanding this correctly? How can I achieve what I wanted?
import 'package:flutter/material.dart';
import 'package:numeric_keyboard/numeric_keyboard.dart';
import 'package:pinput/pin_put/pin_put.dart';
import '../../../../constants.dart';
import '../../../../size_config.dart';
class InputForm extends StatefulWidget {
#override
_InputFormState createState() => _InputFormState();
}
class _InputFormState extends State<InputForm> {
String text = '';
#override
Widget build(BuildContext context) {
return Column(
children: [
PinInput(text: text),
NumericKeyboard(
onKeyboardTap: (value) {
setState(() {
text += value;
});
},
textColor: Colors.red,
rightButtonFn: () {
setState(() {
text = text.substring(0, text.length - 1);
});
},
rightIcon: Icon(
Icons.backspace,
color: Colors.red,
),
mainAxisAlignment: MainAxisAlignment.spaceEvenly),
],
);
}
}
class PinInput extends StatelessWidget {
const PinInput({
Key key,
this.text,
}) : super(key: key);
final String text;
#override
Widget build(BuildContext context) {
final size = getProportionateScreenHeight(60);
final TextEditingController _pinPutController = TextEditingController();
final FocusNode _pinPutFocusNode = FocusNode();
_pinPutFocusNode.unfocus();
print(text);
_pinPutController.text = text;
return PinPut(
fieldsCount: 4,
onSubmit: (String pin) => {},
focusNode: _pinPutFocusNode,
controller: _pinPutController,
preFilledWidget: Align(
alignment: Alignment.bottomCenter,
child: Divider(
color: kPrimaryColor,
thickness: 2.5,
indent: 7.5,
endIndent: 7.5,
),
),
textStyle: TextStyle(
fontSize: getProportionateScreenHeight(24),
),
eachFieldPadding: EdgeInsets.all(
getProportionateScreenHeight(10),
),
eachFieldMargin: EdgeInsets.all(
getProportionateScreenWidth(5),
),
eachFieldHeight: size,
eachFieldWidth: size,
submittedFieldDecoration: boxDecoration(),
selectedFieldDecoration: boxDecoration(),
followingFieldDecoration: boxDecoration(),
inputDecoration: InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
counterText: '',
),
withCursor: true,
pinAnimationType: PinAnimationType.scale,
animationDuration: kAnimationDuration,
);
}
BoxDecoration boxDecoration() {
return BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(
getProportionateScreenWidth(10),
),
);
}
}
The problem is that you recreate a new TextEditingController at each rebuild of your PinInput Widget. However, if you check the PinPutState of the pinput package, it keeps a reference to the first TextEditingController you provide in its initState method:
#override
void initState() {
_controller = widget.controller ?? TextEditingController();
[...]
}
So, you have to keep the same TextEditingController all the way.
The easiest way to fix this would be to raise the TextEditingController to the State of InputForm. Instead of a String text, just use a Controller:
class InputForm extends StatefulWidget {
#override
_InputFormState createState() => _InputFormState();
}
class _InputFormState extends State<InputForm> {
TextEditingController _controller = TextEditingController(text: '');
#override
Widget build(BuildContext context) {
return Column(
children: [
PinInput(controller: _controller),
NumericKeyboard(
onKeyboardTap: (value) => _controller.text += value,
textColor: Colors.red,
rightButtonFn: () => _controller.text =
_controller.text.substring(0, _controller.text.length - 1),
rightIcon: Icon(Icons.backspace, color: Colors.red),
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
),
],
);
}
}
Note: Since you use a TextEditingController instead of a String, you can get rid of all your setState methods.
Full source code:
import 'package:flutter/material.dart';
import 'package:numeric_keyboard/numeric_keyboard.dart';
import 'package:pinput/pin_put/pin_put.dart';
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
home: HomePage(),
),
);
}
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(child: InputForm()),
);
}
}
class InputForm extends StatefulWidget {
#override
_InputFormState createState() => _InputFormState();
}
class _InputFormState extends State<InputForm> {
TextEditingController _controller = TextEditingController(text: '');
#override
Widget build(BuildContext context) {
return Column(
children: [
PinInput(controller: _controller),
NumericKeyboard(
onKeyboardTap: (value) => _controller.text += value,
textColor: Colors.red,
rightButtonFn: () => _controller.text =
_controller.text.substring(0, _controller.text.length - 1),
rightIcon: Icon(Icons.backspace, color: Colors.red),
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
),
],
);
}
}
class PinInput extends StatelessWidget {
const PinInput({
Key key,
this.controller,
}) : super(key: key);
final TextEditingController controller;
#override
Widget build(BuildContext context) {
final size = 60.0;
final FocusNode _pinPutFocusNode = FocusNode();
_pinPutFocusNode.unfocus();
return PinPut(
fieldsCount: 4,
onSubmit: (String pin) => {},
focusNode: _pinPutFocusNode,
controller: controller,
preFilledWidget: Align(
alignment: Alignment.bottomCenter,
child: Divider(
color: Theme.of(context).primaryColor,
thickness: 2.5,
indent: 7.5,
endIndent: 7.5,
),
),
textStyle: TextStyle(
fontSize: 24,
),
eachFieldPadding: EdgeInsets.all(
10,
),
eachFieldMargin: EdgeInsets.all(
5,
),
eachFieldHeight: size,
eachFieldWidth: size,
submittedFieldDecoration: boxDecoration(),
selectedFieldDecoration: boxDecoration(),
followingFieldDecoration: boxDecoration(),
inputDecoration: InputDecoration(
border: InputBorder.none,
focusedBorder: InputBorder.none,
enabledBorder: InputBorder.none,
counterText: '',
),
withCursor: true,
pinAnimationType: PinAnimationType.scale,
animationDuration: Duration(milliseconds: 500),
);
}
BoxDecoration boxDecoration() {
return BoxDecoration(
color: Colors.white,
shape: BoxShape.rectangle,
borderRadius: BorderRadius.circular(
10,
),
);
}
}
UPDATE: How to hide the Keyboard...
...while keeping the focus blinking cursor
1. disable the focus on your PinPut fields:
For this, I used a class described here:
class AlwaysDisabledFocusNode extends FocusNode {
#override
bool get hasFocus => false;
}
...as the focusNode of the PinPut:
class PinInput extends StatelessWidget {
[...]
#override
Widget build(BuildContext context) {
final size = 60.0;
final FocusNode _pinPutFocusNode = AlwaysDisabledFocusNode();
// _pinPutFocusNode.unfocus();
return PinPut(
[...]
focusNode: _pinPutFocusNode,
[...]
);
}
}
So, now, the PinPut never gets the focus. The Soft Keyboard is not shown but, hey!, we lost the blinking cursor!
No worries, we'll keep it always on programmatically.
2. Keep the blinking cursor always on
For this, though, we'll have to change the code of the pinput package.
Currently, in PinPutState, the blinking cursor is shown on the next field if withCursor is set to true and the PinPut has the focus. Instead, we will always show the blinking cursor if withCursoris set to true:
if (widget.withCursor /* && _focusNode!.hasFocus */ && index == pin.length) {
return _buildCursor();
}
VoilĂ ! Does it work for you?
This has been mentioned on PinPut GitHub Issue Tracker [ref] about disabling the device keyboard when using a custom keyboard.
As you can see, the height is different.
The picture on the left shows the app running when the keyboard did not pop up. And the picture on the right shows the app running when keyboard pops up and clicks "Flutter Hot Reload"(Android Studio).
I want the textfield to be like the picture on the right without keyboard pop-up.
How can I fix this?
Appbar
class CustomAppbar extends StatelessWidget with PreferredSizeWidget{
#override
final Size preferredSize;
#override
static double height = AppBar().preferredSize.height;
CustomAppbar() : preferredSize = Size.fromHeight(height);
#override
Widget build(BuildContext context) {
#override
final double statusbarHeight = MediaQuery.of(context).padding.top;
return Container(
child: Stack(
children: <Widget>[
AppBar(
backgroundColor: Colors.white,
elevation: 0,
iconTheme: IconThemeData(color: Colors.black,),
),
Container(
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.all(Radius.circular(10))
),
margin: EdgeInsets.only(left: 60, top: statusbarHeight + 5, bottom: 5, right: 5),
child: InputBox(),
)
],
),
);
}
}
InputBox
class InputBox extends StatefulWidget {
#override
_InputBoxState createState() => _InputBoxState();
}
class _InputBoxState extends State<InputBox> {
TextEditingController _SearchController = TextEditingController();
FocusNode _focusNode = FocusNode();
String _SearchText = "";
_InputBoxState(){
_SearchController.addListener(() {
setState((){
_SearchText = _SearchController.text;
});
});
}
#override
Widget build(BuildContext context) {
return TextField(
focusNode: _focusNode,
style: TextStyle(fontSize: 19),
controller: _SearchController,
decoration: InputDecoration(
hintText: "Search",
border: InputBorder.none,
prefixIcon: Icon(Icons.search, color: Colors.white,),
suffixIcon: _focusNode.hasFocus ? IconButton(
icon: Icon(Icons.cancel, color: Colors.white,),
onPressed: (){
setState((){
_SearchController.clear();
_SearchText = "";
_focusNode.unfocus();
});
},
) : Container()
)
);
}
}
style:TextStyle(height: 20), //custome height
decoration: InputDecoration(
isDense: true) // remove padding inside textfield
you can use ContentPadding if you want give padding inside textfield
I am trying to create a registration widget. Most of the work I have done is finished and works.
I only have some issues with padding between my listed textfields.
How do I create a padding?
According to stack overflow this post is mostly code so here is some plain text because I really cannot say more about this, I have aksed my question and provided my code.
var entries = [];
class RegisPage extends StatefulWidget {
#override
_RegisPageState createState() => _RegisPageState();
}
class _RegisPageState extends State<RegisPage> {
#override
Widget build(BuildContext context) {
Map<String,TextEditingController> textEditingControllers = {};
var textFields = <TextFormField>[];
entries.forEach((str) {
var textEditingController = TextEditingController();
textEditingControllers.putIfAbsent(str, ()=>textEditingController);
return textFields.add(
TextFormField(
controller: textEditingController,
decoration:
InputDecoration(
labelText: str,
border:
OutlineInputBorder(
borderRadius:
BorderRadius.circular(25.0)
)
),
),
);
});
return Scaffold(
appBar: AppBar(
title: Text('Registration'),
),
body:Center(
child:
SingleChildScrollView(
child:
Column(
children:[
Column(children:textFields),
RaisedButton(
child: Text("Register"),
onPressed: (){
entries.forEach((str){
print(textEditingControllers[str].text);
});
}
)
]
)
)
)
);
}
}
If you want to add spacing you can just wrap the TextFormField with padding like this:
Padding(
padding: EdgeInsets.fromLTRB(left, top, right, bottom),
child: TextFormField(
controller: textEditingController,
decoration: InputDecoration(
labelText: str,
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(25.0)
)
),
),
),
Then instead of having a list of TextFormField you could do List<Widget>
Like this:
var textFields = <Widget>[];
Everything should still work the same, but now you can specify padding/spacing on all sides.
Hope this helps!