Flutter Search bar not displaying all the users - flutter

I have been implementing a search bar in Flutter, which works as expected when searching according to usernames, but does not display all the users when it is empty.
It only displays all the users when typing something in the search bar and completely backspacing it. The following code snippet shows its implementation:
class UsersTable extends StatefulWidget {
List<User> users;
List<Library> posts;
UsersTable(
this.users,
this.posts, {
Key? key,
}) : super(key: key);
#override
State<UsersTable> createState() => _UsersTableState();
}
class _UsersTableState extends State<UsersTable> {
List<User> _foundUsers = [];
#override
initState() {
// at the beginning, all users are shown
Future.delayed(Duration.zero, () async {
_foundUsers = widget.users;
print(_foundUsers.length);
if (mounted) setState(() {});
});
super.initState();
}
// This function is called whenever the text field changes
void _runFilter(String enteredKeyword) {
List<User> results = [];
if (enteredKeyword.trim().isEmpty) {
// if the search field is empty or only contains white-space, we'll display all users
results = widget.users;
print("if filter length");
print(results.length);
} else {
results = widget.users
.where((user) => user.username
.toLowerCase()
.contains(enteredKeyword.toLowerCase()))
.toList();
// we use the toLowerCase() method to make it case-insensitive
}
// Refresh the UI
setState(() {
_foundUsers = results;
});
}
Widget body(BuildContext context, List<User> users, List<Library> posts) {
return SafeArea(
child: Material(
child: Container(
padding: const EdgeInsets.all(defaultPadding),
decoration: const BoxDecoration(
color: boxColor,
borderRadius: BorderRadius.all(Radius.circular(10)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Align(
alignment: Alignment.center,
child: Text(
"Active Users",
//style: Theme.of(context).textTheme.subtitle1,
style: TextStyle(
color: Colors.white,
fontSize: 20,
decoration: TextDecoration.none),
)),
const SizedBox(
height: 30,
),
TextField(
style: const TextStyle(color: Colors.white),
onChanged: (value) => _runFilter(value),
decoration: const InputDecoration(
labelStyle: TextStyle(color: Colors.white38),
labelText: 'Search',
suffixIcon: Icon(Icons.search),
suffixIconColor: Colors.white),
),
const SizedBox(
height: 20,
),
// SizedBox(
//width: double.infinity,
// child: SingleChildScrollView(
Container(
child: DataTable(
columnSpacing: 7,
columns: const [
// DataColumn(
// label: Text(
// "First Name",
// style: TextStyle(color: Colors.white),
// ),
// ),
// DataColumn(
// label: Text(
// "Last Name",
// style: TextStyle(color: Colors.white),
// ),
// ),
DataColumn(
label: Text(
"Username",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
),
DataColumn(
label: Text(
"Email ID",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
),
// DataColumn(
// label: Text(
// "DOB",
// style: TextStyle(color: Colors.white),
// ),
// ),
DataColumn(
label: Text(
"Account\nType",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
),
DataColumn(
label: Text(
"Delete\nUser",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
),
DataColumn(
label: Text(
"View\nPosts",
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white),
),
),
],
rows:
//_foundUsers.isNotEmpty?
_foundUsers
.map(
(user) => DataRow(
cells: <DataCell>[
// DataCell(Text(user.first_name,
// style: TextStyle(color: Colors.white))),
// DataCell(Text(user.last_name,
// style: TextStyle(color: Colors.white))),
DataCell(Text(user.username,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white))),
DataCell(Text(user.email,
style: TextStyle(color: Colors.white))),
// DataCell(Text(user.date_of_birth,
// style: TextStyle(color: Colors.white))),
DataCell(Text(user.account_type,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.white))),
DataCell(
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.purple,
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 15),
textStyle: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold),
shadowColor: Colors.grey,
),
onPressed: () {
// if (user.username
// .compareTo('hwumazijadmin48') !=
// 0) {
BlocProvider.of<AuthenticationBloc>(context)
.add(DeleteUser(
username: user.username));
// } else {
// const Text("Cannot Delete Admin");
// }
},
child: const Text('X'),
),
),
DataCell(
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.purple,
padding: const EdgeInsets.symmetric(
horizontal: 15, vertical: 15),
textStyle: const TextStyle(
fontSize: 12,
fontWeight: FontWeight.bold),
shadowColor: Colors.grey,
),
onPressed: () {
List<Library> po = [];
int i = 0;
while (i < posts.length) {
if (posts[i]
.user
.compareTo(user.username) ==
0) {
po.add(posts[i]);
}
i++;
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PostsTable(po),
),
);
},
child: const Text('\u{1F441}'),
),
),
],
),
)
.toList()
//: []
),
),
// ),
],
),
)
//)
));
}
Am I missing something in the code?

move super.initState(); above and remove Future.delayed.
#override
initState() {
super.initState();
_foundUsers = widget.users;
}
no need to setState({}); because build method will trigger after initState. Also no need for mounted because we removed asyncoperation.

Related

How do I make this text field editable and not editable with a button. Dart/Flutter

