你好,我是一只绿色手。我想构建一个页面来从firestore中选择文档,所以我做了一个像这样的页面enter image description here。我希望单选按钮可以在我点击项目时更改状态,但它们不能!
我不知道我的代码出了什么问题
我的代码在这里
import 'package:billbook_flutter/controls/BillListCard.dart';
import 'package:billbook_flutter/module/book.dart';
import 'package:billbook_flutter/services/firestore.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../module/bill.dart';
// ignore: depend_on_referenced_packages
import 'package:intl/intl.dart' as intl;
// ignore: must_be_immutable
class CreateNewBillPage extends StatefulWidget {
BookModule? book;
List<BookModule> books;
final FirebaseFirestore db = FirebaseFirestore.instance;
final FirebaseAuth auth = FirebaseAuth.instance;
CreateNewBillPage({super.key, required this.book, required this.books});
@override
// ignore: library_private_types_in_public_api, no_logic_in_create_state
_CreateNewBillPageState createState() => _CreateNewBillPageState();
}
class _CreateNewBillPageState extends State<CreateNewBillPage> {
TextEditingController billTitleController = TextEditingController();
TextEditingController billDescriptionController = TextEditingController();
TextEditingController billAmountController = TextEditingController();
bool loading = false;
BillType type = BillType.income;
DateTime date = DateTime.now();
final descriptionFocus = FocusNode();
final amountFous = FocusNode();
final titleFocus = FocusNode();
BookModule? selectedBook;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Create New Bill'),
leading: IconButton(
icon: const Icon(Icons.close),
onPressed: () {
Navigator.pop(context);
},
),
actions: [
FilledButton(
onPressed: () async {
//add bill to firebase
if (billTitleController.text.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
behavior: SnackBarBehavior.floating,
width: 400.0,
content: const Text('Bill name cannot be empty'),
action: SnackBarAction(
label: 'Got it',
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
),
));
} else {
if (double.parse(billAmountController.text) > 0 ||
billAmountController.text.isNotEmpty) {
FirestoreService().addBill(
billTitleController.text,
billDescriptionController.text == ''
? 'No description'
: billDescriptionController.text,
FirebaseAuth.instance.currentUser!.uid,
widget.book!.id,
type,
Timestamp.fromDate(date),
double.parse(billAmountController.text) + 0.0);
if (kDebugMode) {
print(date.toString());
}
Navigator.pop(context);
} else {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
behavior: SnackBarBehavior.floating,
width: 400.0,
content: const Text('Bill amount cannot be 0'),
action: SnackBarAction(
label: 'Got it',
onPressed: () {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
},
),
));
}
}
},
style: ButtonStyle(
enableFeedback: true,
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
child:
const Padding(padding: EdgeInsets.all(8), child: Text('Add')),
),
const SizedBox(
width: 10,
)
],
),
body: Center(
child: Form(
child: SingleChildScrollView(
padding: const EdgeInsets.all(20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Card(
child: SizedBox(
width: 500,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: [
SizedBox(
width: 500,
child: SegmentedButton<BillType>(
style: ButtonStyle(
shape: MaterialStateProperty.all<
RoundedRectangleBorder>(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10)),
)),
segments: const <ButtonSegment<BillType>>[
ButtonSegment<BillType>(
value: BillType.income,
label: Padding(
padding: EdgeInsets.all(8.0),
child: Text('Income'),
)),
ButtonSegment<BillType>(
value: BillType.expense,
label: Padding(
padding: EdgeInsets.all(8.0),
child: Text('Expense'),
)),
ButtonSegment<BillType>(
value: BillType.transfer,
label: Padding(
padding: EdgeInsets.all(8.0),
child: Text('Transfer'),
))
],
selected: <BillType>{type},
onSelectionChanged: (Set<BillType> newType) {
setState(() {
type = newType.first;
});
},
),
),
const SizedBox(height: 10),
Row(
children: [
CircleAvatar(
backgroundColor: switchColor(type),
child: switchIcon(type, Colors.white),
),
const SizedBox(
width: 10,
),
//text field
Expanded(
child: TextField(
focusNode: amountFous,
textInputAction: TextInputAction.next,
onSubmitted: (v) {
FocusScope.of(context)
.requestFocus(titleFocus);
},
controller: billAmountController,
inputFormatters: [
FilteringTextInputFormatter.allow(
RegExp(r'[0-9.]'))
],
keyboardType:
const TextInputType.numberWithOptions(
decimal: true),
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter a amount',
labelText: 'Amount'),
autofocus: true,
),
)
],
),
],
),
),
),
),
const SizedBox(
height: 40,
),
SizedBox(
width: 500,
child: TextField(
focusNode: titleFocus,
textInputAction: TextInputAction.next,
onSubmitted: (v) {
FocusScope.of(context).requestFocus(descriptionFocus);
},
controller: billTitleController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter a title',
labelText: 'Title',
labelStyle: TextStyle(
fontWeight: FontWeight.bold, fontSize: 18),
hintStyle: TextStyle(
fontWeight: FontWeight.bold, fontSize: 14)),
),
),
const SizedBox(height: 10),
//Amount
SizedBox(
width: 500,
child: TextField(
focusNode: descriptionFocus,
textInputAction: TextInputAction.next,
controller: billDescriptionController,
maxLines: 10,
minLines: 5,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: 'Enter a description',
labelText: 'Description',
),
),
),
const SizedBox(height: 20),
SizedBox(
width: 500,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
'Date',
style: Theme.of(context).textTheme.bodyLarge,
),
Text.rich(
TextSpan(
children: [
WidgetSpan(
child: OutlinedButton(
onPressed: () async {
DateTime? pickedDate = await showDatePicker(
context: context,
initialDate: DateTime.now(), // 初始选中今天
firstDate: DateTime(2020), // 可选日期从2020年开始
lastDate: DateTime(2025), // 可选日期到2025年结束
);
// 如果用户没有取消对话框,更新选择的日期
if (pickedDate != null) {
setState(() {
date = DateTime(
pickedDate.year,
pickedDate.month,
pickedDate.day,
date.hour,
date.minute);
});
}
},
child: Text(intl.DateFormat('yyyy-MM-dd')
.format(date))),
style: Theme.of(context).textTheme.bodyLarge,
),
const WidgetSpan(
child: Padding(padding: EdgeInsets.all(5.0))),
WidgetSpan(
child: OutlinedButton(
onPressed: () async {
TimeOfDay? pickedTime =
await showTimePicker(
context: context,
initialTime:
TimeOfDay.now(), // 初始选中当前时间
);
// 如果用户没有取消对话框,更新选择的时间
if (pickedTime != null) {
setState(() {
date = DateTime(
date.year,
date.month,
date.day,
pickedTime.hour,
pickedTime.minute);
});
}
},
child: Text(
intl.DateFormat.Hm().format(date),
style: Theme.of(context)
.textTheme
.titleMedium,
))),
],
),
),
],
),
),
const SizedBox(height: 20),
SizedBox(
width: 500,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Text(
'Where',
style: Theme.of(context).textTheme.bodyLarge,
),
OutlinedButton(
onPressed: () {
//show dialog
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Select a book'),
content: StreamBuilder(
stream: widget.db
.collection('books')
.where('userId',
isEqualTo: widget
.auth.currentUser!.uid)
.orderBy('isPinned',
descending: true)
.orderBy('date', descending: true)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot snapshot) {
if (snapshot.hasData) {
if (snapshot.data!.docs.length >
0) {
return SizedBox(
height: 500,
width: 500,
child: ListView.builder(
shrinkWrap: true,
itemCount: snapshot
.data!.docs.length,
itemBuilder:
(BuildContext context,
int index) {
BookModule _book =
BookModule.fromJSON(
snapshot.data
.docs[index]);
return RadioListTile<
BookModule>.adaptive(
value: _book,
groupValue: selectedBook,
onChanged:
(BookModule? value) {
setState(() {
selectedBook = value;
});
},
title: Text(_book.title),
);
},
),
);
} else {
return const Center(
child:
CircularProgressIndicator(),
);
}
} else {
return const Center(
child: Text('Cannot found books'),
);
}
},
),
actions: [
OutlinedButton(
onPressed: () =>
Navigator.pop(context),
child: const Text('Cancel')),
FilledButton(
onPressed: () {
setState(() {
widget.book = selectedBook;
});
Navigator.pop(context);
},
child: const Text('Select'))
],
);
});
},
child: Text(widget.book != null
? widget.book!.title
: 'select a book')),
]),
)
],
),
)),
));
}
}
我尝试使用setState
来改变状态,我希望单选按钮可以改变它的状态,当我点击它。
1条答案
按热度按时间6g8kf2rb1#
当
value
等于groupValue
时,RadioListTile
被选中,但这永远不会发生,因为_book
是在那里创建的。即使selectedBook
和_book
表示同一本书,它们也是不同的对象。你可以把
itemBuilder
做成这样。这将使RadioListTile
依赖于书名