flutter 单选按钮无法更改其状态

vm0i2vca  于 2023-06-30  发布在  Flutter
关注(0)|答案(1)|浏览(185)

你好,我是一只绿色手。我想构建一个页面来从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来改变状态,我希望单选按钮可以改变它的状态,当我点击它。

6g8kf2rb

6g8kf2rb1#

value等于groupValue时,RadioListTile被选中,但这永远不会发生,因为_book是在那里创建的。即使selectedBook_book表示同一本书,它们也是不同的对象。
你可以把itemBuilder做成这样。这将使RadioListTile依赖于书名

itemBuilder:
    (BuildContext context,
    int index) {
  BookModule _book =
  BookModule.fromJSON(
      snapshot.data
          .docs[index]);
  return RadioListTile<
      String>.adaptive(
    value: _book.title,
    groupValue: selectedBook.title,
    onChanged:
        (BookModule? value) {
      setState(() {
        selectedBook = _book;
      });
    },
    title: Text(_book.title),
  );
},

相关问题