UI changes only when I hot reload the app - flutter

I'm trying to get data from bottomsheet to model then I show them in listview.
I can see everything works but when I trying to add new data it does not show first and then if I hot reload the app I can add data and see it listed but for new data I must hot reload again and again I couldn't find the solution
bool? isChecked = false;
final boy = TextEditingController();
final adet = TextEditingController();
final kilo = TextEditingController();
final firma = TextEditingController();
final kalit = TextEditingController();
final kalinlik = TextEditingController();
final en = TextEditingController();
class MyHomePage extends ConsumerWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
Widget build(BuildContext context, WidgetRef ref) {
var size = MediaQuery.of(context).size;
final productInfoProvider = ref.watch(productProvider);
ProductInfos product1 = ProductInfos(
firmAdi: firma.text,
kalite: kalit.text,
kalinlik: kalinlik.text,
en: en.text,
boy: boy.text,
adet: adet.text,
kilo: kilo.text,
pvc: isChecked);
ProductInfos product2 = ProductInfos(
firmAdi: "firma.text",
kalite: "kalit.text",
kalinlik: "kalinlik.text",
en: "en.text",
boy: "boy.text",
adet: "adet.text",
kilo: "kilo.text",
pvc: true);
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Column(
children: [
SizedBox(
height: size.height * 0.5,
child: ListView.separated(
separatorBuilder: (context, index) {
return const Divider(
height: 5,
thickness: 2,
);
},
itemCount: productInfoProvider.products.length,
itemBuilder: (BuildContext context, int index) {
return Container(
color: Colors.white,
child: ListTile(
leading: Text(productInfoProvider
.products[index].firmAdi
.toString()),
subtitle: Text(
productInfoProvider.products[index].en.toString()),
title: Text(
"${productInfoProvider.products[index].kalite.toString()} kalite"),
),
);
},
)),
Center(
child: SizedBox(
height: size.height * 0.2,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.black),
onPressed: () {
print(productInfoProvider.products.length);
showModalBottomSheet(
context: context,
builder: ((context) {
return StatefulBuilder(
//bottomSheet de statefulbuilder kullanmazsak state yenilenmiyor
builder: (BuildContext context,
StateSetter myState) {
return SizedBox(
height: size.height * 0.5,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
solBottomSheet(size, myState),
sagBottomSheet(size, myState),
],
),
Align(
alignment: Alignment.bottomCenter,
child: ElevatedButton(
onPressed: (() {
myState(() {
ref
.read(productProvider)
.addProduct(product1);
inspect(productInfoProvider
.products);
});
}),
style: ElevatedButton.styleFrom(
backgroundColor:
const Color(0xff74a6cc)),
child: const Text("ekle"),
)),
],
),
),
);
});
}));
},
child: const Text("BottomSheet"),
)),
),
],
));
}
SizedBox sagBottomSheet(Size size, StateSetter myState) {
return SizedBox(
width: size.width * 0.4,
child: Column(
children: [
CustomTextField(
textController: boy,
t: TextInputType.number,
hintText: "Boy",
ic: const Icon(Icons.numbers_outlined),
dgonly: [FilteringTextInputFormatter.digitsOnly],
),
space(),
CustomTextField(
textController: adet,
t: TextInputType.number,
hintText: "Adet",
ic: const Icon(Icons.numbers_outlined),
dgonly: [FilteringTextInputFormatter.digitsOnly],
),
space(),
CustomTextField(
textController: kilo,
t: TextInputType.number,
hintText: "Kilo",
ic: const Icon(Icons.numbers_outlined),
dgonly: [FilteringTextInputFormatter.digitsOnly],
),
space(),
CheckboxListTile(
title: const Text(
"PVC",
style: TextStyle(
color: Color(0xff74a6cc), fontWeight: FontWeight.w800),
),
value: isChecked,
onChanged: ((value) {
myState(
() {
isChecked = value;
},
);
})),
],
));
}
SizedBox solBottomSheet(Size size, StateSetter myState) {
return SizedBox(
width: size.width * 0.4,
child: Column(
children: [
CustomTextField(
textController: firma,
t: TextInputType.name,
hintText: "Firma",
ic: const Icon(Icons.home),
),
space(),
CustomTextField(
textController: kalit,
t: TextInputType.number,
hintText: "Kalite",
ic: const Icon(Icons.high_quality_outlined),
dgonly: [FilteringTextInputFormatter.digitsOnly],
),
space(),
CustomTextField(
textController: kalinlik,
t: TextInputType.number,
hintText: "Kalınlık",
ic: const Icon(Icons.high_quality_outlined),
dgonly: [FilteringTextInputFormatter.digitsOnly],
),
space(),
CustomTextField(
textController: en,
t: TextInputType.number,
hintText: "En",
ic: const Icon(Icons.numbers_outlined),
dgonly: [FilteringTextInputFormatter.digitsOnly],
),
],
),
);
}
SizedBox space() {
return const SizedBox(
height: 10,
);
}
}
//this is provider code
class ProductInfoRepo extends ChangeNotifier {
List<ProductInfos> products = [
ProductInfos(
firmAdi: "firm1",
kalite: "430",
kalinlik: "0.50",
en: "750",
boy: "1000",
adet: "500",
kilo: "1500",
pvc: true),
];
addProduct(ProductInfos product) {
products.add(product);
notifyListeners();
}
}
final productProvider = ChangeNotifierProvider((((ref) {
return ProductInfoRepo();
})));

