dart ListView中Dismissable的键属性在使用索引参数时引发异常

kulphzqa  于 2023-09-28  发布在  其他
关注(0)|答案(1)|浏览(80)

如果你们能帮我的话我会很感激的,这让我晚上睡不着。
为什么属性的键位于key:Key(people[index].id.toString())是否返回空列表?因此,代码只是抛出一个异常,并且无法运行,因为值为0:“无效值:有效值范围为空:0”。
有什么线索告诉我该怎么解决吗?
Person.dart:

import 'package:flutter/material.dart';

class Person {
  int id = UniqueKey().hashCode;
  late String personName;
  late double personWeight;
  late double personHeight;
  late double personBMI;
  bool personHasGoodBMI = false;

  String get name => personName;
  set name(String name) => personName = name;

  double get weight => personWeight;
  set weight(double weight) => personWeight = weight;

  double get height => personHeight;
  set height(double height) => personHeight = height;

  double get bmi => personBMI;
  set bmi(double bmi) => personBMI = bmi;

  bool get isValidPerson => personName.isNotEmpty && personWeight > 0 && personHeight > 0;
  set isValidPerson(bool isValidPerson) => isValidPerson = isValidPerson;

  bool get hasGoodBMI => personHasGoodBMI;
  set hasGoodBMI(bool hasGoodBMI) => personHasGoodBMI = hasGoodBMI;

  Person({this.personName = '', this.personWeight = 0, this.personHeight = 0, this.personBMI = 0});
}

person_CRUD.dart:

import 'package:d1_calculadoraimc/models/person.dart';

class PersonCRUD {
  final List<Person> _persons = [];
  Future<void> addPerson(Person person) async {
    await Future.delayed(const Duration(microseconds: 200));
    _persons.add(person);
  }

  Future<void> removePerson(Person person) async {
    await Future.delayed(const Duration(microseconds: 200));
    _persons.remove(person);
  }

  Future<void> updatePerson(String id, bool isGood) async {
    await Future.delayed(const Duration(microseconds: 200));
    final person = _persons.firstWhere((element) => element.id == id);
    person.hasGoodBMI = isGood;
  }

  Future<List<Person>> listPersons() async {
    await Future.delayed(const Duration(microseconds: 200));

    return _persons;
  }
}

calculate_IMC.dart:

class IMCCalculator {
    Person person = Person();

  static double calculate(Person person) {
    double heightInMeters = person.height / 100.0;
    return person.weight / (heightInMeters * heightInMeters);
  }

  Person returnIMCPerson(double height, double weight) {
    person.height = height;
    person.weight = weight;
    person.bmi = calculate(person);
    person.bmi = double.parse(person.bmi.toStringAsFixed(2));
    return person;
  }
}

主文件:

import 'package:d1_calculadoraimc/person_CRUD.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'calculate_IMC.dart';

void main() {
  runApp(MaterialApp(
    title: 'Calculadora IMC',
    theme: ThemeData(
      primarySwatch: Colors.blue,
      textTheme: const TextTheme(
        titleLarge: TextStyle(
          fontSize: 24.0,
          fontWeight: FontWeight.bold,
        ),
      ),
    ),
    home: const _CalculateIMC(),
  ));
}

class _CalculateIMC extends StatefulWidget {
  const _CalculateIMC({Key? key}) : super(key: key);

  @override
  _CalculateIMCState createState() => _CalculateIMCState();
}

class _CalculateIMCState extends State<_CalculateIMC> {
  final _formKey = GlobalKey<FormState>();
  final TextEditingController _nameController = TextEditingController();
  final TextEditingController _heightController = TextEditingController();
  final TextEditingController _weightController = TextEditingController();

  var crud = PersonCRUD();
  var people = <Person>[];
  IMCCalculator imcCalculator = IMCCalculator();
  Person person = Person();

  void getPeople() async {
    people = await crud.listPersons();
    setState(() {});
  }