how do I make the "title" text field editable when the edit button is pressed.
preferably the edit button should be at the right hand side of the app bar next to the done button.
Here is my code, ive removed most of the code for the other fields to save character limit for this post but kept the code for the "title" text field.
Thank You!
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class viewLossEvent extends StatefulWidget {
String code,
title,
orgUnit,
riskOfficer,
natureOfEvent,
islamicBusiness,
shariahRelated,
incidentType,
operationalRiskLossType,
countryOfEvent,
stateOfEvent,
districtOfEvent,
modusOperandi,
factOfIncident,
//Status Approval and Validation
status,
incidentOwner,
rejectionComment,
ormdRec,
reasonNotValid,
//Incident Categorization
busline1,
busline2,
busline3,
proServ,
delChannel,
causCat1,
causCat2,
causCat3,
causCatOth,
eventcat1,
eventcat2,
eventcat3,
boundOthRisk,
payInst,
cardBrand,
payChan,
cardType,
busAct,
accType,
depRisk,
riskAss,
riskRate,
//Fraud Related Information
possfraud,
iADRD,
iADF,
iADC,
//Incident Dates
discDate,
discTime,
occDate,
occTime,
resDate,
resTime,
subBNMDate,
dateIncRep,
recDate,
closeDate,
//Incident Loss Data
grossLoss,
recoveryAmmount,
netLoss,
recoveryEC,
contingentLia;
viewLossEvent(
{this.code,
this.title,
this.orgUnit,
this.riskOfficer,
this.natureOfEvent,
this.islamicBusiness,
this.shariahRelated,
this.incidentType,
this.operationalRiskLossType,
this.countryOfEvent,
this.stateOfEvent,
this.districtOfEvent,
this.modusOperandi,
this.factOfIncident,
this.status,
this.incidentOwner,
this.rejectionComment,
this.ormdRec,
this.reasonNotValid,
this.busline1,
this.busline2,
this.busline3,
this.proServ,
this.delChannel,
this.causCat1,
this.causCat2,
this.causCat3,
this.causCatOth,
this.eventcat1,
this.eventcat2,
this.eventcat3,
this.boundOthRisk,
this.payInst,
this.cardBrand,
this.payChan,
this.cardType,
this.busAct,
this.accType,
this.depRisk,
this.riskAss,
this.riskRate,
this.possfraud,
this.iADRD,
this.iADF,
this.iADC,
this.discDate,
this.discTime,
this.occDate,
this.occTime,
this.resDate,
this.resTime,
this.subBNMDate,
this.dateIncRep,
this.recDate,
this.closeDate,
this.grossLoss,
this.recoveryAmmount,
this.netLoss,
this.recoveryEC,
this.contingentLia});
#override
State<viewLossEvent> createState() => _viewLossEventState();
}
class _viewLossEventState extends State<viewLossEvent> {
int _selectedIndex = 0;
void _navigateBottomBar(int index){
setState(() {
_selectedIndex = index;
});
}
bool isReadOnly = true;
void _edit(){
setState(() {
isReadOnly = !isReadOnly;
});
}
List<Widget> _pages = [];
void initState() {
super.initState();
_pages = [
Container(
child: SingleChildScrollView(
child: Column(
children: [
//Main Info
ListTileTheme(
tileColor: Colors.grey,
child: ExpansionTile(
title: Text(
'Main Info',
style: GoogleFonts.poppins(textStyle: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 20,))
),
children: [
//Fields
ListTileTheme(
tileColor: Colors.white,
child: ListTile(
title: Text('Code : ${widget.code}',
style: GoogleFonts.poppins(textStyle: const TextStyle(
fontWeight: FontWeight.normal,
fontSize: 20,
),),),
),
),
ListTileTheme(
tileColor: Colors.white,
child: ListTile(
leading: Text(
'Title : ',
style: TextStyle(
fontSize: 20.0,
color: Colors.black,
fontWeight: FontWeight.bold
),
),
title: TextField(
readOnly: isReadOnly,
decoration: InputDecoration(
hintStyle: TextStyle(fontSize: 20.0, color: Colors.black),
hintText: '${widget.title}',
border: InputBorder.none,
),
),
),
),
],
),
),
),
Container(
child: SingleChildScrollView(
child: Column(
children: [
//Shariah Details
ListTileTheme(
tileColor: Colors.grey,
child: ExpansionTile(
title: Text(
'LE Shariah',
style: GoogleFonts.poppins(textStyle: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 20,))
),
children: [
//Fields
],),
),
],
),
),
),
Container(
child: SingleChildScrollView(
child: Column(
children: [
//Shariah Details
ListTileTheme(
tileColor: Colors.grey,
child: ExpansionTile(
title: Text(
'LE Attachment',
style: GoogleFonts.poppins(textStyle: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 20,))
),
children: [
//Fields
],),
),
],
),
),
),
Container(
child: SingleChildScrollView(
child: Column(
children: [
//Shariah Details
ListTileTheme(
tileColor: Colors.grey,
child: ExpansionTile(
title: Text(
'LE Action',
style: GoogleFonts.poppins(textStyle: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 20,))
),
children: [
//Fields
],),
),
],
),
),
),
Container(
child: SingleChildScrollView(
child: Column(
children: [
//Shariah Details
ListTileTheme(
tileColor: Colors.grey,
child: ExpansionTile(
title: Text(
'Compliance Incident',
style: GoogleFonts.poppins(textStyle: const TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
fontSize: 20,))
),
children: [
//Fields
],),
),
],
),
),
),
];
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: const Color(0xFF002d62),
title: Text('${widget.code}',
style: GoogleFonts.sora(
textStyle: const TextStyle(
fontSize: 25,
fontWeight: FontWeight.w500,
),
)),
actions: <Widget>[
IconButton(
icon: const Icon(Icons.edit),
onPressed: _edit,
),
],
),
body: _pages[_selectedIndex],
bottomNavigationBar: BottomNavigationBar(
onTap: _navigateBottomBar,
currentIndex: _selectedIndex,
type: BottomNavigationBarType.fixed,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'General'),
BottomNavigationBarItem(icon: Icon(Icons.balance), label: 'LE Shariah'),
BottomNavigationBarItem(icon: Icon(Icons.attachment), label: 'LE Attachment'),
BottomNavigationBarItem(icon: Icon(Icons.call_to_action), label: 'LE Action'),
BottomNavigationBarItem(icon: Icon(Icons.person_pin_circle), label: 'Compliance Incident'),
]),
);
}
}
instaed of using enabled:
use readOnly: ,
bool isReadOnly = true;
this is how your textfield looks
TextField(
readOnly: isReadOnly,
decoration: InputDecoration(
hintStyle: TextStyle(fontSize: 20.0, color: Colors.black),
hintText: '${widget.title}',
border: InputBorder.none,
),
on your edit button
onTap:(){
setState(() {
isReadOnly = !isReadOnly;
});
}
You can set
enabled: isEditable in TextField
When You click on the Edit button set the value of isEditable
setState(() {
isEditable=!isEditable;
});

Flutter PIN Verification Countdown

I made a pin verification with countdown using the timer_count_down package. What I'm trying to do is when the countdown is finished, it shows a text saying "Resend PIN" to resend the PIN to email and starts the countdown again.
Here is my Login.dart code snippet when I enter my email address it sends a PIN code to my email :
...
Form(
key: _formKey,
child: TextFormField(
cursorColor: Color(0xFF2481CF),
autofocus: true,
validator: (value) {
if (value == null || value.isEmpty || !EmailValidator.validate(value.trim())) {
return 'Email is not valid!';
}
return null;
},
controller: _controller,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color(0xFF2481CF)),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Color(0xFF2481CF)),
),
labelText: 'Enter your email :',
labelStyle: TextStyle(
color: Color(0xFF2481CF)
)
),
style: TextStyle(
fontSize: 19.0,
height: 1.3,
),
),
),
...
floatingActionButton: FloatingActionButton(
// onPressed: _submit,
onPressed: () {
if (_formKey.currentState!.validate()) {
showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(height: 20,),
Text('PIN code will be sent to'),
SizedBox(height: 10,),
Text(_controller.text,
style: TextStyle(
fontWeight: FontWeight.bold
),),
SizedBox(
height: 30,
),
Text('Is this OK or would you like to edit the email address?'),
],
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'Cancel'),
child: const Text('Edit',
style: TextStyle(
color: Color(0xFF2481CF)
),),
),
TextButton(
onPressed: () {
//kirim email
sendEmail(_controller.text.trim());
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => PinVerification(_controller.text.trim())),
);
},
child: const Text('Verify',
style: TextStyle(
color: Color(0xFF2481CF)
),),
),
],
),
);
};
},
child: const Icon(Icons.send),
),
And for the PinVerification.dart :
Widget onlySelectedBorderPinPut() {
final BoxDecoration pinPutDecoration = BoxDecoration(
color: const Color.fromRGBO(235, 236, 237, 1),
borderRadius: BorderRadius.circular(5.0),
);
return Form(
key: _formKey,
child: Column(
children: [
SizedBox(
height: 50,
),
Text(
'Enter 6 digit code we sent to your email.',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey),
),
SizedBox(
height: 30,
),
GestureDetector(
onLongPress: () {
print(_formKey.currentState?.validate());
},
child: PinPut(
validator: (s) {
if (s != null && s.contains('1')) return null;
return 'NOT VALID';
},
useNativeKeyboard: true,
autovalidateMode: AutovalidateMode.always,
withCursor: true,
fieldsCount: 6,
fieldsAlignment: MainAxisAlignment.spaceAround,
textStyle: const TextStyle(fontSize: 25.0, color: Colors.black),
eachFieldMargin: EdgeInsets.all(0),
eachFieldWidth: 45.0,
eachFieldHeight: 55.0,
onSubmit: (String pin) => postRequest(pin),
focusNode: _pinPutFocusNode,
controller: _pinPutController,
submittedFieldDecoration: pinPutDecoration,
selectedFieldDecoration: pinPutDecoration.copyWith(
color: Colors.white,
border: Border.all(
width: 2,
color: const Color.fromRGBO(160, 215, 220, 1),
),
),
followingFieldDecoration: pinPutDecoration,
pinAnimationType: PinAnimationType.scale,
),
),
SizedBox(
height: 20,
),
Countdown(
seconds: 20,
build: (_, double time) =>
RichText(
text: TextSpan(
text: 'Send PIN again in ',
style: TextStyle(
color: Theme.of(context).inputDecorationTheme.labelStyle?.color
),
children: <TextSpan>[
TextSpan(
text: time.toString(),
style: TextStyle(
color: Color(0xFF2481CF),
fontWeight: FontWeight.bold
)),
TextSpan(text: ' sec',
style: TextStyle(
color: Theme.of(context).inputDecorationTheme.labelStyle?.color
))
]
),
),
onFinished: () {
},
),
],
),
);
}
This is the screenshot for my PinVerification.dart:
Any solutions?
Created custom timer It worked for me
late Timer _timer;
int _start = 60;
void startTimer() {
const oneSec = const Duration(seconds: 1);
_timer = new Timer.periodic(
oneSec,
(Timer timer) {
if (_start == 0) {
setState(() {
timer.cancel();
});
} else {
setState(() {
_start--;
});
}
},
);
}
#override
void initState() {
super.initState();
startTimer();
}
#override
void dispose() {
super.dispose();
_timer.cancel();
errorController!.close();
}
And inside widget check condition as follows:
_start != 0
? Row(
// mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
"Resend Code in",
style: TextStyle(
color: Colors.white.withOpacity(0.5),
fontWeight: FontWeight.bold,
fontSize: 16),
),
const SizedBox(width: 10),
Text(
_start.toString(),
style: TextStyle(
color: MyTheme.yellow,
fontWeight: FontWeight.bold,
fontSize: 20),
),
],
)
: YourWidget() // your widget here
When the Resend Otp is clikced reset the timer is as follows:
setState(() {
_start = 60;
startTimer();
});