I modified your code a little bit, and it worked.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
void main() => runApp(const ProviderScope(
child: MaterialApp(home: MyHomePage(title: 'MyHomePage'))));
class MyHomePage extends ConsumerStatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
#override
ConsumerState<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends ConsumerState<MyHomePage> {
bool isChecked = false;
final boy = TextEditingController();
final adet = TextEditingController();
final kilo = TextEditingController();
final firma = TextEditingController();
final kalit = TextEditingController();
final kalinlik = TextEditingController();
final en = TextEditingController();
#override
void dispose() {
boy.dispose();
adet.dispose();
kilo.dispose();
firma.dispose();
kalit.dispose();
kalinlik.dispose();
en.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
var size = MediaQuery.of(context).size;
final productInfoProvider = ref.watch(productProvider);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children: [
SizedBox(
height: size.height * 0.5,
child: ListView.separated(
separatorBuilder: (context, index) {
return const Divider(
height: 5,
thickness: 2,
);
},
itemCount: productInfoProvider.products.length,
itemBuilder: (BuildContext context, int index) {
return Container(
color: Colors.white,
child: ListTile(
leading: Text(
productInfoProvider.products[index].firmAdi.toString()),
subtitle:
Text(productInfoProvider.products[index].en.toString()),
title: Text(
"${productInfoProvider.products[index].kalite.toString()} kalite"),
),
);
},
),
),
Center(
child: SizedBox(
height: size.height * 0.2,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white,
foregroundColor: Colors.black),
onPressed: () {
print(productInfoProvider.products.length);
showModalBottomSheet(
context: context,
builder: ((context) {
return StatefulBuilder(
builder: (BuildContext context, StateSetter myState) {
return SizedBox(
height: size.height * 0.5,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
solBottomSheet(size, myState),
sagBottomSheet(size, myState),
],
),
Align(
alignment: Alignment.bottomCenter,
child: ElevatedButton(
onPressed: (() {
final newProduct = createNewProduct();
ref
.read(productProvider)
.addProduct(newProduct);
inspect(productInfoProvider.products);
}),
style: ElevatedButton.styleFrom(
backgroundColor:
const Color(0xff74a6cc)),
child: const Text("ekle"),
),
),
],
),
),
);
});
}),
);
},
child: const Text("BottomSheet"),
),
),
),
],
),
);
}
createNewProduct() {
return ProductInfos(
firmAdi: firma.text,
kalite: kalit.text,
kalinlik: kalinlik.text,
en: en.text,
boy: boy.text,
adet: adet.text,
kilo: kilo.text,
pvc: isChecked,
);
}
SizedBox sagBottomSheet(Size size, StateSetter myState) {
print('build sagBottomSheet');
return SizedBox(
width: size.width * 0.4,
child: Column(
children: [
CustomTextField(
textController: boy,
t: TextInputType.number,
hintText: "Boy",
ic: const Icon(Icons.numbers_outlined),
dgonly: true,
),
space(),
CustomTextField(
textController: adet,
t: TextInputType.number,
hintText: "Adet",
ic: const Icon(Icons.numbers_outlined),
dgonly: true,
),
space(),
CustomTextField(
textController: kilo,
t: TextInputType.number,
hintText: "Kilo",
ic: const Icon(Icons.numbers_outlined),
dgonly: true,
),
space(),
CheckboxListTile(
title: const Text(
"PVC",
style: TextStyle(
color: Color(0xff74a6cc), fontWeight: FontWeight.w800),
),
value: isChecked,
onChanged: ((value) {
myState(() {
isChecked = value ?? false;
});
}),
),
],
),
);
}
SizedBox solBottomSheet(Size size, StateSetter myState) {
return SizedBox(
width: size.width * 0.4,
child: Column(
children: [
CustomTextField(
textController: firma,
t: TextInputType.name,
hintText: "Firma",
ic: const Icon(Icons.home),
),
space(),
CustomTextField(
textController: kalit,
t: TextInputType.number,
hintText: "Kalite",
ic: const Icon(Icons.high_quality_outlined),
dgonly: true,
),
space(),
CustomTextField(
textController: kalinlik,
t: TextInputType.number,
hintText: "Kalınlık",
ic: const Icon(Icons.high_quality_outlined),
dgonly: true,
),
space(),
CustomTextField(
textController: en,
t: TextInputType.number,
hintText: "En",
ic: const Icon(Icons.numbers_outlined),
dgonly: true,
),
],
),
);
}
SizedBox space() {
return const SizedBox(
height: 10,
);
}
}
class CustomTextField extends ConsumerWidget {
const CustomTextField({
required this.textController,
required this.t,
required this.hintText,
required this.ic,
this.dgonly,
Key? key,
}) : super(key: key);
final TextEditingController textController;
final TextInputType t;
final String hintText;
final Icon ic;
final bool? dgonly;
#override
Widget build(BuildContext context, WidgetRef ref) {
return TextField(
decoration: InputDecoration(
hintText: hintText,
icon: ic,
),
controller: textController,
keyboardType: TextInputType.number,
inputFormatters:
dgonly ?? false ? [FilteringTextInputFormatter.digitsOnly] : null,
);
}
}
Models and provider like this:
final productProvider = ChangeNotifierProvider((ref) {
return ProductInfoRepo();
});
class ProductInfoRepo extends ChangeNotifier {
List<ProductInfos> products = [
ProductInfos(
firmAdi: "firm1",
kalite: "430",
kalinlik: "0.50",
en: "750",
boy: "1000",
adet: "500",
kilo: "1500",
pvc: true,
),
];
addProduct(ProductInfos product) {
products.add(product);
notifyListeners();
}
}
class ProductInfos {
ProductInfos({
required this.firmAdi,
required this.kalite,
required this.kalinlik,
required this.en,
required this.boy,
required this.adet,
required this.kilo,
required this.pvc,
});
final String firmAdi;
final String kalite;
final String kalinlik;
final String en;
final String boy;
final String adet;
final String kilo;
final bool pvc;
}
This example is fully functional, you can copy and run it on your device.
The basic idea behind the modifications was:
Creating a createNewProduct() function that creates a new object every time the user clicks the add button.
Also, you need to use ConsumerStatefulWidget to be able to recycle all TextEditingController(), otherwise it will cause a memory leak.
Bonus: use widget decomposition to make the code readable. It also helps to rearrange only the right elements during state changes.

