getting error " getter 'year' was called on null " when clearing textfield in flutter - flutter

here when I click on the clear button it will not clear the all the textbox only clearing the expiry_date field only and also showing me the exception "getter year was called on null".
in this code I have a clear button when I click on that button it will not clearing the button and showing year called on null. I think this problem happens because I've added onChanged in ExpiryDate widget. but I need an onChnaged event. so I need to clear everything. Hope you understand the question. Your help can make my day.
Here is the code :
class _BspLicensedSignupPageState extends State<BspLicensedSignupPage>
with AfterLayoutMixin<BspLicensedSignupPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final TextEditingController _bspBusinessLicenseNumber =
TextEditingController();
final TextEditingController _bspLicenseAuthorityName =
TextEditingController();
final TextEditingController _bspLicenseExpiryDate = TextEditingController();
final format = new DateFormat.yMMMEd('en-US');
String _isoDate;
List<BusinessProfilePicture> _bspLicenseImages =
new List<BusinessProfilePicture>();
List<Object> images = List<Object>();
List<dynamic> _bspLicenseAuthorityTypes = <dynamic>[];
Map<String, dynamic> _bspLicenseAuthorityType;
bool _isvisibleissuingauthority = false;
bool businesslicensecheck = false;
int radioValue = -1;
Future<File> licenceimage;
Future<File> profilepicture;
DateTime date;
TimeOfDay time;
String _countryId;
BSPSignupRepository _bspSignupRepository = new BSPSignupRepository();
bool autovalidate = false;
bool _isEditMode = false;
int selected = 0;
List<String> _imageFilesList = [];
var isUploadingPost = false;
var isEditInitialised = true;
List<File> _licenseImages = [];
#override
void initState() {
super.initState();
_scrollController = ScrollController();
}
Widget _buildbusinesslicenseno() {
return new TudoTextWidget(
prefixIcon: Icon(FontAwesomeIcons.idCard),
controller: _bspBusinessLicenseNumber,
labelText: AppConstantsValue.appConst['licensedsignup']
['businesslicenseno']['translation'],
validator: Validators().validateLicenseno,
);
}
Widget _buildexpirydate() {
return DateTimeField(
format: format,
autocorrect: true,
autovalidate: false,
controller: _bspLicenseExpiryDate,
readOnly: true,
validator: (date) => (date == null || _bspLicenseExpiryDate.text == '')
? 'Please enter valid date'
: null,
onChanged: (date) {
_isoDate = DateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(date);
},
decoration: InputDecoration(
labelText: "Expiry Date",
hintText: "Expiry Date",
prefixIcon: Icon(
FontAwesomeIcons.calendar,
size: 24,
)),
onShowPicker: (context, currentValue) {
return showDatePicker(
context: context,
firstDate: DateTime.now(),
initialDate: currentValue ?? DateTime.now(),
lastDate: DateTime(2100),
);
},
);
}
Widget _buildlicenseauthority() {
return new TudoTextWidget(
validator: (val) => Validators.validateName(val, "Issuing Authority"),
controller: _bspLicenseAuthorityName,
prefixIcon: Icon(Icons.account_circle),
labelText: AppConstantsValue.appConst['licensedsignup']
['licenseissuingauthority']['translation'],
hintText: AppConstantsValue.appConst['licensedsignup']
['licenseissuingauthority']['translation'],
);
}
Widget _buildlabeluploadlicensepicture() {
return Text(AppConstantsValue.appConst['licensedsignup']
['labeluploadlicenpicture']['translation']);
}
Widget _buildlegalbusinesscheck() {
return TudoConditionWidget(
text: AppConstantsValue.appConst['licensedsignup']['legalbusinesscheck']
['translation'],
);
}
Widget content(
BuildContext context, BspLicensedSignupViewModel bspLicensedSignupVm) {
final appBar = AppBar(
centerTitle: true,
title: Text("BSP Licensed Details"),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
NavigationHelper.navigatetoBack(context);
},
),
);
final bottomNavigationBar = Container(
height: 56,
//margin: EdgeInsets.symmetric(vertical: 24, horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FlatButton.icon(
icon: Icon(Icons.close),
label: Text('Clear'),
color: Colors.redAccent,
textColor: Colors.black,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
// onpress
onPressed: () {
_formKey.currentState.reset();
_bspBusinessLicenseNumber.clear();
_bspLicenseAuthorityName.clear();
_bspBusinessLicenseNumber.text = '';
_bspLicenseAuthorityName.text = '';
setState(() {
_licenseImages.clear();
_imageFilesList.clear();
});
},
),
new FlatButton.icon(
icon: Icon(FontAwesomeIcons.arrowCircleRight),
label: Text('Next'),
color: colorStyles["primary"],
textColor: Colors.white,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () async {
},
),
],
),
);
return new Scaffold(
appBar: appBar,
bottomNavigationBar: bottomNavigationBar,
body: Container(
height: double.infinity,
width: double.infinity,
child: Stack(
children: <Widget>[
// Background(),
SingleChildScrollView(
controller: _scrollController,
child: SafeArea(
top: false,
bottom: false,
child: Form(
autovalidate: autovalidate,
key: _formKey,
child: Scrollbar(
child: SingleChildScrollView(
dragStartBehavior: DragStartBehavior.down,
// padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: new Container(
margin: EdgeInsets.fromLTRB(30, 30, 30, 0),
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_buildbusinesslicenseno(),
_buildexpirydate(),
_buildlegalbusinesscheck()
],
),
),
),
),
),
),
),
],
),
),
);
}
#override
Widget build(BuildContext context) {
return new StoreConnector<AppState, BspLicensedSignupViewModel>(
converter: (Store<AppState> store) =>
BspLicensedSignupViewModel.fromStore(store),
onInit: (Store<AppState> store) {
_countryId = store.state.auth.loginUser.user.country.id.toString();
print('_countryId');
print(_countryId);
},
builder: (BuildContext context,
BspLicensedSignupViewModel bspLicensedSignupVm) =>
content(context, bspLicensedSignupVm),
);
}
}
}

The formatter in the onChanged can't format a null date.
So you can add a null check in your DateTimeField's onChanged method.
Like this:
onChanged: (date) {
if (date != null){
_isoDate = DateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(date);
}
}

Related

why my FormBuilder does not reset the values?