Flutter Saving button state on device

I have a search screen where users can search for a county and a list of vaccination posts in said county is shown. Some counties have many some have very few.
In the list of vaccination posts, there is a button that when pressed, the button is disabled. All is good until here, but when I re-search the same county, the button that was once disabled is no longer disabled, meaning it can be pressed again. I would like to avoid that.
I have a list of bools, "true" for each vaccination post. When button is pressed, respective index in the list is changed to "false" thus disabling the button.
Question: How can I keep the button disable? (Whether the same county is re-searched or the app is re-opened)
import 'package:colour/colour.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
// import 'package:shared_preferences/shared_preferences.dart';
class VaccineCenterList extends StatefulWidget {
const VaccineCenterList({key}) : super(key: key);
static const String idScreen = "VaccineCenterList";
#override
_VaccineCenterListState createState() => _VaccineCenterListState();
}
class _VaccineCenterListState extends State<VaccineCenterList> {
TextEditingController hospitalCountyEditingController =
TextEditingController();
final databaseReference = FirebaseDatabase.instance.reference();
final firestoreInstance = FirebaseFirestore.instance;
FirebaseAuth auth = FirebaseAuth.instance;
List<Hospitals> driverList = [];
List<bool> buttonPressed = [];
// Map<String, List> buttonPressed = {};
buttonControls(numOfLocations) {
print(numOfLocations);
int checker = 0;
if (numOfLocations > buttonPressed.length) {
try {
while (numOfLocations >= checker) {
buttonPressed.add(true);
checker = checker + 1;
}
} catch (e) {
print("$e");
}
} else {}
print(buttonPressed);
}
#override
void initState() {
super.initState();
print("I'm in the vaccine list search screen");
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
backgroundColor: Colors.grey[100],
centerTitle: true,
elevation: 0,
iconTheme: const IconThemeData(
color: Colors.black,
),
title: Text(
"Pamoja",
style: GoogleFonts.lexendMega(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 35,
),
),
),
body: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: SingleChildScrollView(
child: Column(
children: [
Container(
// textfield
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: hospitalCountyEditingController,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.words,
decoration: InputDecoration(
border: const OutlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
style: BorderStyle.solid,
width: 1,
),
),
labelText: "Search County",
labelStyle: const TextStyle(
fontSize: 14.0,
color: Colors.blueGrey,
),
hintStyle: GoogleFonts.lexendMega(
color: Colors.grey,
fontSize: 10,
),
),
style: GoogleFonts.lexendMega(
fontSize: 14,
color: Colors.black,
),
),
),
ElevatedButton(
// search button
onPressed: () {
setState(() {
FocusScope.of(context).requestFocus(
FocusNode(),
);
driverList.clear();
if (hospitalCountyEditingController.text == "" ||
hospitalCountyEditingController.text == " ") {
isTrueOrFalse = "False";
} else {
isTrueOrFalse = "True";
}
});
getHospitalDetails();
},
child: Text(
"Search",
style: GoogleFonts.lexendMega(
fontWeight: FontWeight.bold,
),
),
),
SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: MediaQuery.of(context).size.height,
// height: 50,
// color: Colors.black,
child: Padding(
padding: const EdgeInsets.only(
right: 12.0,
left: 12.0,
bottom: 12.0,
),
child: driverList.isEmpty
? Center(
// child: CircularProgressIndicator(),
child: (isTrueOrFalse == "True")
? const CircularProgressIndicator()
: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Search for one of the counties below",
style:
GoogleFonts.lexendMega()),
),
Text(
"Nairobi, Baringo, Busia, Bomet, Bungoma, Elgeyo Marakwet, Embu, Garissa, Homa Bay, Isiolo, Kajiado, Kakamega, Kericho, Kiambu, Kilifi, Kirinyaga, Kisii, Kisumu, Kitui, Kwale, Laikipia, Lamu, Machakos, Makueni, Mandera, Marsabit, Meru, Migori, Mombasa, Muranga, Nakuru, Nandi, Narok, Nyamira, Nyandarua, Nyeri, Samburu, Siaya County, Taita Taveta, Tana River County, Tharaka Nithi, Trans Nzoia, Turkana, Uasin Gishu, Vihiga, Wajir, West Pokot",
textAlign: TextAlign.center,
style: GoogleFonts.lexendMega(
fontWeight: FontWeight.bold,
),
),
],
),
)
: ListView.builder(
itemCount: driverList.length,
itemBuilder: (context, index) {
final Hospitals hospitals = driverList[index];
final String hospitalLocaiton =
hospitals.location;
final String hospitalPhone = hospitals.phone;
final String hospitalName = hospitals.name;
positiveCount = hospitals.positiveCount;
negativeCount = hospitals.negativeCount;
final int setpercentage =
calculatePositivePercentage(
positiveCount, negativeCount);
buttonControls(driverList.length);
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 0,
child: ExpansionTile(
title: Text(
hospitalName.toUpperCase(),
style: GoogleFonts.lexendMega(),
textAlign: TextAlign.center,
),
children: [
Column(
children: [
Container(
child: (hospitalPhone
.isNotEmpty)
? ElevatedButton(
onPressed: () {
Clipboard.setData(
ClipboardData(
text:
hospitalPhone,
),
);
ScaffoldMessenger.of(
context)
.showSnackBar(
const SnackBar(
backgroundColor:
Colors.green,
content: Text(
"Number Copied",
textAlign:
TextAlign
.center,
),
),
);
},
child: Text(
hospitalPhone,
textAlign:
TextAlign.center,
style: GoogleFonts
.lexendMega(
fontSize: 13),
),
style: ElevatedButton
.styleFrom(
elevation: 0,
),
)
: const Text(""),
),
const SizedBox(
height: 5,
),
hospitalLocaiton.isNotEmpty
? Container(
padding:
const EdgeInsets.all(
8),
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
),
borderRadius:
BorderRadius
.circular(10),
),
child: Text(
hospitalLocaiton,
textAlign:
TextAlign.center,
style: GoogleFonts
.lexendMega(
fontSize: 12,
),
),
)
: Text(
hospitalLocaiton,
textAlign:
TextAlign.center,
style: GoogleFonts
.lexendMega(
fontSize: 12,
),
),
const SizedBox(
height: 10,
),
Text(
"$setpercentage% (percent) of voters say this hospital administer vaccines.",
textAlign: TextAlign.center,
style: GoogleFonts.lexendMega(
fontWeight: FontWeight.bold,
fontSize: 12,
color: Colors.deepPurple[400],
),
),
const SizedBox(
height: 10,
),
const Divider(
// thickness: 1,
indent: 20,
endIndent: 20,
color: Colors.black87,
),
const SizedBox(
height: 10,
),
Text(
"Does this Hospital administer Vaccines?\n(To help the public, please vote only if you know.)",
textAlign: TextAlign.center,
style: GoogleFonts.lexendMega(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceEvenly,
children: [
ElevatedButton(
onPressed:
buttonPressed[index]
? () {
positiveIncrement(
hospitalName,
index);
buttonPressed[
index] =
false;
ScaffoldMessenger.of(
context)
.showSnackBar(
const SnackBar(
backgroundColor:
Colors.redAccent,
content:
Text(
"Voted",
textAlign:
TextAlign.center,
),
),
);
}
: null,
child: Text(
"Yes",
style: GoogleFonts
.lexendMega(),
),
style: ElevatedButton
.styleFrom(
primary:
Colour("#87D68D"),
),
),
ElevatedButton(
onPressed:
buttonPressed[index]
? () {
negativeIncrement(
hospitalName,
index);
buttonPressed[
index] =
false;
ScaffoldMessenger.of(
context)
.showSnackBar(
const SnackBar(
backgroundColor:
Colors.redAccent,
content:
Text(
"Voted",
textAlign:
TextAlign.center,
),
),
);
}
: null,
child: Text(
"No",
style: GoogleFonts
.lexendMega(),
),
style: ElevatedButton
.styleFrom(
primary:
Colour("#E3655B"),
),
),
],
),
const SizedBox(
height: 10,
),
Text(
"1 - To vote, tap once\n2 - To Undo vote, tap and hold",
style:
GoogleFonts.lexendMega(
fontSize: 12,
),
textAlign: TextAlign.start,
),
const SizedBox(
height: 10,
),
],
),
],
),
],
),
),
);
},
),
),
),
// SizedBox(height: 40),
],
),
),
],
),
),
),
);
}
}
class Hospitals {
final String name;
final String phone;
final String location;
// final String county;
final int positiveCount;
final int negativeCount;
// final int intPercentage;
// final int totalVotes;
Hospitals({
required this.name,
required this.phone,
required this.location,
// required this.county,
required this.positiveCount,
required this.negativeCount,
// required this.intPercentage,
// required this.totalVotes,
});
static Hospitals fromJson(Map<String, dynamic> json) {
return Hospitals(
name: json['HospitalName'],
phone: json['HospitalPhone'],
// county: json['county'],
location: json['HospitalAddres'],
positiveCount: json['poitiveCount'],
negativeCount: json['negativeCount'],
// intPercentage: json['intPercentage'],
// totalVotes: json['totalVotes']
);
}
}
I did that this way: use the provider to set value, make boolean value
late bool isMyButtonEnabled;
//in initState
setState(() {
isMyButtonEnabled=
myProvider.checkIfButtonShouldBeEnabled(widget.Id);
}
//in build
ElevatedButton(
child: Text('SUBMIT'),
onPressed: !isMyButtonEnabled
? null
: () async => {
//some code
}
this way your boolean will be saved and available everywhere in the app using the Provider package (there are other even more advanced analogs of Provider there that can be used as well)

Flutter project giving Out of Memory error

Currently building my first app. Getting data from firebase. Purpose is to hopefully get more people to get the COVID vaccine in Kenya.
Explanation of the code below: A user will use the search function to search for a specific county, then all vaccination posts in that county will show up with the relevant data. There are also 2 buttons, yes and no. Users can vote to let other users know if a vaccination post is still administering vaccines or not. Also, when button is pressed, they should get disabled.
I have a list that buttonPressed that when called, x number of "true" is added depending on the number of vaccination posts available. They are linked together using their index's.
Problem is after adding the buttons, I am getting an "Out of Memory" error.
Without the buttons, it runs as intended. Buttons are important because some vaccination posts are still operational while others are not.
Question:
1 - How can I get passed "Out of Memory" error?
Code:
import 'package:colour/colour.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
// import 'package:shared_preferences/shared_preferences.dart';
class VaccineCenterList extends StatefulWidget {
const VaccineCenterList({key}) : super(key: key);
static const String idScreen = "VaccineCenterList";
#override
_VaccineCenterListState createState() => _VaccineCenterListState();
}
class _VaccineCenterListState extends State<VaccineCenterList> {
List<bool> buttonPressed = [];
buttonControls(numOfDrivers) {
print(numOfDrivers);
while (numOfDrivers > 0) {
buttonPressed.add(true);
}
print(buttonPressed);
}
#override
void initState() {
super.initState();
print("I'm in the vaccine list search screen");
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
backgroundColor: Colors.grey[100],
centerTitle: true,
elevation: 0,
iconTheme: const IconThemeData(
color: Colors.black,
),
title: Text(
"Pamoja",
style: GoogleFonts.lexendMega(
fontWeight: FontWeight.bold,
color: Colors.black,
fontSize: 35,
),
),
),
body: GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: SingleChildScrollView(
child: Column(
children: [
Container(
// textfield
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: hospitalCountyEditingController,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.words,
decoration: InputDecoration(
border: const OutlineInputBorder(
borderSide: BorderSide(
color: Colors.black,
style: BorderStyle.solid,
width: 1,
),
),
labelText: "Search County",
labelStyle: const TextStyle(
fontSize: 14.0,
color: Colors.blueGrey,
),
hintStyle: GoogleFonts.lexendMega(
color: Colors.grey,
fontSize: 10,
),
),
style: GoogleFonts.lexendMega(
fontSize: 14,
color: Colors.black,
),
),
),
ElevatedButton(
// search button
onPressed: () {
setState(() {
FocusScope.of(context).requestFocus(
FocusNode(),
);
driverList.clear();
if (hospitalCountyEditingController.text == "" ||
hospitalCountyEditingController.text == " ") {
isTrueOrFalse = "False";
} else {
isTrueOrFalse = "True";
}
});
getHospitalDetails();
},
child: Text(
"Search",
style: GoogleFonts.lexendMega(
fontWeight: FontWeight.bold,
),
),
),
SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: MediaQuery.of(context).size.height,
// height: 50,
// color: Colors.black,
child: Padding(
padding: const EdgeInsets.only(
right: 12.0,
left: 12.0,
bottom: 12.0,
),
child: driverList.isEmpty
? Center(
// child: CircularProgressIndicator(),
child: (isTrueOrFalse == "True")
? const CircularProgressIndicator()
: Column(
mainAxisAlignment:
MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
"Search for one of the counties below",
style:
GoogleFonts.lexendMega()),
),
Text(
"Nairobi, Baringo, Busia, Bomet, Bungoma, Elgeyo Marakwet, Embu, Garissa, Homa Bay, Isiolo, Kajiado, Kakamega, Kericho, Kiambu, Kilifi, Kirinyaga, Kisii, Kisumu, Kitui, Kwale, Laikipia, Lamu, Machakos, Makueni, Mandera, Marsabit, Meru, Migori, Mombasa, Muranga, Nakuru, Nandi, Narok, Nyamira, Nyandarua, Nyeri, Samburu, Siaya County, Taita Taveta, Tana River County, Tharaka Nithi, Trans Nzoia, Turkana, Uasin Gishu, Vihiga, Wajir, West Pokot",
textAlign: TextAlign.center,
style: GoogleFonts.lexendMega(
fontWeight: FontWeight.bold,
),
),
],
),
)
: ListView.builder(
itemCount: driverList.length,
itemBuilder: (context, index) {
buttonControls(driverList.length);
final Hospitals hospitals = driverList[index];
final String hospitalLocaiton =
hospitals.location;
final String hospitalPhone = hospitals.phone;
final String hospitalName = hospitals.name;
positiveCount = hospitals.positiveCount;
negativeCount = hospitals.negativeCount;
final int setpercentage =
calculatePositivePercentage(
positiveCount, negativeCount);
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 0,
child: ExpansionTile(
title: Text(
hospitalName.toUpperCase(),
style: GoogleFonts.lexendMega(),
textAlign: TextAlign.center,
),
children: [
Column(
children: [
Container(
child: (hospitalPhone
.isNotEmpty)
? ElevatedButton(
onPressed: () {
Clipboard.setData(
ClipboardData(
text:
hospitalPhone,
),
);
ScaffoldMessenger.of(
context)
.showSnackBar(
const SnackBar(
backgroundColor:
Colors.green,
content: Text(
"Number Copied",
textAlign:
TextAlign
.center,
),
),
);
},
child: Text(
hospitalPhone,
textAlign:
TextAlign.center,
style: GoogleFonts
.lexendMega(
fontSize: 13),
),
style: ElevatedButton
.styleFrom(
elevation: 0,
),
)
: const Text(""),
),
const SizedBox(
height: 5,
),
hospitalLocaiton.isNotEmpty
? Container(
padding:
const EdgeInsets.all(
8),
decoration: BoxDecoration(
border: Border.all(
color: Colors.black,
),
borderRadius:
BorderRadius
.circular(10),
),
child: Text(
hospitalLocaiton,
textAlign:
TextAlign.center,
style: GoogleFonts
.lexendMega(
fontSize: 12,
),
),
)
: Text(
hospitalLocaiton,
textAlign:
TextAlign.center,
style: GoogleFonts
.lexendMega(
fontSize: 12,
),
),
const SizedBox(
height: 10,
),
Text(
"$setpercentage% (percent) of voters say this hospital administer vaccines.",
textAlign: TextAlign.center,
style: GoogleFonts.lexendMega(
fontWeight: FontWeight.bold,
fontSize: 12,
color: Colors.deepPurple[400],
),
),
const SizedBox(
height: 10,
),
const Divider(
// thickness: 1,
indent: 20,
endIndent: 20,
color: Colors.black87,
),
const SizedBox(
height: 10,
),
Text(
"Does this Hospital administer Vaccines?\n(To help the public, please vote only if you know.)",
textAlign: TextAlign.center,
style: GoogleFonts.lexendMega(
fontSize: 12,
fontWeight: FontWeight.bold,
),
),
// Container(
// child: (positiveCount == 0)
// ? Text("Votes: $positiveCount")
// : Text("Votes: " +
// positiveCount.toString()),
// ),
Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment
.spaceEvenly,
children: [
ElevatedButton(
onPressed:
buttonPressed[index]
? () {
positiveIncrement(
hospitalName,
index);
ScaffoldMessenger.of(
context)
.showSnackBar(
const SnackBar(
backgroundColor:
Colors.redAccent,
content:
Text(
"Voted",
textAlign:
TextAlign.center,
),
),
);
}
: null,
child: Text(
"Yes",
style: GoogleFonts
.lexendMega(),
),
style: ElevatedButton
.styleFrom(
primary:
Colour("#87D68D"),
),
),
ElevatedButton(
onPressed:
buttonPressed[index]
? () {
negativeIncrement(
hospitalName,
index);
ScaffoldMessenger.of(
context)
.showSnackBar(
const SnackBar(
backgroundColor:
Colors.redAccent,
content:
Text(
"Voted",
textAlign:
TextAlign.center,
),
),
);
}
: null,
child: Text(
"No",
style: GoogleFonts
.lexendMega(),
),
style: ElevatedButton
.styleFrom(
primary:
Colour("#E3655B"),
),
),
],
),
// const SizedBox(
// height: 10,
// ),
// Text(
// "Total No. of Votes: ",
// style:
// GoogleFonts.lexendMega(
// fontSize: 12),
// ),
const SizedBox(
height: 10,
),
Text(
"1 - To vote, tap once\n2 - To Undo vote, tap and hold",
style:
GoogleFonts.lexendMega(
fontSize: 12,
),
textAlign: TextAlign.start,
),
const SizedBox(
height: 10,
),
],
),
],
),
],
),
),
);
},
),
),
),
// SizedBox(height: 40),
],
),
),
],
),
),
),
);
}
}
class Hospitals {
final String name;
final String phone;
final String location;
// final String county;
final int positiveCount;
final int negativeCount;
// final int intPercentage;
// final int totalVotes;
Hospitals({
required this.name,
required this.phone,
required this.location,
// required this.county,
required this.positiveCount,
required this.negativeCount,
// required this.intPercentage,
// required this.totalVotes,
});
static Hospitals fromJson(Map<String, dynamic> json) {
return Hospitals(
name: json['HospitalName'],
phone: json['HospitalPhone'],
// county: json['county'],
location: json['HospitalAddres'],
positiveCount: json['poitiveCount'],
negativeCount: json['negativeCount'],
// intPercentage: json['intPercentage'],
// totalVotes: json['totalVotes']
);
}
}
buttonControls(numOfDrivers) {
print(numOfDrivers);
while (numOfDrivers > 0) {
buttonPressed.add(true);
}
print(buttonPressed);
}
numOfDrivers reference doesn't change over time, because it's a function parameter. Whenever you call buttonControls with numOfDrivers > 0, you get OutOfMemory.
Ask another question. It's a general problem, not related to OutOfMemory statement in title.

