dart 如何动态添加一个包含初始化数据的TextFormField到UI TextFormField列表的中间?

mrphzbgm  于 2023-02-10  发布在  其他
关注(0)|答案(2)|浏览(128)

我有一个动态表单列表,需要在两个域之间动态添加和删除表单域。我可以从列表底部正确添加/删除表单域。
但是,当我尝试在两个表单域之间添加一个表单域时,该域的数据没有正确更新。
如何正确地在两个字段之间添加字段并正确地填充数据?

import 'package:flutter/material.dart';

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

  @override
  State<DynamicFormWidget> createState() => _DynamicFormWidgetState();
}

class _DynamicFormWidgetState extends State<DynamicFormWidget> {
  List<String?> names = [null];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Dynamic Forms'),
      ),
      body: ListView.separated(
        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
        itemBuilder: (builderContext, index) => Row(
          children: [
            Flexible(
              child: TextFormField(
                initialValue: names[index],

                onChanged: (name) {
                  names[index] = name;
                  debugPrint(names.toString());
                },
                decoration: InputDecoration(
                  hintText: 'Enter your name',
                    border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(8))),
              ),
            ),
            Padding(
              padding: const EdgeInsets.all(8),
              child: IconButton(
                  onPressed: () {
                          setState(() {
                            if(index + 1 == names.length){
                              names.add( null); debugPrint('Added: $names');
                            } else {
                              names.insert(index + 1, null); debugPrint('Added [${index+1}]: $names');
                            }
                          });

                        },
                  color: Colors.green,
                  iconSize: 32,
                  icon: const Icon(Icons.add_circle)),
            ),
            Padding(
              padding: const EdgeInsets.all(8),
              child: IconButton(
                  onPressed: (index == 0&& names.length == 1)
                      ? null
                      : () {
                    setState(() {
                      names.removeAt(index);
                    });

                    debugPrint('Removed [$index]: $names');
                  },
                  color: Colors.red,
                  iconSize: 32,
                  icon: const Icon(Icons.remove_circle)),
            ),
          ],
        ),
        separatorBuilder: (separatorContext, index) => const SizedBox(
          height: 16,
        ),
        itemCount: names.length,
      ),
    );
  }
}
3htmauhk

3htmauhk1#

基本上,问题是Flutter搞不清TextFormField列表中的谁是谁。
要解决此问题,只需将key添加到您的TextFormField,以便它可以由Flutter唯一标识:

...
child: TextFormField(
  initialValue: names[index],
  key: UniqueKey(), // add this line
  onChanged: (name) {
...

如果你想了解更多关于键的知识和正确使用方法,可以看看this

js81xvg6

js81xvg62#

小部件AnimatedList解决了这个问题,它像列表一样跟踪小部件,并使用了一个构建函数,所以很容易与另一个列表同步元素。如果你最终拥有了大量的表单,你可以使用InheritedWidget来简化代码。
在这个示例中,我使用TextEditingController从表单代码部分进行抽象,并使用值进行初始化(小部件继承自ChangeNotifier,因此更改值将更新表单小部件中的文本),为了简单起见,它只在索引处添加(使用通用文本)和删除。
要使每个CustomLineForm对其他CustomLineForm起React(例如:如果只剩下一个条目,则禁用删除)使用StreamBuilderListModel来通知更改,并使每个条目评估是否需要更新而不是重建所有内容。

class App extends StatelessWidget {
  final print_all = ChangeNotifier();

  App({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: FormList(print_notifier: print_all),
        floatingActionButton: IconButton(
          onPressed: print_all.notifyListeners,
          icon: Icon(Icons.checklist),
        ),
      ),
    );
  }
}

class FormList extends StatefulWidget {
  final ChangeNotifier print_notifier;
  FormList({required this.print_notifier, super.key});

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

class _FormList extends State<FormList> {
  final _controllers = <TextEditingController>[];
  final _list_key = GlobalKey<AnimatedListState>();

  void print_all() {
    for (var controller in _controllers) print(controller.text);
  }

  @override
  void initState() {
    super.initState();
    widget.print_notifier.addListener(print_all);
    _controllers.add(TextEditingController(text: 'Inital entrie'));
  }

  @override
  void dispose() {
    widget.print_notifier.removeListener(print_all);
    for (var controller in _controllers) controller.dispose();
    super.dispose();
  }

  void _insert(int index) {
    final int at = index.clamp(0, _controllers.length - 1);
    _controllers.insert(at, TextEditingController(text: 'Insert at $at'));
    // AnimatedList will take what is placed in [at] so the controller
    // needs to exist before adding the widget
    _list_key.currentState!.insertItem(at);
  }

  void _remove(int index) {
    final int at = index.clamp(0, _controllers.length - 1);
    // The widget is replacing the original, it is used to animate the
    // disposal of the widget, ex: size.y -= delta * amount
    _list_key.currentState!.removeItem(at, (_, __) => Container());
    _controllers[at].dispose();
    _controllers.removeAt(at);
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedList(
      key: _list_key,
      initialItemCount: _controllers.length,
      padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
      itemBuilder: (ctx, index, _) {
        return CustomLineForm(
          index: index,
          controler: _controllers[index],
          on_insert: _insert,
          on_remove: _remove,
        );
      },
    );
  }
}

class CustomLineForm extends StatelessWidget {
  final int index;
  final void Function(int) on_insert;
  final void Function(int) on_remove;
  final TextEditingController controler;

  const CustomLineForm({
    super.key,
    required this.index,
    required this.controler,
    required this.on_insert,
    required this.on_remove,
  });

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Flexible(
          child: TextFormField(
            controller: controler,
          ),
        ),
        IconButton(
          icon: Icon(Icons.add_circle),
          onPressed: () => on_insert(index),
        ),
        IconButton(
          icon: Icon(Icons.remove_circle),
          onPressed: () => on_remove(index),
        )
      ],
    );
  }
}

相关问题