I use a FormBuilder with a global key, I have a showModalBottomSheet in which i have my filters rendered, before I click the apply button my filters are reset, but after I applied the filters and I want to reset my filters, my _formKey.currentState?.reset() stops working.
I think the problem is in _buildFilterField but I don't quite understand how to reset it.
final _formKey = GlobalKey<FormBuilderState>();
var filterData;
bool isFilterLoading = true;
Future showFilter(context) => showModalBottomSheet(
isScrollControlled: true,
context: context,
builder: (context) => StatefulBuilder(builder: (context, innerSetState) {
Future.delayed(const Duration(milliseconds: 700), () {
innerSetState(() {
isFilterLoading = false;
});
});
return SafeArea(
child: FormBuilder(
key: _formKey,
child: Container(
child: isFilterLoading != true
? Column(children: [
Padding(
padding: const EdgeInsets.only(),
child: Row(
children: [
TextButton(
onPressed: () {
_formKey.currentState?.reset();
},
child: const Text(
'Сброс',
style: TextStyle(
color: Colors.black, fontSize: 16),
)),
],
),
),
_myRangeSlider(),
_buildFilterField(filterData),
Padding(
padding:
const EdgeInsets.only(top: 10, bottom: 10),
child: SizedBox(
child: ElevatedButton(
onPressed: () {
_formKey.currentState!.save();
filterValues = _formKey.currentState!.value;
setState(() {
String encodedString =
filterValues.keys.map((key) {
return key +
'=' +
Uri.encodeQueryComponent(
filterValues[key].toString());
}).join('&');
filters = encodedString;
page = 1;
list.clear();
productFuture = getProducts(val, filters);
});
Navigator.pop(context);
},
child: const Text(
'Показать результаты',
),
),
),
),
])
: const Center(child: CircularProgressIndicator())),
),
);
}));
Widget _buildFilterField(Map<String, dynamic> field) {
List<Widget> children = [];
print(filterValues);
field.keys.forEach((key) {
if (field[key]["type"] == 'radio') {
children.add(FormBuilderRadioGroup(
initialValue: filterValues?[key] ?? field[key]["initial_value"],
name: field[key]["value"],
decoration: const InputDecoration(
border: InputBorder.none,
),
options: [
for (var option in field[key]["data"])
FormBuilderFieldOption(
value: option['value'],
child: Text(
option['text'],
)),
],
));
}
});
return children.isEmpty
? Container()
: Column(
children: children,
);
}

Not able to remove focus from input field

