***Context**我正在从Firebase Firsestore子集合中读取数据,并将其传递到一个页面,该页面将所有信息显示为自定义的磁贴。点击这个磁贴使我们能够编辑子集合中文档数据的详细信息。点击磁贴时弹出的表单有一个下拉框,一个文本表单字段和2个日期选择器。除了textformfield之外,其他任何东西都可以在其中更改值。
- Firebase Firestore安装程序**用户> docID>状态> statusID
我面临着这个奇怪的错误,我似乎不能在第一次点击页面上的TextFormField框中键入任何内容,我必须第二次点击它才能键入它。当我在android模拟器设备上输入并按下回车键时,我刚刚输入的文本消失了,只显示我之前输入的任何内容。
在尝试点击两次并输入文本值时,它会将表单的所有其他功能重置为编辑前的原始值。所以我现在被迫保存到每个变化的firebase。这可以使所有其他字段保持不变,但TextFormField仍然不起作用!
- 关于程序流程:status_detailed_screen_tab-->更新状态屏幕 *
我将突出显示将信息发送到更新屏幕的代码。
- status_detailed_screen_tab**
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:prototype_1/screens/add_new_status_screen.dart';
import 'package:prototype_1/screens/update_status_screen.dart';
import 'package:prototype_1/util/text_styles/text_style.dart';
import 'package:prototype_1/util/tiles/current_status_detailed_screen_tile.dart';
import 'package:prototype_1/util/tiles/past_status_detailed_screen_tile.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:intl/intl.dart';
late Stream<QuerySnapshot> documentStream;
List<Map<String, dynamic>> userCurrentStatus = [];
class StatusesTab extends StatefulWidget {
const StatusesTab({
super.key,
required this.docID,
});
final String docID;
@override
State<StatusesTab> createState() => _StatusesTabState();
}
class _StatusesTabState extends State<StatusesTab> {
//This is what the stream builder is waiting for
@override
void initState() {
documentStream = FirebaseFirestore.instance
.collection('Users')
.doc(widget.docID)
.collection('Statuses')
.snapshots();
super.initState();
}
@override
Widget build(BuildContext context) {
List<Map<String, dynamic>> userPastStatus = [];
List<Map<String, dynamic>> toRemove = [];
return Scaffold(
backgroundColor: Colors.transparent,
body: Padding(
padding: EdgeInsets.all(30.0.sp),
child: StreamBuilder<QuerySnapshot>(
stream: documentStream,
builder: (context, snapshot) {
var users = snapshot.data?.docs.toList();
if (snapshot.hasData) {
userCurrentStatus = [];
userPastStatus = [];
toRemove = [];
for (var i = 0; i < users!.length; i++) {
var data = users[i].data();
userCurrentStatus.add(data as Map<String, dynamic>);
userCurrentStatus[i]
.addEntries({'ID': users[i].reference.id}.entries);
}
for (var status in userCurrentStatus) {
if (DateFormat("d MMM yyyy")
.parse(status['endDate'])
.isBefore(DateTime.now())) {
userPastStatus.add(status);
toRemove.add(status);
}
}
userCurrentStatus
.removeWhere((element) => toRemove.contains(element));
}
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Icon(
Icons.medical_information_rounded,
color: Colors.white,
size: 30.sp,
),
SizedBox(
width: 20.w,
),
Text(
"Active Statuses",
maxLines: 2,
style: GoogleFonts.poppins(
color: Colors.white,
fontSize: 20.sp,
fontWeight: FontWeight.w500,
letterSpacing: 1.5,
),
),
],
),
SizedBox(
height: 295.h,
child: ListView.builder(
itemCount: userCurrentStatus.length,
padding: EdgeInsets.all(12.sp),
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return InkWell(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ***UpdateStatusScreen(
statusID: userCurrentStatus[index]['ID'],
docID: widget.docID,
selectedStatusType: userCurrentStatus[index]
['statusType'],
statusName: TextEditingController(
text: userCurrentStatus[index]['statusName']
.toString()),
startDate: userCurrentStatus[index]
['startDate'],
endDate: userCurrentStatus[index]['endDate'],
),***
),
);
},
child: SoldierStatusTile(
statusID: userCurrentStatus[index]['ID'],
docID: widget.docID,
statusType: userCurrentStatus[index]['statusType'],
statusName: userCurrentStatus[index]['statusName'],
startDate: userCurrentStatus[index]['startDate'],
endDate: userCurrentStatus[index]['endDate'],
),
);
},
),
),
Row(
children: [
Icon(
Icons.av_timer_rounded,
color: Colors.white,
size: 30.sp,
),
SizedBox(
width: 20.w,
),
Text(
"Past Statuses",
maxLines: 2,
style: GoogleFonts.poppins(
color: Colors.white,
fontSize: 20.sp,
fontWeight: FontWeight.w500,
letterSpacing: 1.5,
),
),
],
),
Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: userPastStatus.length,
itemBuilder: (context, index) {
return PastSoldierStatusTile(
statusID: userPastStatus[index]['ID'],
docID: widget.docID,
statusType: userPastStatus[index]['statusType'],
statusName: userPastStatus[index]['statusName'],
startDate: userPastStatus[index]['startDate'],
endDate: userPastStatus[index]['endDate'],
);
},
),
),
SizedBox(
height: 20.h,
),
Center(
child: TextButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddNewStatusScreen(
docID: widget.docID,
selectedStatusType: "Select status type...",
statusName: TextEditingController(),
startDate:
DateFormat('d MMM yyyy').format(DateTime.now()),
endDate:
DateFormat('d MMM yyyy').format(DateTime.now()),
),
),
);
},
child: Container(
padding: EdgeInsets.symmetric(
horizontal: 40.0.w, vertical: 16.0.h),
decoration: BoxDecoration(
shape: BoxShape.rectangle,
gradient: const LinearGradient(
colors: [
Color.fromARGB(255, 72, 30, 229),
Color.fromARGB(255, 130, 60, 229),
],
),
borderRadius: BorderRadius.circular(50.0.r)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.note_add,
color: Colors.white,
size: 30.sp,
),
SizedBox(
width: 20.w,
),
StyledText("ADD NEW STATUS", 18.sp,
fontWeight: FontWeight.bold),
],
),
),
),
),
],
);
},
),
),
);
}
}
- update_status_screen**
// ignore_for_file: must_be_immutable
import 'package:auto_size_text/auto_size_text.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:intl/intl.dart';
import 'package:prototype_1/util/text_styles/text_style.dart';
class UpdateStatusScreen extends StatefulWidget {
UpdateStatusScreen({
super.key,
required this.docID,
required this.selectedStatusType,
required this.statusName,
required this.startDate,
required this.endDate,
required this.statusID,
});
late TextEditingController statusName;
late String? selectedStatusType;
late String startDate;
late String endDate;
late String docID;
late String statusID;
@override
State<UpdateStatusScreen> createState() => _UpdateStatusScreenState();
}
CollectionReference db = FirebaseFirestore.instance.collection('Users');
class _UpdateStatusScreenState extends State<UpdateStatusScreen> {
final _formKey = GlobalKey<FormState>();
final _statusTypes = [
"Select status type...",
"Excuse",
"Leave",
"Medical Appointment",
];
void initState() {
display();
super.initState();
}
void _showStartDatePicker() {
showDatePicker(
context: context,
initialDate: DateFormat("d MMM yyyy").parse(widget.startDate),
firstDate: DateTime(1960),
lastDate: DateTime(2030),
).then((value) {
setState(() {
if (value != null) {
widget.startDate = DateFormat('d MMM yyyy').format(value);
editUserStatus();
}
});
});
}
void _showEndDatePicker() {
showDatePicker(
context: context,
initialDate: DateFormat("d MMM yyyy").parse(widget.endDate),
firstDate: DateTime(1960),
lastDate: DateTime(2030),
).then((value) {
setState(() {
if (value != null) {
widget.endDate = DateFormat('d MMM yyyy').format(value);
editUserStatus();
}
});
});
}
void editStatus() {
editUserStatus();
Navigator.pop(context);
}
Future editUserStatus() async {
db.doc(widget.docID).collection('Statuses').doc(widget.statusID).update({
//User map formatting
'statusName': widget.statusName.text.trim(),
'statusType': widget.selectedStatusType,
'startDate': widget.startDate,
'endDate': widget.endDate,
});
}
void display() {
//print(sName.text);
print(widget.selectedStatusType);
print(widget.startDate);
print(widget.endDate);
print(widget.docID);
print(widget.statusID);
}
@override
void dispose() {
widget.statusName.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: const Color.fromARGB(255, 21, 25, 34),
body: SingleChildScrollView(
child: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 12.w),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
InkWell(
onTap: () {
Navigator.pop(context);
},
child: Icon(
Icons.arrow_back_sharp,
color: Colors.white,
size: 25.sp,
),
),
SizedBox(
height: 20.h,
),
StyledText(
"Let's add a new status for this soldier ✍️",
30.sp,
fontWeight: FontWeight.bold,
),
StyledText(
"Fill in the details of the status",
14.sp,
fontWeight: FontWeight.w300,
),
SizedBox(
height: 40.h,
),
//Status type drop down menu
Container(
decoration: BoxDecoration(
color: Colors.black54,
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(12.r),
),
child: DropdownButtonFormField<String>(
alignment: Alignment.center,
dropdownColor: Colors.black54,
value: widget.selectedStatusType,
icon: const Icon(
Icons.arrow_downward_sharp,
color: Colors.white,
),
style: const TextStyle(color: Colors.black54),
items: _statusTypes
.map(
(item) => DropdownMenuItem<String>(
value: item,
child: AutoSizeText(
item,
maxLines: 1,
style: GoogleFonts.poppins(
fontSize: 16.sp,
fontWeight: FontWeight.w400,
color: Colors.white),
),
),
)
.toList(),
onChanged: (String? item) async => setState(() {
widget.selectedStatusType = item;
editUserStatus();
}),
),
),
SizedBox(
height: 30.h,
),
//Name of status textfield
Container(
decoration: BoxDecoration(
color: Colors.black54,
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(12.r),
),
child: Padding(
padding: EdgeInsets.only(left: 20.w),
child: TextFormField(
style: GoogleFonts.poppins(
color: Colors.white,
fontSize: 16.sp,
fontWeight: FontWeight.w500,
),
controller: widget.statusName,
decoration: InputDecoration(
border: InputBorder.none,
labelText: 'Enter Status Name:',
labelStyle: GoogleFonts.poppins(
color: Colors.white,
fontSize: 16.sp,
fontWeight: FontWeight.w500,
),
),
),
),
),
SizedBox(
height: 30.h,
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
//Status start date picker
Container(
height: 55.h,
width: 150.w,
decoration: BoxDecoration(
color: Colors.black54,
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(12.r),
),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 15.w, vertical: 15.h),
child: AutoSizeText(
widget.startDate,
//sDate,
style: GoogleFonts.poppins(
color: Colors.white, fontSize: 16.sp),
),
),
),
Padding(
padding: EdgeInsets.all(8.0.sp),
child: InkWell(
onTap: () {
_showStartDatePicker();
},
child: const Icon(
Icons.date_range_rounded,
color: Colors.white,
),
),
),
SizedBox(
width: 10.w,
),
//Status end date picker
Container(
width: 145.w,
height: 55.h,
decoration: BoxDecoration(
color: Colors.black54,
border: Border.all(color: Colors.white),
borderRadius: BorderRadius.circular(12.r),
),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: 15.w, vertical: 15.h),
child: AutoSizeText(
widget.endDate,
//eDate,
style: GoogleFonts.poppins(
color: Colors.white, fontSize: 16.sp),
),
),
),
Padding(
padding: EdgeInsets.all(8.0.sp),
child: InkWell(
onTap: () {
_showEndDatePicker();
},
child: const Icon(
Icons.date_range_rounded,
color: Colors.white,
),
),
),
],
),
SizedBox(
height: 40.h,
),
GestureDetector(
onTap: editStatus,
child: Container(
padding: EdgeInsets.all(10.sp),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.deepPurple.shade400,
Colors.deepPurple.shade700,
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(12.r),
),
child: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.add_to_photos_rounded,
color: Colors.white,
size: 30.sp,
),
SizedBox(
width: 20.w,
),
AutoSizeText(
'ADD STATUS',
style: GoogleFonts.poppins(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 22.sp,
),
),
],
),
),
),
),
],
),
),
),
),
),
);
}
}
当我第一次尝试在文本字段上键入 * W/OnBackInvokedCallback(11092)时,我得到了这个错误:未为应用程序启用OnBackInvokedCallback。W/OnBackInvokedCallback(11092):在应用程序清单中设置'android:enableOnBackInvokedCallback ="true"'。D/插入控制器(11092):show(ime(),fromIme = true)* Error Image
You can't see it here, but the cursor is for some reason at the start of the TextFormField in the status Name feature
1条答案
按热度按时间o75abkj41#
您提供的错误消息与Android应用程序中的OnBackInvokedCallback和InsetsController相关。以下是对每个组件的说明:
OnBackInvokedCallback:此回调用于拦截Android应用程序中的后退按钮按下事件。通过启用此回调,可以覆盖后退按钮的默认行为。
插入控制器:Insets是屏幕上可能为系统UI元素(如状态栏或导航栏)保留的区域。InsetsController允许您控制如何显示或隐藏这些inset。
关于您看到的错误消息,它表明您的应用程序没有启用OnBackInvokedCallback。建议的解决方案是在应用的清单文件中将"android:enableOnBackInvokedCallback"属性设置为"true"。通过这样做,您可以启用回调并允许应用程序处理后退按钮按下事件。
至于你提到的关于“show(ime(),fromIme = true)”的日志消息,似乎与你提供的错误消息无关。此日志消息与输入法编辑器(IME)相关,并指示正在显示IME或使其可见。“fromIme = true”部分可以指示IME正在从IME本身触发。
如果您遇到文本字段问题或与显示IME相关的意外行为,则可能与您提供的错误消息无关。有关特定代码实现或上下文的更多信息将有助于提供更准确的解决方案。