How to add user-submitted tags data from "material_tag_editor" into a "Flutter Form Builder" form?

I am building a form using the "Flutter Form Builder" package 4.0.2 and trying to add two fields where users enter "tags" via the "material_tag_editor" package 0.0.6
The Problem: when then form is submitted by pressing the "Post" button, neither of the data submitted for those "tag" form fields (Q1 or Q3) is included (see screenshot of the console below).
Notice the line "flutter: {qFour: 30, qFive: sample answer to q5, qTen: sample answer to q10}" - neither Q1 nor Q3 are included (I added their data in separate print statements, so you see them in the console - look for the >>> lines).
Here are screenshots of the form with sample tags entered (iPhone simulator screenshot), and the bottom of the form with the button:
Here's the code:
import 'package:flutter/cupertino.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:streakers_journal_beta/screens/reviews_screen.dart';
import 'package:streakers_journal_beta/screens/welcome_screen.dart';
import 'package:streakers_journal_beta/models/user.dart';
import 'package:rflutter_alert/rflutter_alert.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
// BEGIN code from material_tag_editor
import 'package:material_tag_editor/tag_editor.dart';
import 'package:material_tag_editor/tag_editor_layout_delegate.dart';
import 'package:material_tag_editor/tag_layout.dart';
import 'package:material_tag_editor/tag_render_layout_box.dart';
// END code from material_tag_editor
//import 'dart:html';
//import 'dart:convert';
// This is the stateful widget that the main application instantiates, per https://api.flutter.dev/flutter/widgets/Form-class.html
class SandboxWriteReviewScreen extends StatefulWidget {
// BEGIN code from material_tag_editor
final String title = 'Material Tag Editor Demo';
// END code from material_tag_editor
#override
_SandboxWriteReviewScreenState createState() =>
_SandboxWriteReviewScreenState();
}
// This is the private State class that goes with WriteReviewScreen
class _SandboxWriteReviewScreenState extends State<SandboxWriteReviewScreen> {
var data;
AutovalidateMode autovalidateMode = AutovalidateMode.always;
bool readOnly = false;
bool showSegmentedControl = true;
//final _newFormbuilderKey = GlobalKey<FormState>();
final _newnewFormbuilderKey = GlobalKey<FormBuilderState>();
// above "GlobalKey" lets us generate a unique, app-wide ID that we can associate with our form, per https://fluttercrashcourse.com/blog/realistic-forms-part1
final ValueChanged _onChanged = (val) => print(val);
// BEGIN related to FormBuilderTextField in form below
final _ageController = TextEditingController(text: '45');
bool _ageHasError = false;
// END related to FormBuilderTextField in form below
String qEleven;
String qTwelve;
// BEGIN code from material_tag_editor
List<String> qOne = [];
final FocusNode _focusNode = FocusNode();
onDelete(index) {
setState(() {
qOne.removeAt(index);
});
}
// below = reiteration for cons
List<String> qThree = [];
//final FocusNode _focusNode = FocusNode();
uponDelete(index) {
// NOTE: "uponDelete" for cons vs. "onDelete" for pros
setState(() {
qThree.removeAt(index);
});
}
// END code from material_tag_editor
//final _user = User();
List<bool> isSelected;
int starIconColor =
0xffFFB900; // was 0xffFFB900; 0xffD49428 is from this image: https://images.liveauctioneers.com/houses/logos/lg/bartonsauction550_large.jpg?auto=webp&format=pjpg&width=140
#override
void initState() {
//isSelected = [true, false];
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
Padding(
padding: EdgeInsets.only(right: 12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
IconButton(
icon: Icon(Icons.keyboard_backspace),
onPressed: () {
Navigator.pop(context);
},
),
Text(
'back',
style: TextStyle(
fontSize: 7,
),
),
],
),
),
],
leading: Icon(
Icons.rate_review,
color: Colors.black54,
),
title: Column(
children: [
Text(
'SANDBOX Write a Review',
style: TextStyle(
color: Colors.white,
fontSize: 16,
),
),
SizedBox(
height: 6.0,
),
Text(
'flutter_form_builder ^4.0.2',
style: TextStyle(
color: Colors.limeAccent,
fontSize: 14,
),
),
SizedBox(
height: 6.0,
),
],
),
// BEGIN appBar gradient code, per https://medium.com/flutter-community/how-to-improve-your-flutter-application-with-gradient-designs-63180ba96124
flexibleSpace: Container(
decoration: BoxDecoration(
color: Colors.indigoAccent,
),
),
backgroundColor: Colors.white,
centerTitle: false,
),
body: SingleChildScrollView(
child: Container(
child: Builder(
builder: (context) => FormBuilder(
// was "builder: (context) => Form("
key: _newnewFormbuilderKey,
initialValue: {
'date': DateTime.now(),
},
child: Padding(
padding: const EdgeInsets.all(14.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
SizedBox(
height: 12.0,
),
RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
'Q1 via TagEditor', // was 'What are 3 good or positive things about the house, property or neighborhood?', // [ 1 ​]
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
TextSpan(
text: ' (optional)',
style: TextStyle(
fontWeight: FontWeight.normal,
fontStyle: FontStyle.italic,
fontSize: 14.0,
color: Colors.black54,
), // was 'misleading or inaccurate?',
),
],
),
),
// BEGIN code from material_tag_editor
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: TagEditor(
length: qOne.length,
delimiters: [
','
], // was delimiters: [',', ' '], Also tried "return" ('\u2386',) and '\u{2386}'
hasAddButton: true,
textInputAction: TextInputAction
.next, // moves user from one field to the next!!!!
autofocus: false,
maxLines: 1,
// focusedBorder: OutlineInputBorder(
// borderSide: BorderSide(color: Colors.lightBlue),
// borderRadius: BorderRadius.circular(20.0),
// ),
inputDecoration: const InputDecoration(
// below was "border: InputBorder.none,"
isDense: true,
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(20.0),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.lightBlue),
borderRadius: const BorderRadius.all(
const Radius.circular(20.0),
),
// above is per https://github.com/flutter/flutter/issues/5191
),
labelText: 'separate, with, commas',
labelStyle: TextStyle(
fontStyle: FontStyle.italic,
backgroundColor:
Color(0x65dffd02), // was Color(0xffDDFDFC),
color: Colors.black87, // was Color(0xffD82E6D),
fontSize: 14,
),
),
onTagChanged: (value) {
setState(() {
qOne.add(value);
});
},
tagBuilder: (context, index) => _Chip(
index: index,
label: qOne[index],
onDeleted: onDelete,
),
),
),
// END code from material_tag_editor
SuperDivider(),
// END Chips Input
RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
'​​Q3 via TagEditor (skipped Q2, for simplicity)', // [ 2 ​] was '​​List up to 3 negatives, or things you don’t like, about the house, property or neighborhood:',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
TextSpan(
text: '(optional)', // was text: '\n(optional)',
style: TextStyle(
fontWeight: FontWeight.normal,
fontStyle: FontStyle.italic,
fontSize: 14.0,
backgroundColor:
Color(0x70DDFDFC), // was Color(0x30F8A0A2),
color: Colors.black54, // was Color(0xffD82E6D),
//color: Colors.black54,
), // was 'misleading or inaccurate?',
),
],
),
),
// BEGIN code from material_tag_editor
Padding(
padding: const EdgeInsets.only(top: 16.0),
child: TagEditor(
length: qThree.length,
delimiters: [','], // was delimiters: [',', ' '],
hasAddButton: true,
textInputAction: TextInputAction
.next, // moves user from one field to the next!!!!
autofocus: false,
maxLines: 1,
// focusedBorder: OutlineInputBorder(
// borderSide: BorderSide(color: Colors.lightBlue),
// borderRadius: BorderRadius.circular(20.0),
// ),
inputDecoration: const InputDecoration(
// below was "border: InputBorder.none,"
isDense: true,
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(
const Radius.circular(20.0),
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.lightBlue),
borderRadius: const BorderRadius.all(
const Radius.circular(20.0),
),
// above is per https://github.com/flutter/flutter/issues/5191
),
labelText: 'separate, with, commas',
labelStyle: TextStyle(
fontStyle: FontStyle.italic,
backgroundColor:
Color(0x65dffd02), // was Color(0xffDDFDFC),
color: Colors.black87, // was Color(0xffD82E6D),
fontSize: 14,
),
),
onTagChanged: (value) {
setState(() {
qThree.add(value);
});
},
tagBuilder: (context, index) => _Chip(
index: index,
label: qThree[index],
onDeleted: uponDelete,
),
),
),
// END code from material_tag_editor
SuperDivider(),
RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
'​​Q4 - via FormBuilder\'s FormBuilderRadioGroup', // [ 3 ​]
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
TextSpan(
text: ' (required)',
style: TextStyle(
fontWeight: FontWeight.normal,
fontStyle: FontStyle.italic,
fontSize: 14.0,
color: Colors.red[700],
), // was 'misleading or inaccurate?',
),
],
),
),
FormBuilderRadioGroup(
name: 'qFour',
decoration: const InputDecoration(
border: InputBorder.none,
labelStyle: TextStyle(fontStyle: FontStyle.italic),
),
wrapVerticalDirection: VerticalDirection.down,
// orientation: GroupedRadioOrientation.vertical,
orientation: OptionsOrientation.vertical,
onChanged: _onChanged,
options: [
FormBuilderFieldOption(
value: '0', child: Text('Never')),
FormBuilderFieldOption(
value: '30', child: Text('Within the last month')),
FormBuilderFieldOption(
value: '180',
child: Text('Within the last 6 months')),
FormBuilderFieldOption(
value: '181',
child: Text('More than 6 months ago')),
],
),
SuperDivider(),
Center(
child: RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
'Q5 - via FormBuilder\'s FormBuilderTextField', // [ 4 ​]
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
TextSpan(
text: ' (optional)',
style: TextStyle(
fontWeight: FontWeight.normal,
fontStyle: FontStyle.italic,
fontSize: 14.0,
color: Colors.black54,
), // was 'misleading or inaccurate?',
),
],
),
),
),
GavTextField(
maxCharLength: 200,
fieldAttribute: 'qFive',
fieldLabelText: '',
),
SuperDivider(),
RichText(
text: TextSpan(
style: TextStyle(
color: Colors.blue,
),
children: <TextSpan>[
TextSpan(
text:
'Q10 - via FormBuilder\'s FormBuilderTextField (skipped Q6 - Q9, for simplicity)', // [ 9 ​]
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16.0,
),
),
TextSpan(
text: ' (optional)',
style: TextStyle(
fontWeight: FontWeight.normal,
fontStyle: FontStyle.italic,
fontSize: 14.0,
color: Colors.black54,
), // was 'misleading or inaccurate?',
),
],
),
),
GavTextField(
maxCharLength: 1200,
fieldAttribute: 'qTen',
fieldLabelText:
'Be honest & kind.', // was 'Be honest, but kind.',
),
SuperDivider(),
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Colors.purple,
padding: EdgeInsets.symmetric(
horizontal: 50, vertical: 20),
textStyle: TextStyle(
fontSize: 20, fontWeight: FontWeight.bold)),
onPressed: () {
_newnewFormbuilderKey.currentState.save();
if (_newnewFormbuilderKey.currentState
.validate()) {
print(_newnewFormbuilderKey.currentState.value);
print(
' >>> Q1\'s value via separate print: {$qOne}',
);
print(
' >>> Q3\'s value via separate print: {$qThree}',
);
} else {
print("validation failed");
}
},
child: Text(
'Post',
style: TextStyle(
color: Colors.white,
fontSize: 20,
),
),
),
],
),
),
SizedBox(
height: 200.0,
),
],
),
),
),
),
),
),
);
}
}
class GavTextField extends StatelessWidget {
GavTextField(
{#required this.maxCharLength,
#required this.fieldAttribute,
#required this.fieldLabelText});
int maxCharLength;
String fieldAttribute;
String fieldLabelText;
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 16.0),
child: FormBuilderTextField(
name: '$fieldAttribute',
// BEGIN countdown to max number of characters, per https://stackoverflow.com/a/64035861/1459653
maxLength: maxCharLength,
maxLines: null,
buildCounter: (
BuildContext context, {
int currentLength,
int maxLength,
bool isFocused,
}) {
return Text(
'${maxLength - currentLength}',
);
},
// END countdown to max number of characters, per https://stackoverflow.com/a/64035861/1459653
decoration: InputDecoration(
labelText:
'$fieldLabelText', // was " Separate items, with, commas",
//counterText: _textController.text.length.toString(),
labelStyle: TextStyle(
fontSize: 12.5,
fontStyle: FontStyle.italic,
),
//helperText: 'Separate, with, commas',
//floatingLabelBehavior: ,
// filled: true,
// fillColor: Colors.lightBlue.withOpacity(0.05),
// BEGIN change border if focus
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.lightBlue),
borderRadius: BorderRadius.circular(20.0),
),
// END change border if focus
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(20.0),
borderSide: BorderSide(),
),
),
textInputAction:
TextInputAction.next, // moves user from one field to the next!!!!
autofocus:
false, // on screen load, first text field is already active - user can just start typing
),
);
}
} //</formstate>`
var alertStyle = AlertStyle(
animationType: AnimationType.fromTop,
isCloseButton: true,
isOverlayTapDismiss: true,
descTextAlign: TextAlign.start,
alertBorder: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
side: BorderSide(
color: Colors.grey,
),
),
titleStyle: TextStyle(
fontWeight: FontWeight.normal,
fontStyle: FontStyle.italic,
fontSize: 16,
color: Colors.black54,
),
alertAlignment: Alignment.topCenter,
);
// BEGIN code from material_tag_editor
class _Chip extends StatelessWidget {
const _Chip({
#required this.label,
#required this.onDeleted,
#required this.index,
});
final String label;
final ValueChanged<int> onDeleted;
final int index;
#override
Widget build(BuildContext context) {
return Chip(
backgroundColor: Colors.blueGrey.shade100,
labelPadding: const EdgeInsets.only(left: 8.0),
label: Text(label),
deleteIcon: Icon(
Icons.cancel_rounded, // was "Icons.close,"
size: 18,
),
onDeleted: () {
onDeleted(index);
},
);
}
}
// END code from material_tag_editor
class SuperDivider extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(
top: 4.0,
bottom: 4.0,
),
child: const Divider(
color: Colors.white70,
height: 30,
thickness: 0.1,
indent: 0,
endIndent: 0,
),
);
}
}
Flutter form builder assumes that only form children's will be used which automatically updates ancestor form whenever their value is changed. But since tag builder is not a form widget, you can do two things -
Wrap these widgets inside a new form widget, whose responsibility will be to only send updates to the ancestor form and render its child.
You can do something like
class GenericFormWidget<T> extends StatefulWidget {
GenericFormWidget({
Key key,
#required this.attribute,
#required this.builder,
}) : super(key: key);
final String attribute;
final Function(BuildContext, Function(T value)) builder;
#override
Widget build(BuildContext context) {
return widget.builder(context, _updateValue);
}
void _updateValue(T value) =>
_formState.setAttributeValue(widget.attribute, value);
}
Instead of using flutter form builder, you can have use Flutter form and create your own field UI using material widgets. This will be costly for you since you will need to migrate existing fields too.