I have four textfields, a title field, a details field, a date field, and a time field. Both the date and time fields are wrapped within a gesture detector, and onTap calls a pickDateAndTime method. The problem is that when I click on the date field and try to manually change the time through the input rather than the dial way, the focus goes to the title field and when I am still on the time picker and type something in the time picker, the title field gets changed with the new input. The weird part is that this error just appeared out of nowhere, and there are no errors reported in the console.
class TodoScreen extends StatefulWidget {
final int? todoIndex;
final int? arrayIndex;
const TodoScreen({Key? key, this.todoIndex, this.arrayIndex})
: super(key: key);
#override
State<TodoScreen> createState() => _TodoScreenState();
}
class _TodoScreenState extends State<TodoScreen> {
final ArrayController arrayController = Get.find();
final AuthController authController = Get.find();
final String uid = Get.find<AuthController>().user!.uid;
late TextEditingController _dateController;
late TextEditingController _timeController;
late TextEditingController titleEditingController;
late TextEditingController detailEditingController;
late String _setTime, _setDate;
late String _hour, _minute, _time;
late String dateTime;
late bool done;
#override
void initState() {
super.initState();
String title = '';
String detail = '';
String date = '';
String? time = '';
if (widget.todoIndex != null) {
title = arrayController
.arrays[widget.arrayIndex!].todos![widget.todoIndex!].title ??
'';
detail = arrayController
.arrays[widget.arrayIndex!].todos![widget.todoIndex!].details ??
'';
date = arrayController
.arrays[widget.arrayIndex!].todos![widget.todoIndex!].date!;
time = arrayController
.arrays[widget.arrayIndex!].todos![widget.todoIndex!].time;
}
_dateController = TextEditingController(text: date);
_timeController = TextEditingController(text: time);
titleEditingController = TextEditingController(text: title);
detailEditingController = TextEditingController(text: detail);
done = (widget.todoIndex == null)
? false
: arrayController
.arrays[widget.arrayIndex!].todos![widget.todoIndex!].done!;
}
DateTime selectedDate = DateTime.now();
TimeOfDay selectedTime = TimeOfDay(
hour: (TimeOfDay.now().minute > 55)
? TimeOfDay.now().hour + 1
: TimeOfDay.now().hour,
minute: (TimeOfDay.now().minute > 55) ? 0 : TimeOfDay.now().minute + 5);
Future<DateTime?> _selectDate() => showDatePicker(
builder: (context, child) {
return datePickerTheme(child);
},
initialEntryMode: DatePickerEntryMode.calendarOnly,
context: context,
initialDate: selectedDate,
initialDatePickerMode: DatePickerMode.day,
firstDate: DateTime.now(),
lastDate: DateTime(DateTime.now().year + 5));
Future<TimeOfDay?> _selectTime() => showTimePicker(
builder: (context, child) {
return timePickerTheme(child);
},
context: context,
initialTime: selectedTime,
initialEntryMode: TimePickerEntryMode.input);
Future _pickDateTime() async {
DateTime? date = await _selectDate();
if (date == null) return;
if (date != null) {
selectedDate = date;
_dateController.text = DateFormat("MM/dd/yyyy").format(selectedDate);
}
TimeOfDay? time = await _selectTime();
if (time == null) {
_timeController.text = formatDate(
DateTime(
DateTime.now().year,
DateTime.now().day,
DateTime.now().month,
DateTime.now().hour,
DateTime.now().minute + 5),
[hh, ':', nn, " ", am]).toString();
}
if (time != null) {
selectedTime = time;
_hour = selectedTime.hour.toString();
_minute = selectedTime.minute.toString();
_time = '$_hour : $_minute';
_timeController.text = _time;
_timeController.text = formatDate(
DateTime(2019, 08, 1, selectedTime.hour, selectedTime.minute),
[hh, ':', nn, " ", am]).toString();
}
}
#override
Widget build(BuildContext context) {
bool visible =
(_dateController.text.isEmpty && _timeController.text.isEmpty)
? false
: true;
final formKey = GlobalKey<FormState>();
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
title: Text((widget.todoIndex == null) ? 'New Task' : 'Edit Task',
style: menuTextStyle),
leadingWidth: (MediaQuery.of(context).size.width < 768) ? 90.0 : 100.0,
leading: Center(
child: Padding(
padding: (MediaQuery.of(context).size.width < 768)
? const EdgeInsets.only(left: 0)
: const EdgeInsets.only(left: 21.0),
child: TextButton(
style: const ButtonStyle(
splashFactory: NoSplash.splashFactory,
),
onPressed: () {
Get.back();
},
child: Text(
"Cancel",
style: paragraphPrimary,
),
),
),
),
centerTitle: true,
actions: [
Center(
child: Padding(
padding: (MediaQuery.of(context).size.width < 768)
? const EdgeInsets.only(left: 0)
: const EdgeInsets.only(right: 21.0),
child: TextButton(
style: const ButtonStyle(
splashFactory: NoSplash.splashFactory,
),
onPressed: () async {
},
child: Text((widget.todoIndex == null) ? 'Add' : 'Update',
style: paragraphPrimary),
),
),
)
],
),
body: SafeArea(
child: Container(
width: double.infinity,
padding: (MediaQuery.of(context).size.width < 768)
? const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0)
: const EdgeInsets.symmetric(horizontal: 35.0, vertical: 15.0),
child: Column(
children: [
Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
validator: Validator.titleValidator,
controller: titleEditingController,
autofocus: true, // problem here
autocorrect: false,
cursorColor: Colors.grey,
maxLines: 1,
maxLength: 25,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
counterStyle: counterTextStyle,
hintStyle: hintTextStyle,
hintText: "Title",
border: InputBorder.none),
style: todoScreenStyle),
primaryDivider,
TextField(
controller: detailEditingController,
maxLines: null,
autocorrect: false,
cursorColor: Colors.grey,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
counterStyle: counterTextStyle,
hintStyle: hintTextStyle,
hintText: "Notes",
border: InputBorder.none),
style: todoScreenDetailsStyle),
],
),
),
Visibility(
visible: (widget.todoIndex != null) ? true : false,
child: GestureDetector(
onTap: () {},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"Completed",
style: todoScreenStyle,
),
Transform.scale(
scale: 1.3,
child: Theme(
data: ThemeData(
unselectedWidgetColor: const Color.fromARGB(
255, 187, 187, 187)),
child: Checkbox(
shape: const CircleBorder(),
checkColor: Colors.white,
activeColor: primaryColor,
value: done,
side: Theme.of(context).checkboxTheme.side,
onChanged: (value) {
setState(() {
done = value!;
});
})),
)
],
),
),
),
GestureDetector(
onTap: () async {
await _pickDateTime();
setState(() {
visible = true;
});
},
child: Column(
children: [
Row(
children: [
Flexible(
child: TextField(
enabled: false,
controller: _dateController,
onChanged: (String val) {
_setDate = val;
},
decoration: InputDecoration(
hintText: "Date",
hintStyle: hintTextStyle,
border: InputBorder.none),
style: todoScreenStyle,
),
),
visible
? IconButton(
onPressed: () {
_dateController.clear();
_timeController.clear();
setState(() {});
},
icon: const Icon(
Icons.close,
color: Colors.white,
))
: Container()
],
),
primaryDivider,
TextField(
onChanged: (String val) {
_setTime = val;
},
enabled: false,
controller: _timeController,
decoration: InputDecoration(
hintText: "Time",
hintStyle: hintTextStyle,
border: InputBorder.none),
style: todoScreenStyle,
)
],
),
),
],
),
),
),
);
}
}
Should I open an issue on Github, as I had not made any changes to the code for it behave this way and also because there were no errors in the console
Here is the full code on Github
Update
Here is a reproducible example:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const TodoScreen(),
);
}
}
class TodoScreen extends StatefulWidget {
const TodoScreen({Key? key}) : super(key: key);
#override
State<TodoScreen> createState() => _TodoScreenState();
}
class _TodoScreenState extends State<TodoScreen> {
late TextEditingController _dateController;
late TextEditingController _timeController;
late TextEditingController titleEditingController;
late TextEditingController detailEditingController;
late String _setTime, _setDate;
late String _hour, _minute, _time;
late String dateTime;
#override
void initState() {
super.initState();
String title = '';
String detail = '';
String date = '';
String? time = '';
_dateController = TextEditingController(text: date);
_timeController = TextEditingController(text: time);
titleEditingController = TextEditingController(text: title);
detailEditingController = TextEditingController(text: detail);
}
#override
void dispose() {
super.dispose();
titleEditingController.dispose();
detailEditingController.dispose();
_timeController.dispose();
_dateController.dispose();
}
Theme timePickerTheme(child) => Theme(
data: ThemeData.dark().copyWith(
timePickerTheme: TimePickerThemeData(
backgroundColor: const Color.fromARGB(255, 70, 70, 70),
dayPeriodTextColor: Colors.green,
hourMinuteTextColor: MaterialStateColor.resolveWith((states) =>
states.contains(MaterialState.selected)
? Colors.white
: Colors.white),
dialHandColor: Colors.green,
helpTextStyle: TextStyle(
fontSize: 12, fontWeight: FontWeight.bold, color: Colors.green),
dialTextColor: MaterialStateColor.resolveWith((states) =>
states.contains(MaterialState.selected)
? Colors.white
: Colors.white),
entryModeIconColor: Colors.green,
),
textButtonTheme: TextButtonThemeData(
style: ButtonStyle(
foregroundColor:
MaterialStateColor.resolveWith((states) => Colors.green)),
),
),
child: child!,
);
Theme datePickerTheme(child) => Theme(
data: ThemeData.dark().copyWith(
colorScheme: ColorScheme.dark(
surface: Colors.green,
secondary: Colors.green,
onPrimary: Colors.white,
onSurface: Colors.white,
primary: Colors.green,
)),
child: child!,
);
DateTime selectedDate = DateTime.now();
TimeOfDay selectedTime = TimeOfDay(
hour: (TimeOfDay.now().minute > 55)
? TimeOfDay.now().hour + 1
: TimeOfDay.now().hour,
minute: (TimeOfDay.now().minute > 55) ? 0 : TimeOfDay.now().minute + 5);
Future<DateTime?> _selectDate() => showDatePicker(
builder: (context, child) {
return datePickerTheme(child);
},
initialEntryMode: DatePickerEntryMode.calendarOnly,
context: context,
initialDate: selectedDate,
initialDatePickerMode: DatePickerMode.day,
firstDate: DateTime.now(),
lastDate: DateTime(DateTime.now().year + 5));
Future<TimeOfDay?> _selectTime() => showTimePicker(
builder: (context, child) {
return timePickerTheme(child);
},
context: context,
initialTime: selectedTime,
initialEntryMode: TimePickerEntryMode.input);
Future _pickDateTime() async {
DateTime? date = await _selectDate();
if (date == null) return;
if (date != null) {
selectedDate = date;
_dateController.text = selectedDate.toString();
}
TimeOfDay? time = await _selectTime();
if (time != null) {
selectedTime = time;
_hour = selectedTime.hour.toString();
_minute = selectedTime.minute.toString();
_time = '$_hour : $_minute';
_timeController.text = _time;
_timeController.text =
DateTime(2019, 08, 1, selectedTime.hour, selectedTime.minute)
.toString();
}
}
#override
Widget build(BuildContext context) {
bool visible =
(_dateController.text.isEmpty && _timeController.text.isEmpty)
? false
: true;
final formKey = GlobalKey<FormState>();
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: AppBar(
centerTitle: true,
),
body: SafeArea(
child: Container(
width: double.infinity,
padding: (MediaQuery.of(context).size.width < 768)
? const EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0)
: const EdgeInsets.symmetric(horizontal: 35.0, vertical: 15.0),
child: Column(
children: [
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(14.0)),
padding: const EdgeInsets.symmetric(
horizontal: 24.0, vertical: 15.0),
child: Form(
key: formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: titleEditingController,
autofocus: true,
autocorrect: false,
cursorColor: Colors.grey,
maxLines: 1,
maxLength: 25,
textInputAction: TextInputAction.next,
decoration: InputDecoration(
hintText: "Title", border: InputBorder.none),
),
Divider(color: Colors.black),
TextField(
controller: detailEditingController,
maxLines: null,
autocorrect: false,
cursorColor: Colors.grey,
textInputAction: TextInputAction.done,
decoration: InputDecoration(
hintText: "Notes", border: InputBorder.none),
),
],
),
)),
GestureDetector(
onTap: () async {
await _pickDateTime();
setState(() {
visible = true;
});
},
child: Container(
margin: const EdgeInsets.only(top: 20.0),
width: double.infinity,
padding: const EdgeInsets.symmetric(
horizontal: 24.0, vertical: 15.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(14.0)),
child: Column(
children: [
Row(
children: [
Flexible(
child: TextField(
enabled: false,
controller: _dateController,
onChanged: (String val) {
_setDate = val;
},
decoration: InputDecoration(
hintText: "Date", border: InputBorder.none),
),
),
visible
? IconButton(
onPressed: () {
_dateController.clear();
_timeController.clear();
setState(() {});
},
icon: const Icon(
Icons.close,
color: Colors.white,
))
: Container()
],
),
Divider(
color: Colors.blue,
),
TextField(
onChanged: (String val) {
_setTime = val;
},
enabled: false,
controller: _timeController,
decoration: InputDecoration(
hintText: "Enter", border: InputBorder.none),
)
],
)),
),
],
),
),
),
);
}
}
In your main.dart file, you should return something like this:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
// This allows closing keyboard when tapping outside of a text field
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus &&
currentFocus.focusedChild != null) {
FocusManager.instance.primaryFocus!.unfocus();
}
},
child: // your app's entry point,
);
}
}
Add a focusNode in your textField:
FocusNode focusNode = FocusNode();
TextField(
focusNode: focusNode,
);
And then in the gesture detector, add that following code to unselect the textfield input:
FocusScope.of(context).requestFocus(FocusNode());
simply wrap your Scaffold widget GestureDetector and add FocusScope.of(context).requestFocus(FocusNode()); it will automatically unfocused text field when you click anywhere on your screen
GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(FocusNode());
},
child: Scaffold()
)
You can use below code to remove focus in gesture detector event
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
currentFocus.unfocus();
}