Related

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

StateProvider not updating with correct values

I've been trying to use riverpod for state management in a POC app, I have a screen with a text editing controller, I'm trying to check whether the text is empty for a textfield and enable/disable a button based on that logic. There seems to be an issue with my code since the button seems to always display the opposite status of what I'm trying to do. How can I fix this issue ?
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:to_do_list/models/task.dart';
import 'package:to_do_list/reusable_widgets/app_bar.dart';
class TaskScreen extends ConsumerStatefulWidget {
const TaskScreen({Key? key}) : super(key: key);
#override
TaskScreenState createState() => TaskScreenState();
}
class TaskScreenState extends ConsumerState<TaskScreen> {
DateTime? _selectedDate;
static final TextEditingController _titleController = TextEditingController();
final TextEditingController _descriptionController = TextEditingController();
var textProvider = StateProvider((_) => _titleController.text.isNotEmpty);
#override
Widget build(BuildContext context) {
bool value = ref.watch(textProvider);
return Scaffold(
resizeToAvoidBottomInset: false,
appBar: const AppBarWrapper(
title: "To-Do List",
),
body: Stack(children: [
Padding(
padding: const EdgeInsets.all(14.0),
child: Column(
children: [
TextField(
controller: _titleController,
onChanged: (val) {
ref.read(textProvider.state).state = val.isEmpty;
},
autofocus: true,
decoration: const InputDecoration(hintText: "Title"),
),
const SizedBox(
height: 14,
),
TextField(
controller: _descriptionController,
decoration: const InputDecoration(hintText: "Description"),
minLines: 3,
maxLines: 5,
),
SizedBox(
width: 150,
child: ListTile(
leading: const Icon(Icons.calendar_today),
title: Text(
_selectedDate?.toString() ?? 'No Date',
),
onTap: () async {
_selectedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime.now(),
lastDate: DateTime.now().add(const Duration(days: 365)),
);
},
),
),
],
),
),
Positioned(
bottom: MediaQuery.of(context).viewInsets.bottom,
left: 0,
right: 0,
child: ElevatedButton(
onPressed: value
? () {
Navigator.of(context).pop(Task(
title: _titleController.text,
description: _descriptionController.text.isNotEmpty
? _descriptionController.text
: null,
date: _selectedDate));
}
: null,
child: const Icon(
Icons.done,
),
),
),
]),
);
}
}
Try to change ref.read(textProvider.state).state = val.isEmpty; to ref.read(textProvider.notifier).state = val.isEmpty;. It should help you