  @override
  void initState() {
    getPeople();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('Calculadora IMC'),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: () {
            person.name = "";
            person.height = 0.0;
            person.weight = 0.0;
            showDialog(
              context: context,
              builder: (BuildContext bc) => AlertDialog(
                title: const Text("Enviar informações"),
                content: Form(
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      TextFormField(
                        key: _formKey,
                        controller: _nameController,
                        decoration: const InputDecoration(
                          labelText: 'Nome',
                        ),
                        validator: (value) {
                          if (value == null || value.isEmpty) {
                            return 'Por favor, insira um nome';
                          }
                          return null;
                        },
                      ),
                      TextFormField(
                        key: _formKey,
                        controller: _heightController,
                        decoration: const InputDecoration(
                          labelText: 'Altura (cm)',
                        ),
                        keyboardType: TextInputType.number,
                        inputFormatters: [
                          FilteringTextInputFormatter.digitsOnly,
                        ],
                        validator: (value) {
                          if (value == null || value.isEmpty) {
                            return 'Por favor, insira uma altura';
                          }
                          return null;
                        },
                      ),
                      TextFormField(
                        key: _formKey,
                        controller: _weightController,
                        decoration: const InputDecoration(
                          labelText: 'Peso (kg)',
                        ),
                        keyboardType: TextInputType.number,
                        inputFormatters: [
                          FilteringTextInputFormatter.digitsOnly,
                        ],
                        validator: (value) {
                          if (value == null || value.isEmpty) {
                            return 'Por favor, insira um peso';
                          }
                          return null;
                        },
                      ),
                    ],
                  ),
                ),
                actions: [
                  ElevatedButton(
                    onPressed: () => Navigator.of(context).pop(),
                    child: const Icon(Icons.cancel_rounded),
                  ),
                  ElevatedButton(
                    onPressed: () async {
                      if (person.isValidPerson) {
                        await crud.addPerson(Person(
                            personName: _nameController.text,
                            personHeight: double.parse(_heightController.text),
                            personWeight: double.parse(_weightController.text),
                            personBMI: imcCalculator
                                .returnIMCPerson(
                                    double.parse(_heightController.text),
                                    double.parse(_weightController.text))
                                .bmi
                                .toDouble()));
                        setState(() {
                          getPeople();
                        });
                        Navigator.of(context).pop();
                      } else {
                        ScaffoldMessenger.of(context).showSnackBar(
                          const SnackBar(
                            content: Text("Preencha todos os campos"),
                          ),
                        );
                      }
                      setState(() {
                        getPeople();
                      });
                    },
                    child: const Icon(Icons.send_rounded),
                  ),
                ],
              ),
            );
          },
          child: const Icon(Icons.add_rounded),
        ),
        body: Container(
            margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 20),
            child: Column(children: [
              Expanded(child: ListView.builder(itemBuilder: (context, index) {
                return Dismissible(
                  key: Key(people[index].id.toString()),
                  onDismissed: (DismissDirection direction) async {
                    await crud.removePerson(people[index]);
                    getPeople();
                  },
                  child: ListTile(
                    title: Text(
                      people[index].name,
                      style: const TextStyle(fontSize: 24),
                    ),
                    subtitle: Text(
                      "IMC: ${people[index].bmi}",
                      style: const TextStyle(fontSize: 16),
                    ),
                    trailing: Icon(
                      people[index].hasGoodBMI
                          ? Icons.sentiment_satisfied_alt_rounded
                          : Icons.sentiment_dissatisfied_rounded,
                      color:
                          people[index].hasGoodBMI ? Colors.green : Colors.red,
                    ),
                  ),
                );
              }))
            ])));
  }
}
y1aodyip

y1aodyip1#

你得到错误的原因是itemBuilder被调用时people是空的。
要解决这个问题,请将ListView.builder替换为一个私有方法,该方法返回一个取决于people大小的小部件:
使用前:

Expanded(
  child: ListView.builder()
)

之后:

Widget _getChild() {
  if (people.isEmpty) {
    return CircularProgressIndicator();
  } else {
    return ListView.builder(
      itemCount: people.length,
      // replace with your itemBuilder here
      itemBuilder: (context, index) {
        return Container(
            key: Key(people[index].id.toString())
        );
      },
    );
  }
}

Expanded(
  child: _getChild(),
)

相关问题