How to provide filled places in Flutter

First I want to introduce the pages.
Form Page
This is where I fill a form. When I click save as draft it will save the form data to the SQLite database. For now, I haven't implemented SQLite yet. Anyway, But I have a list that buffers the form input. When you click on Save as draft, I create a FormData object and add that data to a list and provide that list with Provider.
Form Page Code
class FormPage extends StatefulWidget {
FormData? formData = FormData();
FormPage({Key? key, this.formData}) : super(key: key);
#override
State<FormPage> createState() => _HomePageState();
}
class _HomePageState extends State<FormPage> {
String? isEmpty(String val) {
String? text;
setState(() {
if (val.isEmpty) {
text = 'Boş bırakılamaz';
}
});
return text;
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xffffe0b2),
appBar: AppBar(
// backgroundColor: Colors.teal.shade400,
title: const Text('Hasta Etkileşim Kaydı'),
centerTitle: true,
backgroundColor: const Color(0xffffb74d),
elevation: 0,
),
body: SafeArea(
child: FutureBuilder(
future: readJsonData(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text(snapshot.error.toString());
} else if (snapshot.hasData) {
var items = snapshot.data as List<dynamic>;
var listStajTuru = items[0].stajTuruItems;
var listCinsiyet = items[0].cinsiyetItems;
var listEtkilesim = items[0].etkilesimTuruItems;
var listKapsam = items[0].kapsamItems;
var listOrtam = items[0].ortamItems;
var listDoktor = items[0].doktorItems;
return Form(
key: _formKey,
child: ListView(
shrinkWrap: true,
children: [
myDropDownContainer(_valueStajTuru, listStajTuru,
hintTextStajTuru, onChangedStajTuru),
myDropDownContainer(_valueDoktor, listDoktor,
hintTextDoktor, onChangedDoktor),
myDropDownContainer(_valueCinsiyet, listCinsiyet,
hintTextCinsiyet, onChangedCinsiyet),
myDropDownContainer(_valueEtkilesim, listEtkilesim,
hintTextEtkilesim, onChangedEtkilesim),
myDropDownContainer(_valueKapsam, listKapsam,
hintTextKapsam, onChangedKapsam),
myDropDownContainer(_valueOrtam, listOrtam,
hintTextOrtam, onChangedOrtam),
myTextFieldRow(20, "Kayıt No: ", 10,
_formData.setKayitNo, isEmpty),
myTextFieldRow(
20, "Hastanın Yaşı:", 3, _formData.setYas, isEmpty),
myTextFieldRow(
20, "Şikayet:", 10, _formData.setSikayet, isEmpty),
myTextFieldRow(50, "Ayırıcı Tanı:", 50,
_formData.setAyiriciTani, isEmpty),
myTextFieldRow(50, "Kesin Tanı:", 50,
_formData.setKesinTani, isEmpty),
myTextFieldRow(50, "Tedavi Yöntemi:", 100,
_formData.setTedaviYontemi, isEmpty),
const SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
submitButton(context, "Sava as Draft"),
submitButton(context, "GÖNDER"),
],
),
],
),
);
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
})),
);
}
//Kullanıcı kaydet butonuna basarsa local olarak kaydedecek
Container submitButton(BuildContext context, String title) {
return Container(
width: 120,
height: 50,
margin: const EdgeInsets.all(12),
child: TextButton(
onPressed: () {
setState(() {
if (_formKey.currentState!.validate()) {
Provider.of<FormAdd>(context, listen: false)
.addNewFormToList(_formData);
alertDialog(context).then((_) =>_formKey.currentState!.reset());
}
});
},
child: Text(
title,
style: kTextStyle.copyWith(
fontSize: 14, color: Colors.white, fontWeight: FontWeight.bold),
),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(
const Color(0xffffa726),
),
),
),
);
}
//DropDownWidget
Container myDropDownContainer(String initialVal, List<String> listItems,
String text, Function myFunc) {
return Container(
margin: const EdgeInsets.all(8),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
width: 120,
child: Text(
text,
style: kTextStyle,
),
),
const SizedBox(
width: 20,
),
Expanded(
child: Container(
height: 50,
decoration: BoxDecoration(
color: Colors.orangeAccent,
borderRadius: BorderRadius.circular(5)),
child: DropdownButtonFormField<String>(
// autovalidateMode: AutovalidateMode.always,
//menuMaxHeight: 300,
validator: (value) {
if (value!.isEmpty) {
return "485s4a8sd4as85";
}
},
decoration: const InputDecoration(border: InputBorder.none),
isExpanded: true,
//onTap: () => myFunc,
//borderRadius: BorderRadius.circular(5),
value: initialVal,
icon: const Icon(
Icons.arrow_downward,
color: Colors.black38,
),
iconSize: 24,
elevation: 16,
dropdownColor: Colors.deepOrange,
style: kTextStyle.copyWith(color: Colors.black),
onChanged: (val) => myFunc(val),
items: listItems.map<DropdownMenuItem<String>>((String? val) {
return DropdownMenuItem(
//TODO: Set default values
value: val == null ? val = initialVal : val = val,
child: Text(
val,
style: kTextStyle.copyWith(color: Colors.black),
),
);
}).toList(),
),
),
)
],
),
);
}
//TextFieldWidget
Row myTextFieldRow(double height, String text, int? maxLength,
Function function, Function regexFunction) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
SizedBox(
width: 80,
height: height,
child: Text(
text,
style: kTextStyle.copyWith(color: Colors.black54),
),
),
const SizedBox(
width: 10,
),
Expanded(
child: TextFormField(
validator: (value) => regexFunction(value),
// focusNode: FocusNode(),
onChanged: (input) {
function(input);
},
autofocus: false,
textAlignVertical: TextAlignVertical.bottom,
style: kTextStyle.copyWith(fontSize: 16),
maxLength: maxLength,
maxLines: null, //TODO:Arrange maxlines for the inputs
decoration: InputDecoration(
isDense: true,
contentPadding: EdgeInsets.fromLTRB(0, 0, 0, height),
border: const OutlineInputBorder(
borderSide: BorderSide(color: Colors.green),
borderRadius: BorderRadius.all(Radius.circular(3))),
// labelStyle:kTextStyle.copyWith(fontSize: 16, color: Colors.white54),
),
),
),
],
);
}
Future<List<FormContent>> readJsonData() async {
final jsonData =
await rootBundle.rootBundle.loadString('assets/json/formdata.json');
return [
for (final e in json.decode(jsonData)) FormContent.fromJson(e),
];
}
final _formKey = GlobalKey<FormState>();
final FormData _formData = FormData();
String _valueEtkilesim = "Gözlem";
String _valueKapsam = "Öykü";
String _valueOrtam = "Poliklinik";
String _valueDoktor = "Diğer";
String _valueStajTuru = "Ortopedi";
String _valueCinsiyet = "Erkek"; // initial value
final String hintTextCinsiyet = "Cinsiyet:";
final String hintTextStajTuru = "Staj Türü:";
final String hintTextEtkilesim = "Etkileşim Türü:";
final String hintTextKapsam = "Kapsam:";
final String hintTextOrtam = "Gerçekleştiği Ortam:";
final String hintTextDoktor = "Klinik Eğitici:";
void onChangedCinsiyet(String? newVal) {
setState(() {
_valueCinsiyet = newVal!;
_formData.setCinsiyet(_valueCinsiyet);
});
}
void onChangedStajTuru(String newVal) {
setState(() {
_valueStajTuru = newVal;
_formData.setStajTuru(newVal);
});
}
void onChangedEtkilesim(String newVal) {
setState(() {
_valueEtkilesim = newVal;
_formData.setEtkilesimTuru(newVal);
});
}
void onChangedKapsam(String newVal) {
setState(() {
_valueKapsam = newVal;
_formData.setKapsam(newVal);
});
}
void onChangedOrtam(String newVal) {
setState(() {
_valueOrtam = newVal;
_formData.setOrtam(newVal);
});
}
void onChangedDoktor(String newVal) {
setState(() {
_valueDoktor = newVal;
_formData.setDoktor(newVal);
});
}
}
Assume, we saved the form as a draft. Now, It is in the DraftsPage.
Drafts Page
Everything is alright up to now. I have a list of the saved in draft forms. When I click on any list item I could get the form information (3rd picture).
Here is the question.
When I click on the Edit button. I want to renavigate to the Form Page.However this time I want to fill the places with the relevant data. For instance, this time Kayıt No: TextField shouldn't be empty it must be filled with 555, because it was saved to draft. And unfilled places must be blank. I know I need to provide an object of the form(formData) but, I couldn't manage it. When I click on the Edit button. I should provide that object and use it in the Form Page to fill those forms. But, how?
Draft Code
class Drafts extends StatelessWidget {
#override
Widget build(BuildContext context) {
void pushToFormPage(FormData formData) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FormView(
formData: formData,
)));
}
List<FormData> _forms = Provider.of<FormAdd>(context).getForms();
return Scaffold(
backgroundColor: Colors.grey,
appBar: AppBar(
title: const Text("Taslaklar"),
centerTitle: true,
),
body: ListView.separated(
separatorBuilder: (BuildContext context, int index) => const Divider(
height: 5,
color: Colors.blueAccent,
),
itemCount: _forms.length,
itemBuilder: (BuildContext context, int index) {
return studentListViewInstanceContainer(
const Color.fromARGB(200, 200, 130, 240),
pushToFormPage,
_forms[index], context);
},
),
);
}
}
GestureDetector studentListViewInstanceContainer(
Color color, Function function, FormData formData,BuildContext context) {
return GestureDetector(
onTap: () => function(formData),
child: Container(
height: 100,
color: color,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Kayıt No: ${formData.getKayitNo()}',
style: kTextStyle,
),
Text(
'Tarih: ${formData.getKayitNo()}',
style: kTextStyle.copyWith(fontSize: 15),
) //TODO: implement DateT
],
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
ElevatedButton(onPressed: (){
// taslağa kaydedilmiş bilgiler tekrar yüklenecek.
FormData data= Provider.of<FormData>(context,listen: false);
Navigator.push(context, MaterialPageRoute(builder: (context)=>FormPage(formData: data,)));
},
child:const Text('Edit'),
),
const SizedBox(width: 5,),
ElevatedButton(onPressed: (){
},
child: const Text('Delete'),
),
],
),
],
),
),
);
}
This is my FormData model class.
class FormData with ChangeNotifier{
String? _kayitNo;
String? _stajTuru;
String? _doktor; //TODO: Convert string to AttendingPhysician type object
// late AttendingPhysician klinikEgitici;
String? _yas; // TODO: Convert to string later
String? _cinsiyet;
String? _sikayet;
String? _ayiriciTani;
String? _kesinTani;
String? _tedaviYontemi;
String? _etkilesimTuru;
String? _kapsam;
String? _gerceklestigiOrtam;
//DateTime tarih;
String getKayitNo()=>_kayitNo!;
String getStajTuru()=>_stajTuru!;
String getDoktor()=>_doktor!;
String getYas()=>_yas!;
String getCinsiyet()=>_cinsiyet!;
String getSikayet()=>_sikayet!;
String getAyiriciTani()=>_ayiriciTani!;
String getKesinTani()=>_kesinTani!;
String getTedaviYontemi()=>_tedaviYontemi!;
String getEtkilesimTuru()=>_etkilesimTuru!;
String getKapsam()=>_kapsam!;
String getOrtam()=>_gerceklestigiOrtam!;
void setStajTuru(String stajTuru) {
_stajTuru = stajTuru;
notifyListeners();
}
void setCinsiyet(String cinsiyet) {
_cinsiyet = cinsiyet;
notifyListeners();
}
void setEtkilesimTuru(String etkilesim) {
_etkilesimTuru = etkilesim;
notifyListeners();
}
void setKapsam(String kapsam) {
_kapsam = kapsam;
notifyListeners();
}
void setOrtam(String ortam) {
_gerceklestigiOrtam = ortam;
notifyListeners();
}
void setKayitNo(String kayitNo) {
_kayitNo = kayitNo;
notifyListeners();
}
void setYas(String yas) {
_yas = yas;
notifyListeners();
}
void setSikayet(String sikayet) {
_sikayet = sikayet;
notifyListeners();
}
void setAyiriciTani(String ayiriciTani) {
_ayiriciTani = ayiriciTani;
notifyListeners();
}
void setKesinTani(String kesinTani) {
_kesinTani = kesinTani;
notifyListeners();
}
void setTedaviYontemi(String tedaviYontemi) {
_tedaviYontemi = tedaviYontemi;
notifyListeners();
}
void setDoktor(String doktor){
_doktor=doktor;
notifyListeners();
}
}
To make it easier to understand the question. This process is the same as saving mail as a draft. When you want to edit the mail, the unfinished mail will show up not an empty mail.