Main page data is resetting automatically Flutter after user come back to second page to first page

I'm stuck with one issue where once I click on the upload image button it will take me to the next page wherein upload image class I will choose the images from the camera or Gallery and I will click on done. In the done button I have written navigation. pop(context) to the first page but when I come back all the text field data is cleared whatever I have entered and again I need to enter it back which is quite frustrating all the time...PLease help me to solve the issue or show the way how to save the data?
import '../blocs/addtrip_bloc.dart';
import '../widgets/customtextformfield.dart';
import 'CameraWidgetState.dart';
class DataInfoPage extends StatefulWidget {
const DataInfoPage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<DataInfoPage> {
_DataInfoPageState createState() => _DataInfoPageState();
String date = "";
final FirebaseAuth _auth = FirebaseAuth.instance;
User? user;
var name = '';
#override
void initState() {
super.initState();
initUser();
savedata();
}
initUser() async {
user = (await _auth.currentUser!);
name = user!.displayName!;
setState(() {});
}
void savedata() {
final absavedata = context.read<AddTripBloc>();
}
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
DateTime selectedDate = DateTime.now();
TextEditingController tripname = new TextEditingController();
TextEditingController sdate = new TextEditingController();
DateTimeRange? _selectedDateRange;
String _displayText(String begin, DateTime? date) {
if (date != null) {
return '$begin Date: ${date.toString().split(' ')[0]}';
} else {
return 'Press the button to show the picker';
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
body: SingleChildScrollView(
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(
height: 20,
),
Text(
"Please enter trip details ",
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 25,
color: Colors.black),
),
SizedBox(
height: 20,
),
Padding(
padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
customfields(
hintValue: ' Enter Trip Name',
labeltxt: 'Trip Name *',
keyboardtype: TextInputType.text,
text: tripname),
SizedBox(
height: 20,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
FloatingActionButton(
onPressed: _show,
child: const Icon(Icons.date_range),
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.circular(5))),
Column(
children: [
Text(
_displayText(
'Start', _selectedDateRange?.start),
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
color: Colors.black),
),
SizedBox(
height: 20,
),
ElevatedButton(
onPressed: () async {
await Navigator.of(context).push(MaterialPageRoute(
builder: (context) => CameraWidget()));
// Navigator.push(
// context,
// MaterialPageRoute(
// builder: (context) => CameraWidget()));
},
child: const Text("Upload Images of Trip *"),
),
SizedBox(
height: 35,
),
],
)),
],
),
),
));
}
void _show() async {
final DateTimeRange? result = await showDateRangePicker(
context: context,
firstDate: DateTime(2022, 1, 1),
lastDate: DateTime(2030, 12, 31),
currentDate: DateTime.now(),
saveText: 'Done.',
);
if (result != null) {
// Rebuild the UI
print(result.start.toString());
setState(() {
_selectedDateRange = result;
});
}
}
}
Custom Flied Code
class customfields extends StatelessWidget {
customfields(
{required this.hintValue,
this.labeltxt = "",
required this.keyboardtype,
this.maxvalue = 1,
required this.text});
String hintValue;
String labeltxt;
int maxvalue;
TextInputType keyboardtype;
TextEditingController text;
bool _validate = false;
#override
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
TextFormField(
controller: text,
cursorColor: Colors.black,
//textAlign: TextAlign.left,
// autocorrect: true,
// textInputAction: TextInputAction.go,
// maxLines: maxvalue,
// autofocus: true,
style: TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
textAlignVertical: TextAlignVertical.center,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(
RegExp('[a-z A-Z 0-9 #?!#%^&* ? # ^_ . : ! | "" \'-]')),
],
decoration: InputDecoration(
border: new UnderlineInputBorder(
// borderRadius: new BorderRadius.circular(5.0),
// borderSide: new BorderSide(),
),
hintText: hintValue,
labelText: labeltxt,
errorText: _validate ? 'Value Can\'t Be Empty' : null,
//prefixIcon: Icon(Icons),
//suffixIcon: Icon(Icons.currency_bitcoin)
),
keyboardType: keyboardtype,
validator: (value) {
if (value!.isEmpty) {
return "Value Can't be empty.";
}
return null;
},
),
],
),
);
}
}

