I have a dropdown and a textfield, my goal is when i select a value from dropdown and it will called api, the api will return a string value, and the returned value will be displayed in the textfield. Any help and suggestions will be appreciated in advance.
for example:
when I select "CN" value from the dropdown, the IDDD field will be changed to 86
my code:
class VerifyPhone extends StatefulWidget {
#override
State<StatefulWidget> createState() {
// implement createState
return VerifyPhoneState();
}
}
class VerifyPhoneState extends State<VerifyPhone> {
final _formKey = GlobalKey<FormState>();
String _phone = "";
String _code = "";
String? _iddd = '60';
TextEditingController _idddController = TextEditingController();
List<String> countryCode = ['MY', "CN"];
_showOtpDialog() {
var _form = _formKey.currentState as FormState;
if (_form.validate()) {
_form.save();
showOtpDialog(
context,
_phone,
success: (Map<String, String?>? result) {
if (result != null)
Provider.of<RegistLogic>(context, listen: false)
.doUpdateOtpResult(result);
RoutUtil.push(context, SetupPassword());
},
);
}
}
String? _phoneValidation(String? v) {
return [v.isMalaysiaMobileNo()].validate();
}
#override
void initState() {
super.initState();
_code = countryCode[0];
}
#override
void dispose() {
_idddController.dispose();
super.dispose();
}
_getIddd(String countryCode) async {
final response = await AuthService.countryInfo(
countryCode: countryCode,
);
if (response != null) {
_iddd = response['list01'][0]['int_phone_area_code'];
}
print('idd $_iddd');
}
#override
Widget build(BuildContext context) {
// implement build
return Scaffold(
backgroundColor: Colors.white,
body: ConstrainedBox(
constraints: BoxConstraints.expand(),
child: Stack(
children: <Widget>[
SingleChildScrollView(
child: Column(
children: <Widget>[
// _topTitleBar(context),
AuthHeader(options: HeaderOptions("Phone Number")),
_inputView(),
],
),
),
Positioned(
bottom: 18.0,
left: 20,
right: 20,
child: _postBotton(context),
)
],
),
));
}
Widget _inputView() {
return Container(
width: double.infinity,
margin: EdgeInsets.symmetric(horizontal: 10),
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Text(
'Please enter your phone number to verify your information',
style: TextStyle(color: CustomThemeColor.coolGray)),
),
Column(
children: [
Row(
children: [
Container(
width: 50,
child: DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Country',
labelStyle: TextStyle(fontSize: 12)),
value: _code,
validator: (v) => [v.isRequired()].validate(),
isDense: true,
onChanged: (val) {
FocusScope.of(context).requestFocus(FocusNode());
_code = val!;
_getIddd(val);
},
items: countryCode.map((item) {
return DropdownMenuItem<String>(
value: item,
child: Text(item),
);
}).toList(),
),
),
SizedBox(
width: 20,
),
Container(
width: 50,
child: TextFormField(
initialValue: _iddd,
enabled: false,
decoration: InputDecoration(
labelText: "IDDD",
),
onChanged: (value) {
setState(() {
_iddd = value;
});
},
onSaved: (val) {
_iddd = val!;
},
autovalidateMode: AutovalidateMode.onUserInteraction,
),
),
SizedBox(
width: 20,
),
Container(
width: 200,
child: TextFormField(
decoration: InputDecoration(
labelText: "Enter your phone number",
),
onSaved: (val) {
_phone = val!;
},
keyboardType: TextInputType.phone,
inputFormatters: [
FilteringTextInputFormatter.digitsOnly,
LengthLimitingTextInputFormatter(11),
],
validator: _phoneValidation,
autovalidateMode: AutovalidateMode.onUserInteraction,
),
)
],
)
],
)
],
),
));
}
Widget _postBotton(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 10),
padding: EdgeInsets.symmetric(vertical: 15),
child: RaisedButton(
padding: EdgeInsets.symmetric(vertical: 10),
onPressed: () {
_showOtpDialog();
},
color: CustomThemeColor.buttonBlue,
child:
Text('NEXT', style: TextStyle(fontSize: 20, color:
Colors.white)),
));
}
}
Thank you
_getIddd(String countryCode) async {
final response = await AuthService.countryInfo(
countryCode: countryCode,
);
if (response != null) {
_iddd = response['list01'][0]['int_phone_area_code'];
_idddController.text = _iddd;
}
print('idd $_iddd');
}
Related
I want to create a widget like this.
This contains the Textfield, Once type something in the field, It will be sorting the list and show the results below the field. The result list shown below can also have a checkbox field to select multiple items from the result.
Is there any built-in flutter widget that can be useful for this case?
Or any other package to achieve this.
Following is the screenshot for reference.
I tried with RawAutoComplete widget.
Here is the code.
class SearchBarDemo extends StatelessWidget {
const SearchBarDemo({super.key});
static List<String> suggestons = [
"USA",
"UK",
"Uganda",
"Uruguay",
"United Arab Emirates"
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: RawAutocomplete(
optionsBuilder: (textEditingValue) {
if (textEditingValue.text == '') {
return const Iterable<String>.empty();
} else {
List<String> matches = <String>[];
matches.addAll(suggestons);
matches.retainWhere((s) {
return s
.toLowerCase()
.contains(textEditingValue.text.toLowerCase());
});
return matches;
}
},
fieldViewBuilder:
(context, textEditingController, focusNode, onFieldSubmitted) {
return TextField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(7),
),
hintText: 'Search',
contentPadding: EdgeInsets.symmetric(
vertical: 8,
horizontal: 4), // EdgeInsets.only(top: 8, left: 5),
prefixIcon: Container(
margin: EdgeInsets.symmetric(vertical: 8, horizontal: 4),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
border: Border(
right: BorderSide(
color: Colors.grey.shade400,
),
),
),
child: Icon(
Icons.search,
color: Colors.grey.shade400,
),
),
),
controller: textEditingController,
focusNode: focusNode,
onSubmitted: (String value) {},
);
},
onSelected: (selection) {},
optionsViewBuilder: (context, onSelected, options) {
return Material(
child: SingleChildScrollView(
child: Column(
children: options.map((opt) {
return InkWell(
onTap: () {
onSelected(opt);
},
child: Column(
children: [
Container(
height: 50,
width: 250,
alignment: Alignment.topLeft,
child: Card(
child: SizedBox(
child: ListTile(
title: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
opt,
style: TextStyle(fontSize: 12),
),
Transform.scale(
scale: 0.8,
child: Checkbox(
value: false,
onChanged: (val) {},
),
),
],
),
),
),
),
),
],
),
);
}).toList(),
),
),
);
},
),
);
}
}
Output of the above code is:
It covers the whole screen and show the result content in center.
You can customize the filter logic. Also you may like SearchDelegate-class
class SearchBarDemo extends StatefulWidget {
const SearchBarDemo({super.key});
#override
State<SearchBarDemo> createState() => _SearchBarDemoState();
}
class _SearchBarDemoState extends State<SearchBarDemo> {
static List<String> suggestons = [
"USA",
"UK",
"Uganda",
"Uruguay",
"United Arab Emirates"
];
List<String> filterItems = [];
List<String> checkedItems = [];
late final TextEditingController controller = TextEditingController()
..addListener(() {
/// filter logic will be here
final text = controller.text.trim();
filterItems = suggestons
.where(
(element) => element.toLowerCase().startsWith(text.toLowerCase()))
.toList();
setState(() {});
});
#override
void dispose() {
controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
CupertinoTextField(
controller: controller,
),
Expanded(
child: ListView.builder(
itemCount: filterItems.length,
itemBuilder: (context, index) {
final bool isChecked =
checkedItems.contains(filterItems[index]);
return CheckboxListTile(
value: isChecked,
title: Text("${filterItems[index]}"),
onChanged: (value) {
if (isChecked) {
checkedItems.remove(filterItems[index]);
} else {
checkedItems.add(filterItems[index]);
}
setState(() {});
},
);
}),
),
],
));
}
}
I have attached a project which is having a bottom modal sheet. Which sheet contains three TextField as name, number and email. So here I have implemented CRUD (Create, read, update and delete) operation and it's fine working. But without validating the TextField it shows in the HomePage. although if I miss to enter name or number still it's passing the data to the homepage card. I have tried many validating options but didn't worked out. If anyone can please help me.
My code:
import 'package:flutter/material.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
List<Map<String, dynamic>> _contacts = [];
bool _isLoading = true;
final bool _validatename = true;
final bool _validatenumber = true;
final bool _validateemail = true;
void _refreshContacts() async {
final data = await Contact.getContacts();
setState(() {
_contacts = data;
_isLoading = false;
});
}
#override
void initState() {
super.initState();
_refreshContacts();
}
final _nameController = TextEditingController();
final _numberController = TextEditingController();
final _emailController = TextEditingController();
final bool _validate = false;
void _showForm(int? id) async {
if (id != null) {
final existingContact = _contacts.firstWhere((element) => element['id'] ==id);
_nameController.text = existingContact['name'];
_numberController.text = existingContact['number'];
_emailController.text = existingContact['email'];
}
showModalBottomSheet(context: context,
elevation: 5,
isScrollControlled: true,
builder: (_) => Container(
padding: EdgeInsets.only(top: 15, left: 15, right: 15, bottom: MediaQuery.of(context).viewInsets.bottom + 120),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
TextField(
controller: _nameController,
decoration: const InputDecoration(
hintText: "Name",
),
),
const SizedBox(
height: 10.0,
),
TextField(
keyboardType: TextInputType.number,
controller: _numberController,
decoration: const InputDecoration(
hintText: "Numbers",
),
),
const SizedBox(
height: 10.0,
),
TextField(
// keyboardType: TextInputType.emailAddress,
controller: _emailController,
decoration: const InputDecoration(
hintText: "Email Address",
),
),
const SizedBox(
height: 20.0,
),
Row(
children: [
ElevatedButton(
onPressed: () async {
if (id == null) {
await _addContact();
}
if (id != null) {
await _updateContact(id);
}
Navigator.of(context).pop();
_nameController.text = '';
_numberController.text = '';
_emailController.text = '';
},
child: Text(id == null ? 'Create New' : 'Update'),
),
const SizedBox(
width: 10.0,
),
ElevatedButton(onPressed: () async {
_nameController.text = '';
_numberController.text = '';
_emailController.text = '';
}, child: const Text("Clear")),
const SizedBox(
width: 10.0,
),
ElevatedButton(onPressed: (){
Navigator.pop(context);
}, child: const Text("Go Back")),
],
),
]),
));
}
Future<void> _addContact() async {
await Contact.createContact(
_nameController.text, _numberController.text, _emailController.text
);
_refreshContacts();
}
Future<void> _updateContact(int id) async {
await Contact.updateContact(id, _nameController.text, _numberController.text, _emailController.text );
_refreshContacts();
}
void _deleteContact(int id) async {
await Contact.deleteContact(id);
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("Sccessfully Contact Deleted")));
_refreshContacts();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Contact App",),
backgroundColor: Colors.blueAccent,
centerTitle: true,
toolbarHeight: 80,
),
body: _isLoading ? const Center(child: CircularProgressIndicator(),) :
ListView.builder(
itemCount: _contacts.length,
itemBuilder: (context, index) =>
Card(
elevation: 5,
shape: const Border(
right: BorderSide(color: Colors.blue, width: 10.0),
),
color: Colors.orange[200],
margin: const EdgeInsets.all(15.0),
child: Material(
elevation: 20.0,
shadowColor: Colors.blueGrey,
child: ListTile(
title: Text(_contacts[index]['name'], style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(_contacts[index]['number'], style: const TextStyle(color: Colors.grey, fontSize: 18),),
const SizedBox(
height: 5.0,
),
Text(_contacts[index]['email'], style: const TextStyle(fontSize: 17, color: Colors.black),),
],
),
trailing: SizedBox(
width: 100,
child: Row(
children: [
IconButton(onPressed: () => _showForm(_contacts[index]['id']), icon: const Icon(Icons.edit, color: Colors.blueGrey,)),
IconButton(onPressed: () => _deleteContact(_contacts[index]['id']), icon: const Icon(Icons.delete, color: Colors.red,)),
],
),
),
),
),
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add, size: 28,),
onPressed: () => _showForm(null),
),
);
}
}
Above codes are from homepage. I need only the validating part + if anyone can know how to show the each card in another page using page route. Actually this is a contact app. I have tried the new screen to show the full details but couldn't.
You can return when any of the field is empty like
ElevatedButton(
onPressed: () async {
if (_nameController.text.isEmpty ||
_numberController.text.isEmpty ||
_emailController.text.isEmpty) {
return;
}
},
child: Text(id == null ? 'Create New' : 'Update'),
),
But it will be better to use Form widget TextFormFiled with validator . Find more on validation
final _formKey = GlobalKey<FormState>();
showModalBottomSheet(
context: context,
elevation: 5,
isScrollControlled: true,
builder: (_) => Container(
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
controller: _nameController,
decoration: const InputDecoration(
hintText: "Name",
),
),
Row(
children: [
ElevatedButton(
onPressed: () async {
final isValided =
_formKey.currentState?.validate();
if (isValided == true) {}
},
child: Text(id == null ? 'Create New' : 'Update'),
),
EDIT: Tried to delete post but can't because of answer. So I am editing entire post to better describe new issue I'm having as I have now narrowed down the problem.
I have seen other posts that solve this in different situation but I cannot solve my situation with their solutions.
I have a drop down menu for quick access of frequently typed sales persons which takes in any new salesperson name and adds to the list. This is connected with my screen and I can't figure out how to not have the keyboard clear the text field on "done" tapped.
Any help is appreciated!
Screen:
class SmallAddSaleScreen extends StatefulWidget {
#override
_SmallAddSaleScreenState createState() => _SmallAddSaleScreenState();
}
class _SmallAddSaleScreenState extends State<SmallAddSaleScreen> {
final randomGen = Faker.withGenerator(random);
final uuid = Uuid();
final salesPerson = TextEditingController();
final saleAmount = TextEditingController();
final item = TextEditingController();
final dateEdited = DateTime.now().millisecondsSinceEpoch;
int saleDate = DateTime.now().millisecondsSinceEpoch;
String capitolizeFirst(String string) => string[0].toUpperCase() + string.substring(1);
#override
void initState() {
item.text = capitolizeFirst(randomGen.lorem.word());
saleAmount.text = (random.integer(13000) + random.decimal()).toStringAsFixed(2);
super.initState();
}
#override
Widget build(BuildContext context) {
String formattedDate = DateFormat.yMMMMd().format(DateTime.fromMillisecondsSinceEpoch(saleDate));
Future<void> selectSaleDate(BuildContext context) async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: DateTime.fromMillisecondsSinceEpoch(saleDate),
firstDate: DateTime(2015, 8),
lastDate: DateTime(2101),
);
if (picked != null)
setState(() {
saleDate = picked.millisecondsSinceEpoch;
});
}
return Scaffold(
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
children: [
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Sales Person:'),
SizedBox(width: 20),
Container(
width: 350,
child: Consumer(builder: (context, watch, child) {
salesPerson.text = watch(appState).selectedSaleSource!;
return TextField(
controller: salesPerson,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.words,
autocorrect: false,
decoration: InputDecoration(
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.black)),
labelText: 'Name',
),
);
}),
),
]),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Sale Amount:'),
SizedBox(width: 20),
Container(
width: 350,
child: TextField(
controller: saleAmount,
keyboardType: TextInputType.numberWithOptions(decimal: true),
textCapitalization: TextCapitalization.words,
autocorrect: false,
decoration: InputDecoration(
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.black)),
labelText: '\$',
),
),
),
]),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Item:'),
SizedBox(width: 75),
Container(
width: 350,
child: TextField(
controller: item,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.words,
autocorrect: false,
decoration: InputDecoration(
border: OutlineInputBorder(borderSide: BorderSide(color: Colors.black)),
labelText: 'Item',
),
),
),
]),
SizedBox(height: 20),
Container(
height: 50,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text('Sale date: '),
SizedBox(width: 40),
Container(
width: 350,
child: Text(formattedDate),
),
]),
),
],
),
SizedBox(width: 50),
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(height: 40),
Container(
width: 250,
child: SalesSourcePulldownMenuWidget(),
),
SizedBox(height: 100),
Container(
width: 250,
child: TextButton(
style: ButtonStyle(
textStyle: MaterialStateProperty.all(TextStyle(fontWeight: FontWeight.bold)),
foregroundColor: MaterialStateProperty.all(Colors.white),
backgroundColor: MaterialStateProperty.all(Colors.amber[800])),
onPressed: () async {
await selectSaleDate(context);
},
child: const Text('SELECT SALE DATE')),
),
SizedBox(height: 20),
Container(
width: 250,
child: TextButton(
style: ButtonStyle(
textStyle: MaterialStateProperty.all(TextStyle(fontWeight: FontWeight.bold)),
foregroundColor: MaterialStateProperty.all(Colors.white),
backgroundColor: MaterialStateProperty.all(Colors.amber[800])),
child: const Text('SAVE'),
onPressed: () async {
if (salesPerson.text.isNotEmpty && saleAmount.text.isNotEmpty && item.text.isNotEmpty) {
final userID = context.read(appState).authorizedLocalUser!.localID;
final sale = Sale(
salesPerson: salesPerson.text.trim(),
saleAmount: double.parse(saleAmount.text.trim()),
item: item.text.trim(),
saleDate: saleDate,
dateModified: dateEdited,
createdBy: userID,
saleUUID: uuid.v4(),
);
await context.read(cudServices).createSaleData(context, sale);
await context.read(appState).addSaleSource(salesPerson.text.trim());
context.read(appState).setSaleSource('');
salesPerson.text;
context.read(appState).saveSuccess(context);
} else {
context.read(appState).emptyField(context);
}
item.text = capitolizeFirst(randomGen.lorem.word()); // Randomize input
saleAmount.text =
(random.integer(13000) + random.decimal()).toStringAsFixed(2); // Randomize input
setState(() {
saleDate = DateTime.now().millisecondsSinceEpoch; // Reset input
});
},
),
)
],
)
],
),
);
}
}
Drop down widget:
class _SalesSourcePulldownMenuWidgetState extends State<SalesSourcePulldownMenuWidget> {
late String? selectedSaleSource;
bool selected = false;
#override
void initState() {
context.read(appState).getSaleSources();
super.initState();
}
#override
Widget build(BuildContext context) {
late List<String> listItems;
late List<DropdownMenuItem<String>> menuItems;
return Consumer(builder: (context, watch, child) {
listItems = watch(appState).saleSources;
selectedSaleSource = watch(appState).selectedSaleSource;
if (selectedSaleSource == '') {
selected = false;
}
menuItems = listItems
.map((String value) => DropdownMenuItem<String>(
value: value,
child: Text(value),
))
.toList();
return DropdownButton<String>(
value: selected ? selectedSaleSource : null,
isExpanded: true,
hint: const Text('Existing persons'),
items: menuItems,
onTap: () {
selected = true;
},
onChanged: (String? newValue) {
setState(() {
if (newValue != null) {
context.read(appState).setSaleSource(newValue);
selectedSaleSource = newValue;
}
});
},
);
});
}
}
That's normal behavior, assign the value for those variables in initState() method:
class _SmallAddSaleScreenState extends State<SmallAddSaleScreen> {
final randomGen = Faker.withGenerator(random);
final uuid = Uuid();
final salesPerson = TextEditingController();
final saleAmount = TextEditingController();
final item = TextEditingController();
final dateEdited = DateTime.now().millisecondsSinceEpoch;
int saleDate = DateTime.now().millisecondsSinceEpoch;
String capitolizeFirst(String _s) => _s[0].toUpperCase() + _s.substring(1);
String formattedDate(DateTime _d) = DateFormat.yMMMMd().format(DateTime.fromMillisecondsSinceEpoch(_d));
#override
void initState() {
item.text = capitolizeFirst(randomGen.lorem.word());
saleAmount.text = (random.integer(13000) + random.decimal()).toStringAsFixed(2);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Column(
...
I am new to coding.I am using upi_pay package in my project to make UPI payments getting error as "Type mismatch: inferred type is String? but String was expected" when I tried the build the app
I followed this article https://dev.to/dsc_ciet/adding-upi-payment-gateway-in-flutter-376c
I am new to coding, don't mind if this was a easy thing.
Please go through the below code
Thanks in advance
class PaymentScreen extends StatefulWidget {
#override
_PaymentScreenState createState() => _PaymentScreenState();
}
class _PaymentScreenState extends State<PaymentScreen> {
String _upiAddrError;
final _upiAddressController = TextEditingController();
final _amountController = TextEditingController();
bool _isUpiEditable = false;
Future<List<ApplicationMeta>> _appsFuture;
#override
void initState() {
_amountController.text =
(Random.secure().nextDouble() * 10).toStringAsFixed(2);
_appsFuture = UpiPay.getInstalledUpiApplications();
super.initState();
}
void _generateAmount() {
setState(() {
_amountController.text =
(Random.secure().nextDouble() * 10).toStringAsFixed(2);
});
}
Future<void> _onTap(ApplicationMeta app) async {
final err = _validateUpiAddress(_upiAddressController.text);
if (err != null) {
setState(() {
_upiAddrError = err;
});
return;
}
setState(() {
_upiAddrError = null;
});
final transactionRef = Random.secure().nextInt(1 << 32).toString();
print("Starting transaction with is $transactionRef");
final a = await UpiPay.initiateTransaction(
amount: _amountController.text,
app: app.upiApplication,
receiverName: "Sharad",
receiverUpiAddress: _upiAddressController.text,
transactionRef: transactionRef,
merchantCode: '7372',
);
print(a);
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: EdgeInsets.symmetric(horizontal: 16),
child: ListView(
children: <Widget>[
Container(
margin: EdgeInsets.only(top: 32),
child: Row(
children: <Widget>[
Expanded(
child: TextFormField(
controller: _upiAddressController,
enabled: _isUpiEditable,
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'address#upi',
labelText: 'Receiving UPI Address',
),
),
),
Container(
margin: EdgeInsets.only(left: 8),
child: IconButton(
icon: Icon(
_isUpiEditable ? Icons.check : Icons.edit,
),
onPressed: () {
setState(() {
_isUpiEditable = !_isUpiEditable;
});
},
),
),
],
),
),
if (_upiAddrError != null)
Container(
margin: EdgeInsets.only(top: 4, left: 12),
child: Text(
_upiAddrError,
style: TextStyle(color: Colors.red),
),
),
Container(
margin: EdgeInsets.only(top: 32),
child: Row(
children: <Widget>[
Expanded(
child: TextField(
controller: _amountController,
readOnly: true,
enabled: false,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Amount',
),
),
),
Container(
margin: EdgeInsets.only(left: 8),
child: IconButton(
icon: Icon(Icons.loop),
onPressed: _generateAmount,
),
),
],
),
),
Container(
margin: EdgeInsets.only(top: 128, bottom: 32),
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
margin: EdgeInsets.only(bottom: 12),
child: Text(
'Pay Using',
style: Theme.of(context).textTheme.caption,
),
),
FutureBuilder<List<ApplicationMeta>>(
future: _appsFuture,
builder: (context, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Container();
}
return GridView.count(
crossAxisCount: 2,
shrinkWrap: true,
mainAxisSpacing: 8,
crossAxisSpacing: 8,
childAspectRatio: 1.6,
physics: NeverScrollableScrollPhysics(),
children: snapshot.data
.map((it) => Material(
key: ObjectKey(it.upiApplication),
color: Colors.grey[200],
child: InkWell(
onTap: () => _onTap(it),
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment:
MainAxisAlignment.center,
children: <Widget>[
Image.memory(
it.icon,
width: 64,
height: 64,
),
Container(
margin: EdgeInsets.only(top: 4),
child: Text(
it.upiApplication.getAppName(),
),
),
],
),
),
))
.toList(),
);
},
),
],
),
)
],
),
),
),
);
}
}
String _validateUpiAddress(String value) {
if (value.isEmpty) {
return 'UPI Address is required.';
}
if (!UpiPay.checkIfUpiAddressIsValid(value)) {
return 'UPI Address is invalid.';
}
return null;
}
You're assigning null to _upiAddrError but it's a non-nullable String.
Declare that variable as String? _upiAddrError instead of String _upiAddrError to make it nullable.
This is the code for the Createpoll module of my Polling app. I want to generate just 4 dynamic text fields, but the below code generates unlimited text fields. I'm not able to figure out which part to edit to fit my needs.
Create Poll Screenshot
I'm also unable to make changes to the hint text in the text field, It keeps repeating "Option 1", I want it to go like Option1, Option2.....so on.
import 'package:flutter/material.dart';
import 'package:justpoll/Constants.dart';
import 'package:justpoll/screens/create_poll/create_poll2.dart';
import 'package:justpoll/widgets/custom_input.dart';
class CreatePoll extends StatefulWidget {
#override
_CreatePollState createState() => _CreatePollState();
}
class _CreatePollState extends State<CreatePoll> {
final _formKey = GlobalKey<FormState>();
TextEditingController _nameController;
static List<String> friendsList = [null];
String emoji_id;
List<String> emoji = [
"❤️",
"🤩",
"✌️",
"😂",
"😡",
];
#override
void initState() {
super.initState();
_nameController = TextEditingController();
}
#override
void dispose() {
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
backgroundColor: MyColors.white,
appBar: AppBar(
title: Padding(
padding: const EdgeInsets.all(75.0),
child: Text('New Poll'),
),
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(
Icons.close,
),
),
backgroundColor: Colors.black87,
),
body: ListView(
children: [
Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// name textfield
Center(
child: Padding(
padding: const EdgeInsets.all(6.0),
child: Text("1/4"),
),
),
Padding(
padding: const EdgeInsets.only(right: 32.0),
child: CustomInput(
textEditingController: _nameController,
labletext: 'Question?*',
decoration: InputDecoration(hintText: 'Question*'),
validator: (v) {
if (v.trim().isEmpty) return 'Please enter something';
return null;
},
),
),
SizedBox(
height: 20,
),
// Text(
// 'Options',
// style: TextStyle(fontWeight: FontWeight.w700, fontSize: 16),
// ),
// Padding(
// padding: const EdgeInsets.only(right: 2.0),
// child: Row(
// children: [
// Expanded(
// flex: 2,
// child: Padding(
// padding: const EdgeInsets.only(right: 20),
// child: CustomInput(
// textEditingController: _nameController,
// labletext: 'Option 1*',
// decoration: InputDecoration(hintText: 'Option'),
// validator: (v) {
// if (v.trim().isEmpty)
// return 'Please enter something';
// return null;
// },
// ),
// ),
// ),
// Expanded(
// flex: 1,
// child: Padding(
// padding: const EdgeInsets.only(right: 30),
// child: Container(
// padding: EdgeInsets.only(left: 10, right: 16),
// decoration: BoxDecoration(
// border: Border.all(
// color: MyColors.black, width: 1.5),
// borderRadius: BorderRadius.circular(10)),
// child: DropdownButton(
// hint: Text('❤️'),
// value: emoji_id,
// icon: Icon(Icons.arrow_drop_down),
// iconSize: 36,
// isExpanded: true,
// underline: SizedBox(),
// style: TextType.regularDarkText,
// onChanged: (newValue) {
// setState(() {
// emoji_id = newValue;
// });
// },
// items: emoji.map((emoji_id) {
// return DropdownMenuItem(
// value: emoji_id,
// child: Text(emoji_id),
// );
// }).toList(),
// ),
// ),
// ),
// ),
// ],
// ),
// ),
..._getOptions(),
SizedBox(
height: 30,
),
Align(
alignment: Alignment.bottomRight,
child: MaterialButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => CreatePoll2(),
),
);
},
color: Colors.black,
textColor: Colors.white,
child: Icon(
Icons.arrow_forward,
size: 24,
),
padding: EdgeInsets.all(16),
shape: CircleBorder(),
),
),
],
),
),
),
],
),
),
);
}
/// get friends text-fields
List<Widget> _getOptions() {
List<Widget> friendsTextFields = [];
for (int i = 0; i < friendsList.length; i++) {
friendsTextFields.add(Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
children: [
Expanded(child: FriendTextFields(i)),
SizedBox(
width: 5,
),
// we need add button at last friends row
_addRemoveButton(i == friendsList.length - 1, i),
],
),
));
}
return friendsTextFields;
}
/// add / remove button
Widget _addRemoveButton(bool add, int index) {
return InkWell(
onTap: () {
if (add) {
// add new text-fields at the top of all friends textfields
friendsList.insert(0, null);
} else
friendsList.removeAt(index);
setState(() {});
},
child: Container(
width: 26,
height: 26,
decoration: BoxDecoration(
color: (add) ? Colors.black : Colors.red,
borderRadius: BorderRadius.circular(20),
),
child: Icon(
(add) ? Icons.add : Icons.remove,
color: Colors.white,
),
),
);
}
}
class FriendTextFields extends StatefulWidget {
final int index;
FriendTextFields(this.index);
#override
_FriendTextFieldsState createState() => _FriendTextFieldsState();
}
class _FriendTextFieldsState extends State<FriendTextFields> {
TextEditingController _nameController;
#override
void initState() {
super.initState();
_nameController = TextEditingController();
}
#override
void dispose() {
_nameController.dispose();
super.dispose();
}
String emoji_id;
List<String> emoji = [
"❤️",
"🤩",
"✌️",
"😂",
"😡",
];
#override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_nameController.text = _CreatePollState.friendsList[widget.index] ?? '';
});
return Row(
children: [
Expanded(
flex: 2,
child: CustomInput(
textEditingController: _nameController,
labletext: 'Option 1*',
decoration: InputDecoration(hintText: 'Option'),
validator: (v) {
if (v.trim().isEmpty) return 'Please enter something';
return null;
},
),
),
Expanded(
flex: 1,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
padding: EdgeInsets.only(left: 10, right: 16),
decoration: BoxDecoration(
border: Border.all(color: MyColors.black, width: 1.5),
borderRadius: BorderRadius.circular(10)),
child: DropdownButton(
hint: Text('❤️'),
value: emoji_id,
icon: Icon(Icons.arrow_drop_down),
iconSize: 36,
isExpanded: true,
underline: SizedBox(),
style: TextType.regularDarkText,
onChanged: (newValue) {
setState(() {
emoji_id = newValue;
});
},
items: emoji.map((emoji_id) {
return DropdownMenuItem(
value: emoji_id,
child: Text(emoji_id),
);
}).toList(),
),
),
),
),
],
);
}
}
Firstly, If you want to limit it to just 4 dynamic fields then please replace this line
_addRemoveButton(i == friendsList.length - 1, i)
with this
_addRemoveButton(i < 3 ? i == friendsList.length - 1 : false, i),
This will make sure that the add button is not shown on the 4th field. Instead it will show the remove button for the 4th field.
Secondly, to have it show each field as Option 1, Option 2, Option 3 etc, replace the line
labletext: 'Option 1*',
with
labletext: 'Option ${widget.index + 1}',