how to remove widget based on it's index in flutter

I've question about how to close the appropriate widget based on the close button index. here in this image, you can see the output so here I am adding some widget using add button which located inside app bar now i've added close button inside the container when the user pressed the close button it will remove that container.
Here is the image of output :
Here is the code i've tried
class BspUnlicensedSignupPage extends StatefulWidget {
static const String routeName = "/bspUnlicensedSignup";
final BspSignupCommonModel bspSignupCommonModel;
BspUnlicensedSignupPage({
Key key,
#required this.bspSignupCommonModel,
}) : super(key: key);
#override
_BspUnlicensedSignupPageState createState() =>
_BspUnlicensedSignupPageState();
}
class _BspUnlicensedSignupPageState extends State<BspUnlicensedSignupPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
List<Object> images = List<Object>();
Future<File> _imageFile;
bool autovalidate = false;
bool informationislegitimate = false;
DateTime expirydate1 = DateTime.now();
DateTime expirydate2 = DateTime.now();
final format = DateFormat("yyyy-MM-dd");
final format2 = DateFormat("yyyy-MM-dd");
String type2 = 'Passport';
List<String> _type = <String>[
'',
'Passport',
'Driving License',
'Voter ID card',
'Ration Card',
'Aadhar',
'Other Id',
];
String type = 'Passport';
var _myWidgets = List<Widget>();
int _index = 3;
final Map<int, String> identification1Values = Map();
final Map<int, String> documentValues = Map();
final Map<int, DateTime> expiryDateValues = Map();
final Map<int, String> issuingAuthority = Map();
final Map<int, String> identificationPicturesValues = Map();
final List<TextEditingController> _documentControllers = List();
final List<TextEditingController> _issuingauthoritytype = List();
final List<TextEditingController> _expiryDate = List();
final List<TextEditingController> _issuingauthority = List();
final List<List<Object>> _identificationpictures = List();
#override
void initState() {
super.initState();
setState(() {
images.add("Add Image");
images.add("Add Image");
images.add("Add Image");
images.add("Add Image");
images.add("Add Image");
});
}
void _add() {
int keyValue = _index;
_myWidgets = List.from(_myWidgets)
..add(Column(
key: Key("$keyValue"),
children: <Widget>[
SizedBox(height: 10),
Container(
// padding: EdgeInsets.fromLTRB(18,5,18,18),
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 15,
),
],
),
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
Align(
alignment: Alignment.topRight,
child: GestureDetector(
child: Icon(Icons.close),
onTap: () {
print("CLose pressed");
_myWidgets.removeAt(_index);
},
),
),
SizedBox(
height: 10,
),
Column(
children: <Widget>[
SizedBox(
height: 20,
),
_buildidentificationtype1(keyValue),
_builddocumentnumber1(keyValue),
_builddate(keyValue),
_buildissuingauthority1(keyValue),
_buildidentificationpictures(keyValue),
],
),
],
)
],
),
)
],
));
setState(() => ++_index);
}
bool isClicked = false;
Widget _buildidentificationtype1(int keyValue) {
TextEditingController controller = TextEditingController();
_issuingauthoritytype.add(controller);
return FormBuilder(
autovalidate: autovalidate,
child: FormBuilderCustomField(
attribute: "Business type",
validators: [FormBuilderValidators.required()],
formField: FormField(
builder: (FormFieldState<dynamic> field) {
return InputDecorator(
decoration: InputDecoration(
prefixIcon: Icon(Icons.location_on),
labelText: "Business type",
errorText: field.errorText,
),
isEmpty: type == '',
child: new DropdownButtonHideUnderline(
child: new DropdownButton(
value: type,
isDense: true,
onChanged: (String newValue) {
setState(() {
type = controller.text = newValue;
field.didChange(newValue);
});
},
items: _type.map(
(String value) {
return new DropdownMenuItem(
value: value,
child: new Text(value),
);
},
).toList(),
),
),
);
},
)),
);
}
Widget _builddocumentnumber1(int keyValue) {
TextEditingController controller = TextEditingController();
_documentControllers.add(controller);
return new TudoTextWidget(
controller: controller,
prefixIcon: Icon(FontAwesomeIcons.idCard),
labelText: "Document Number",
validator: Validators().validateLicenseno,
onSaved: (val) {
setState(() {
documentValues[keyValue] = val;
});
// _licenseno = val;
},
);
}
Widget _builddate(int keyValue) {
TextEditingController controller = TextEditingController();
_expiryDate.add(controller);
return DateTimeField(
format: format,
autocorrect: true,
autovalidate: autovalidate,
controller: controller,
readOnly: true,
decoration: InputDecoration(
labelText: "Expiry Date",
hintText: "Expiry Date",
prefixIcon: Icon(
FontAwesomeIcons.calendar,
size: 24,
)),
onShowPicker: (context, currentValue) {
return showDatePicker(
context: context,
firstDate: DateTime(1900),
initialDate: currentValue ?? DateTime.now(),
lastDate: DateTime.now());
},
);
}
Widget _buildissuingauthority1(int keyValue) {
TextEditingController controller = TextEditingController();
_issuingauthority.add(controller);
return new TudoTextWidget(
prefixIcon: Icon(FontAwesomeIcons.idCard),
labelText: "Issuing Authority",
validator: (val) => Validators.validateName(val, "Issuing Authority"),
onSaved: (val) {
setState(() {
issuingAuthority[keyValue] = val;
});
},
controller: controller,
);
}
Widget _buildidentificationpictures(int keyValue) {
return GridView.count(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
crossAxisCount: 5,
childAspectRatio: 1,
children: List.generate(images.length, (index) {
if (images[index] is ImageUploadModel) {
ImageUploadModel uploadModel = images[index];
return Card(
clipBehavior: Clip.antiAlias,
child: Stack(
children: <Widget>[
Image.file(
uploadModel.imageFile,
width: 300,
height: 300,
),
Positioned(
right: 5,
top: 5,
child: InkWell(
child: Icon(
Icons.remove_circle,
size: 20,
color: Colors.red,
),
onTap: () {
setState(() {
images.replaceRange(index, index + 1, ['Add Image']);
});
},
),
),
],
),
);
} else {
return Card(
child: IconButton(
icon: Icon(Icons.add),
onPressed: () {
_onAddImageClick(index);
},
),
);
}
}),
);
}
#override
Widget build(BuildContext context) {
final appBar = AppBar(
title: Text("BSP Unlicensed Details"),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
NavigationHelper.navigatetoBack(context);
},
),
actions: <Widget>[IconButton(icon: Icon(Icons.add), onPressed: _add)],
centerTitle: true,
);
final bottomNavigationBar = Container(
color: Colors.transparent,
height: 56,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FlatButton.icon(
icon: Icon(Icons.close),
label: Text('Clear'),
color: Colors.redAccent,
textColor: Colors.black,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () {},
),
new FlatButton.icon(
icon: Icon(FontAwesomeIcons.arrowCircleRight),
label: Text('Next'),
color: colorStyles["primary"],
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
textColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () async {
setState(() {
autovalidate = !autovalidate;
});
if (_formKey.currentState.validate()) {
BspSignupCommonModel model = widget.bspSignupCommonModel;
for (var i = 0; i < _myWidgets.length; i++) {
String document = _documentControllers[i].text;
String issuingAuthorityType = _issuingauthoritytype[i].text;
String expiryDate = _expiryDate[i].text;
String issuingAuthority = _issuingauthority[i].text;
// String picture = _identificationpictures[i].text;
print('Document: $document');
print('IssuingAuthorityType: $issuingAuthorityType');
print('ExpiryDate: $expiryDate');
print('IssuingAuthority: $issuingAuthority');
print('Picture: ${_identificationpictures.length}');
print(_myWidgets.length);
List<Licensed> listOfLicenses = new List<Licensed>();
Licensed licensed = new Licensed(
bspLicenseNumber: document,
bspAuthority: issuingAuthority,
bspExpiryDate: expiryDate,
bspIssuing: issuingAuthorityType);
licensed.bspLicenseNumber = _documentControllers[i].text;
licensed.bspExpiryDate = _expiryDate[i].text;
licensed.bspIssuing = _issuingauthoritytype[i].text;
licensed.bspAuthority = _issuingauthority[i].text;
listOfLicenses.add(licensed);
model.unlicensed = listOfLicenses;
}
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BspLicensedSignupTermsPage(
bspSignupCommonModel: model)));
}
}),
],
),
);
return new Scaffold(
appBar: appBar,
bottomNavigationBar: bottomNavigationBar,
body: Container(
height: double.infinity,
width: double.infinity,
child: Form(
autovalidate: autovalidate,
key: _formKey,
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
Expanded(
child: SizedBox(
child: ListView(
padding: const EdgeInsets.all(18.0),
children: _myWidgets,
),
),
),
],
)
],
)),
),
);
}
}
Edit: It seems that you will need to use a Map instead of a List because if you remove an item the indexes could change.
You could do something like this: int keyValue = ++_lastKey;where _lastKey would be a variable that you increment every time you add an item and it will work as unique identifier
Then instead of _myWidgets.removeAt(keyValue); call this _myWidgetsMap.remove(keyValue) inside a setState(), like this:
setState(() {
_myWidgetsMap.remove(keyValue);
});
And you should call the code inside _add() inside a setState()
But I recommend you to build your widgets according to a list of information, instead of a list of widgets. This way when you change the information, the widgets would adapt correctly.
Maybe you can try to draw or not draw the widget if the button is tapped or not, you can create a var in the code to handle the state of the widget and when you tap the close button change the value of the var, something like this:
bool isClosed = false;
!isClosed?MyWidget():Container()
and in the onTap() of the close button you need to include this:
setState(() {
isClosed = true;
});
I hope this help you
class _MyHomePageState extends State<MyHomePage> {
bool isClosed = false;
void Closed() {
setState(() {
isClosed = true;
});
print("CLick");
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: !isClosed?Center(
child: Container(
width: 200,
height: 200,
decoration: BoxDecoration(
color: Colors.blueAccent,
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
child: Container(
child: Icon(Icons.close),
),
onTap: (){
Closed();
},
),
Text(
'Close Here by Tap',
),
],
),
),
):Container(),
);
}
}
this code works for me
I know I'm late to the party but someone else may find this helpful.
I was faced with exactly the same problem. And I'm proud to say that after a good one hour, I found a solution!
To explain it simply:-
Use a Map<String, Widget> instead of a List (as some have rightly suggested already)
Use UniqueKey().toString() to generate a new key as you dynamically build a widget each time and add this <key, value> pair to the Map
Here's the code snippet
Map<String, MyCard> theCards = {
'0': MyCard(
isInitialCard: true,
)
};
void removeMyCard(String cIndex) {
setState(() {
print(cIndex);
substrateCards.remove(cIndex);
});
}
void addMyCard() {
setState(() {
String id = UniqueKey().toString();
theCards.putIfAbsent(
id,
() => MyCard(
onClose: () {
removeMyCard(id);
},
));
print(id);
});
}
Then, in the ListView.builder, build from the Map, like so:-
ListView.builder(
shrinkWrap: true,
itemCount: theCards.length,
itemBuilder: (BuildContext context, int index) {
String key = theCards.keys.elementAt(index);
return theCards[key];
}),
It will work, whether you delete from the middle, end or wherever.