Search Result does not update instantly flutter

I'm emulating this search and filter github here and the codes are almost the same but the filtered results do not update instantly while I type and also I faced the following issues:
I will have to press enter on my laptop to finally get the filtered list
When I hit the close icon(which is to clear all the words), I will have to tap the searchbar again so that all my listtile are back on the listview.
Here's my code:
class _CurrencySelectState extends State<CurrencySelect> {
late List<Currency> resCur;
String query = '';
#override
void initState() {
super.initState();
resCur = currencyList;
}
void searchCur(String query) {
final List<Currency> filteredCur = currencyList.where((cur) {
final symbolLower = cur.symbol.toLowerCase(); // Search using symbol
final nameLower = cur.country.toLowerCase(); // Search using country
final searchLower = query.toLowerCase();
return symbolLower.contains(searchLower) ||
nameLower.contains(searchLower);
}).toList();
setState(() {
this.query = query;
resCur = filteredCur;
});
}
#override
Widget build(BuildContext context) {
Widget buildCur(Currency cur) => ListTile(
leading: Padding(
padding: EdgeInset.all(5)
child: SizedBox(
child: Column(
children: <Widget>[
SvgPicture.asset(
cur.assetPath,
),
]),
),
),
title: Column(
children: [
Text(
cur.symbol,
style: TextStyle(
...
),
Text(
cur.name,
style: TextStyle(
...
),
],
),
trailing: Text(
"0.25",
style: TextStyle(
...
),
);
return TextButton(
onPressed: () async {
showModalBottomSheet(
enableDrag: false,
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return DraggableScrollableSheet(
expand: false,
builder: (context, scrollController) {
return Column(
children: <Widget>[
SearchWidget(
text: query,
onChanged: searchCur,
hintText: "Enter symbol or country"
),
Expanded(
child: ListView.builder(
controller: scrollController,
itemCount: resCur.length,
itemBuilder: (context, int index) {
final cur = resCur[index];
return buildCur(cur);
},
),
)
],
);
},
);
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
...
),
SvgPicture.asset(
...
)
],
));
}
}
Searchwidget code:
import 'package:flutter/material.dart';
class SearchWidget extends StatefulWidget {
final String text;
final ValueChanged<String> onChanged;
final String hintText;
const SearchWidget({
Key? key,
required this.text,
required this.onChanged,
required this.hintText,
}) : super(key: key);
#override
_SearchWidgetState createState() => _SearchWidgetState();
}
class _SearchWidgetState extends State<SearchWidget> {
final controller = TextEditingController();
#override
Widget build(BuildContext context) {
final styleActive = TextStyle(color: Colors.black);
final styleHint = TextStyle(color: Colors.black54);
final style = widget.text.isEmpty ? styleHint : styleActive;
return Container(
height: 42,
margin: const EdgeInsets.fromLTRB(16, 16, 16, 16),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.white,
border: Border.all(color: Colors.black26),
),
padding: const EdgeInsets.symmetric(horizontal: 8),
child: TextField(
controller: controller,
decoration: InputDecoration(
icon: Icon(Icons.search, color: style.color),
suffixIcon: widget.text.isNotEmpty
? GestureDetector(
child: Icon(Icons.close, color: style.color),
onTap: () {
controller.clear();
widget.onChanged('');
FocusScope.of(context).requestFocus(FocusNode());
},
)
: null,
hintText: widget.hintText,
hintStyle: style,
border: InputBorder.none,
),
style: style,
onChanged: widget.onChanged,
),
);
}
}

How to print multiple values from dynamic list in ... loop

Hy, im working on a project, using table_calendar plugin im receiving meetings record from database and showing them as a marker on the calendar, now i want to show that meeting details on the page in a list, but i don't have any idea how to do that, i able to print it meeting ids in the list, but how i can print other meetings details also, like location, time, customer name etc?
import 'package:flutter/material.dart';
import 'package:hexcolor/hexcolor.dart';
import 'package:intl/intl.dart';
import 'notifcation_dialog.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:table_calendar/table_calendar.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'variables.dart';
import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
import 'package:expandable/expandable.dart';
TextEditingController agendaController = new TextEditingController();
TextEditingController phoneController = new TextEditingController();
TextEditingController locationController = new TextEditingController();
meeting mee = new meeting();
meeting meet = new meeting();
String _time = '';
List meetDate;
CalendarController _controller;
String dateWithT;
String emailFromSharedPref, meetingAdded;
Map smData;
Map meetingDetail;
List meetingInfo, customerInfo;
List<dynamic> _selectedEvents;
Map<DateTime, List<dynamic>> _events;
Map<DateTime, List<dynamic>> c_nameMap;
class AddMeeting extends StatefulWidget {
#override
_AddMeetingeState createState() => _AddMeetingeState();
}
class _AddMeetingeState extends State<AddMeeting> {
#override
void initState() {
super.initState();
_controller = CalendarController();
_events = {};
_selectedEvents = [];
c_nameMap = {};
getIdAndGetMeetings();
}
Future getIdAndGetMeetings() async {
// getting email from SharedPreferences
SharedPreferences prefs = await SharedPreferences.getInstance();
emailFromSharedPref = prefs.getString('email');
// getting saleManager info using email
var url = "http://.../getSaleMangerDetal.php";
var response = await http.post(url, body: {
"m_email": emailFromSharedPref,
});
smData = jsonDecode(response.body);
// getting customer info using saleManagerID
var customerInfoUrl = "http://.../calender/getCustomerInfo.php";
var customerInfoResponse = await http.post(customerInfoUrl, body: {
"sm_id": smData['manager_id'],
});
// saving customer info in the list, because there are many dict in the list
customerInfo = jsonDecode(customerInfoResponse.body);
// get meetings details from server using saleManager Id
var getmeetingUrl = "http://.../getMeetings.php";
var getMeetiongResponse = await http.post(getmeetingUrl, body: {
"manager_id": smData['manager_id'],
});
meetingInfo = jsonDecode(getMeetiongResponse.body);
print(meetingInfo);
for (int i = 0; i < meetingInfo.length; i++) {
dateWithT =
meetingInfo[i]['m_date'].replaceAll(new RegExp(r'[^\w\s]+'), '');
dateWithT = dateWithT.replaceAll(' ', '');
dateWithT = dateWithT + "000000";
dateWithT = dateWithT.substring(0, 8) + 'T' + dateWithT.substring(8);
mee.dateTime = DateTime.parse(dateWithT);
if (_events[mee.dateTime] != null) {
_events[mee.dateTime].add(meetingInfo[i]['meeting_id']);
} else {
_events[mee.dateTime] = [meetingInfo[i]['meeting_id']];
}
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flutter Calendar'),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Text(_time),
TableCalendar(
events: _events,
// initialCalendarFormat: CalendarFormat.week,
calendarStyle: CalendarStyle(
canEventMarkersOverflow: true,
todayColor: Colors.orange,
selectedColor: Theme.of(context).primaryColor,
todayStyle: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 18.0,
color: Colors.white)),
headerStyle: HeaderStyle(
centerHeaderTitle: true,
formatButtonDecoration: BoxDecoration(
color: Colors.orange,
borderRadius: BorderRadius.circular(20.0),
),
formatButtonTextStyle: TextStyle(color: Colors.white),
formatButtonShowsNext: false,
),
startingDayOfWeek: StartingDayOfWeek.monday,
onDaySelected: (date, events) {
setState(() {
_selectedEvents = events;
});
},
builders: CalendarBuilders(
calendarController: _controller,
),
..._selectedEvents.map((event) => Stack(
children: <Widget>[
// getMeetingsIndividualDetails(event),
Card(
child: Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ExpandablePanel(
header: Text(event),
// Text(getMeetingsIndividualDetails(event, 'c_name')),
collapsed: Text(
'qwe',
softWrap: true,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
expanded: Text(
'as',
softWrap: true,
),
tapHeaderToExpand: true,
hasIcon: true,
),
),
),
),
],
)),
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: _showAddDialog,
),
);
}
_showAddDialog() async {
await showDialog(
context: context,
builder: (context) => StatefulBuilder(builder: (context, setState) {
return ShowDialog();
}));
setState(() {
_selectedEvents = _events[mee.dateTime];
});
}
}
addMeetingToDatabase(List meetDate2) async {
// print(meetDate2[0]);
var getmeetingUrls = "http://.../addMeeting.php";
var getMeetiongResponses = await http.post(getmeetingUrls, body: {
"sm_id": smData['manager_id'],
"c_id": '2',
"agenda": agendaController.text,
"phone": phoneController.text,
"location": locationController.text,
"date": meetDate2[0],
"time": _time,
});
meetingAdded = jsonDecode(getMeetiongResponses.body);
print(meetingAdded);
}
class ShowDialog extends StatefulWidget {
#override
_ShowDialogState createState() => _ShowDialogState();
}
class _ShowDialogState extends State<ShowDialog> {
#override
Widget build(BuildContext context) {
return AlertDialog(
content: SingleChildScrollView(
child: Container(
// height: MediaQuery.of(context).size.height/3,
child: Column(
// mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 8),
child: TextField(
controller: phoneController,
// maxLines: null,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Phone',
prefixIcon: Icon(Icons.phone_android),
labelStyle: TextStyle(fontSize: 15)),
),
),
Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 8),
child: TextField(
controller: locationController,
// maxLines: null,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Meeting Location',
prefixIcon: Icon(Icons.add_location),
labelStyle: TextStyle(fontSize: 15)),
),
),
Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 8),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
RaisedButton(
child: Text('Meeting Time'),
onPressed: () {
DatePicker.showTimePicker(context,
theme: DatePickerTheme(
containerHeight: 210.0,
),
showTitleActions: true, onConfirm: (time) {
print('confirm $time');
_time =
'${time.hour} : ${time.minute} : ${time.second}';
setState(() {});
},
currentTime: DateTime.now(),
locale: LocaleType.en);
setState(() {});
}),
Text(_time)
],
),
),
Padding(
padding: const EdgeInsets.only(top: 8.0, bottom: 8),
child: TextField(
controller: agendaController,
maxLines: null,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: 'Agenda',
prefixIcon: Icon(Icons.message),
labelStyle: TextStyle(fontSize: 15)),
),
),
],
),
),
),
actions: <Widget>[
FlatButton(
child: Text("Save"),
onPressed: () {
// if (agendaController.text.isEmpty) return;
// if (_events[_controller.selectedDay] != null) {
// _events[_controller.selectedDay]
// .add(agendaController.text);
String asd = _controller.selectedDay.toString();
meetDate = asd.split(' ');
addMeetingToDatabase(meetDate);
agendaController.clear();
phoneController.clear();
locationController.clear();
Navigator.pop(context);
},
)
],
);
}
}
what i have achieved?
What i want to achieve
Instead of List<Dynamic>, it's better to map those data on an object so it can be accessed with a constant getter function.
Let Map<DateTime, List<dynamic>> _events; be Map<DateTime, EventDetails> _events;
where EventDetails contains for example:
class EventDetails {
String author;
String eventName;
String description;
EventDetails(
{required this.author,
required this.eventName,
required this.description});
}
Then you can add data using an Object.
EventDetails eventDetails = EventDetails(author: 'AUTHOR',
eventName: 'EVENT_NAME', description: 'DESCRIPTION');
...
_events[mee.dateTime].add(eventDetails);