image picker is not uploading image and it will send the null value to the backend

I've one page on that page there is one add button and it will add the widget as much users want but when I am uploading image it will not upload. and when I press the add button it will already upload the image on the next generated widget. and also it is not giving me the value of that images.
Here is the image it will upload image on the click of the image add button but it's not uploading. and also when I am adding multipart it will send the value as null to the backend side
Here is code i've tried.
class BspUnlicensedSignupPage extends StatefulWidget {
static const String routeName = "/bspUnlicensedSignup";
final BspSignupCommonModel bspSignupCommonModel;
BspUnlicensedSignupPage({
Key key,
#required this.bspSignupCommonModel,
}) : super(key: key);
#override
_BspUnlicensedSignupPageState createState() =>
_BspUnlicensedSignupPageState();
}
class _BspUnlicensedSignupPageState extends State<BspUnlicensedSignupPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
List<Object> images = List<Object>();
Future<File> _imageFile;
bool autovalidate = false;
bool informationislegitimate = false;
DateTime expirydate1 = DateTime.now();
DateTime expirydate2 = DateTime.now();
final format = DateFormat("yyyy-MM-dd");
final format2 = DateFormat("yyyy-MM-dd");
String type2 = 'Passport';
List<String> _type = <String>[
'',
'Passport',
'Driving License',
'Voter ID card',
'Ration Card',
'Aadhar',
'Other Id',
];
String type = 'Passport';
// Map<String, String> _formdata = {};
var _myWidgets = List<Widget>();
int _index = 1;
final Map<int, String> identification1Values = Map();
final Map<int, String> documentValues = Map();
final Map<int, DateTime> expiryDateValues = Map();
final Map<int, String> issuingAuthority = Map();
final Map<int, String> identificationPicturesValues = Map();
final List<TextEditingController> _documentControllers = List();
final List<TextEditingController> _issuingauthoritytype = List();
final List<TextEditingController> _expiryDate = List();
final List<TextEditingController> _issuingauthority = List();
final List<List<Object>> _identificationpictures = List();
#override
void initState() {
super.initState();
setState(() {
images.add("Add Image");
images.add("Add Image");
images.add("Add Image");
images.add("Add Image");
images.add("Add Image");
});
}
void _add() {
// TextEditingController controller = TextEditingController();
setState(() {
int keyValue = _myWidgets.length;
_myWidgets = List.from(_myWidgets)
..add(Column(
key: Key("$keyValue"),
children: <Widget>[
SizedBox(height: 10),
Container(
// padding: EdgeInsets.fromLTRB(18,5,18,18),
padding: EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 15,
),
],
),
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
Align(
alignment: Alignment.topRight,
child: GestureDetector(
child: Icon(Icons.close),
onTap: () {
print("CLose pressed");
setState(() {
_myWidgets = List.from(_myWidgets)
..removeAt(keyValue);
});
},
),
),
SizedBox(
height: 10,
),
Column(
children: <Widget>[
SizedBox(
height: 20,
),
_buildidentificationtype1(keyValue),
_builddocumentnumber1(keyValue),
_builddate(keyValue),
_buildissuingauthority1(keyValue),
_buildidentificationpictures(keyValue),
],
),
],
)
],
),
)
],
));
});
}
bool isClicked = false;
Widget _buildidentificationtype1(int keyValue) {
TextEditingController controller = TextEditingController();
_issuingauthoritytype.add(controller);
return FormBuilder(
autovalidate: autovalidate,
child: FormBuilderCustomField(
attribute: "Business type",
validators: [FormBuilderValidators.required()],
formField: FormField(
builder: (FormFieldState<dynamic> field) {
return InputDecorator(
decoration: InputDecoration(
prefixIcon: Icon(Icons.location_on),
labelText : 'Business type',
),
isEmpty: type == '',
child: new DropdownButtonHideUnderline(
child: new DropdownButton(
value: type,
isDense: true,
onChanged: (String newValue) {
setState(() {
type = controller.text = newValue;
field.didChange(newValue);
});
},
items: _type.map(
(String value) {
return new DropdownMenuItem(
value: value,
child: new Text(value),
);
},
).toList(),
),
),
);
},
)),
);
}
Widget _builddocumentnumber1(int keyValue) {
TextEditingController controller = TextEditingController();
_documentControllers.add(controller);
return new TudoTextWidget(
controller: controller,
prefixIcon: Icon(FontAwesomeIcons.idCard),
labelText : 'Document Number'
onSaved: (val) {
setState(() {
documentValues[keyValue] = val;
});
// _licenseno = val;
},
);
}
Widget _builddate(int keyValue) {
TextEditingController controller = TextEditingController();
_expiryDate.add(controller);
return DateTimeField(
format: format,
autocorrect: true,
autovalidate: autovalidate,
controller: controller,
readOnly: true,
// validator: (date) => date == null ? 'Please enter valid date' : null,
decoration: InputDecoration(
labelText: "Expiry Date",
hintText: "Expiry Date",
prefixIcon: Icon(
FontAwesomeIcons.calendar,
size: 24,
)),
onShowPicker: (context, currentValue) {
return showDatePicker(
context: context,
firstDate: DateTime.now(),
initialDate: currentValue ?? DateTime.now(),
lastDate: DateTime(2100));
},
);
}
Widget _buildissuingauthority1(int keyValue) {
TextEditingController controller = TextEditingController();
_issuingauthority.add(controller);
return new TudoTextWidget(
prefixIcon: Icon(FontAwesomeIcons.idCard),
labelText: 'Issuning authority',
validator: (val) => Validators.validateName(val, "Issuing Authority"),
onSaved: (val) {
setState(() {
issuingAuthority[keyValue] = val;
});
// _illusingauthority = issuingAuthority[keyValue] = val;
},
controller: controller,
);
}
Widget _buildidentificationpictures(int keyValue) {
return GridView.count(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
crossAxisCount: 5,
childAspectRatio: 1,
children: List.generate(images.length, (index) {
if (images[index] is ImageUploadModel) {
ImageUploadModel uploadModel = images[index];
return Card(
clipBehavior: Clip.antiAlias,
child: Stack(
children: <Widget>[
Image.file(
uploadModel.imageFile,
width: 300,
height: 300,
),
Positioned(
right: 5,
top: 5,
child: InkWell(
child: Icon(
Icons.remove_circle,
size: 20,
color: Colors.red,
),
onTap: () {
setState(() {
images.replaceRange(index, index + 1, ['Add Image']);
_identificationpictures.add(images);
});
},
),
),
],
),
);
} else {
return Card(
child: IconButton(
icon: Icon(Icons.add),
onPressed: () {
_onAddImageClick(index);
},
),
);
}
}),
);
}
Future _onAddImageClick(int index) async {
setState(() {
_imageFile = ImagePicker.pickImage(source: ImageSource.gallery);
getFileImage(index);
});
}
void getFileImage(int index) async {
// var dir = await path_provider.getTemporaryDirectory();
_imageFile.then((file) async {
setState(() {
ImageUploadModel imageUpload = new ImageUploadModel();
imageUpload.isUploaded = false;
imageUpload.uploading = false;
imageUpload.imageFile = file;
imageUpload.imageUrl = '';
images.replaceRange(index, index + 1, [imageUpload]);
});
});
}
Widget _buildinformationislegitmate() {
return TudoConditionWidget(
text:
"Above entered Identity information is legitimate and accurate to my knowledge",
);
}
#override
Widget build(BuildContext context) {
final appBar = AppBar(
title: Text("BSP Unlicensed Details"),
leading: IconButton(
icon: Icon(Icons.arrow_back_ios),
onPressed: () {
NavigationHelper.navigatetoBack(context);
},
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.add),
onPressed: () {
setState(() {
_add();
});
},
),
],
centerTitle: true,
);
final bottomNavigationBar = Container(
color: Colors.transparent,
height: 56,
//margin: EdgeInsets.symmetric(vertical: 24, horizontal: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
new FlatButton.icon(
icon: Icon(Icons.close),
label: Text('Clear'),
color: Colors.redAccent,
textColor: Colors.black,
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () {},
),
new FlatButton.icon(
icon: Icon(FontAwesomeIcons.arrowCircleRight),
label: Text('Next'),
color: colorStyles["primary"],
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 30),
textColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(7),
),
onPressed: () async {
setState(() {
autovalidate = !autovalidate;
});
if (_formKey.currentState.validate()) {
List<Licensed> listOfLicenses = new List<Licensed>();
BspSignupCommonModel model = widget.bspSignupCommonModel;
for (var i = 0; i < _myWidgets.length; i++) {
String document = _documentControllers[i].text;
String issuingAuthorityType = _issuingauthoritytype[i].text;
String expiryDate = _expiryDate[i].text;
String issuingAuthority = _issuingauthority[i].text;
// String picture = _identificationpictures[i].text;
print('Document: $document');
print('IssuingAuthorityType: $issuingAuthorityType');
print('ExpiryDate: $expiryDate');
print('IssuingAuthority: $issuingAuthority');
print('Picture: ${_identificationpictures.length}');
print(_myWidgets.length);
Licensed licensed = new Licensed(
bspLicenseNumber: document,
bspAuthority: issuingAuthority,
bspExpiryDate: expiryDate,
bspIssuing: issuingAuthorityType,
);
licensed.bspLicenseNumber = _documentControllers[i].text;
licensed.bspExpiryDate = _expiryDate[i].text;
licensed.bspIssuing = _issuingauthoritytype[i].text;
licensed.bspAuthority = _issuingauthority[i].text;
listOfLicenses.add(licensed);
}
model.unlicensed = listOfLicenses;
print(model.toJson());
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BspLicensedSignupTermsPage(
bspSignupCommonModel: model),
));
}
}),
],
),
);
return new Scaffold(
appBar: appBar,
bottomNavigationBar: bottomNavigationBar,
floatingActionButton: new FloatingActionButton.extended(
onPressed: () {
_add();
},
label: Text(
"Add License",
style: TextStyle(color: Colors.white, fontSize: 16),
),
icon: Icon(
Icons.add,
size: 28,
color: Colors.white,
),
),
body: Container(
height: double.infinity,
width: double.infinity,
child: Form(
autovalidate: autovalidate,
key: _formKey,
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
Expanded(
child: SizedBox(
child: ListView(
padding: const EdgeInsets.all(18.0),
children: _myWidgets,
),
),
),
_buildinformationislegitmate(),
],
)
],
)),
),
);
}
}
The problem seems to be that you are using the same list images for every item, you could try to use a Map that has the lists for each item.
I suggest you to try to create a separate widget with the entire card so it can handle it's own state.