我如何在 Flutter 中创建一个动态列表,用户可以在其中通过文本字段添加项目并再次删除该项目?

How can i make a dynamic list in Flutter where user can add items through a textfield and delete the item again?

我想为用户提供一个文本输入字段,当他提示某些内容并单击按钮时,输入应显示在列表项中。用户还应该可以选择删除列表中的项目,就像 in 和 todo 应用程序一样。

在这里你可以找到我的代码:

Link to Code

所以我决定编写这个简单的程序来更新我的技能...您可以直接复制粘贴这段代码,它应该可以正常工作。

我在这里使用了 provider 包来使它更专业一些,因为当任务添加到您的列表时,您不能总是依赖 setState() 来更新您的 UI。也因为您将来可能会更频繁地使用该提供商。

为了便于理解,我在下面的代码中添加了注释。但是,请不要犹豫,清除评论中的任何混淆:)

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:todo/list_provider.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider( // This initiates the provider.
      create: (context) => TaskProvider(), // Initiating it here makes this provider data available everywhere in the application
      child: MaterialApp(
        title: 'Flutter Demo',
        home: const MyHomePage(),
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Dynamic List'),
      ),
      body: const HomePageBody(), // I pass a separate widget here just to make the code a bit cleaner
      floatingActionButton: FloatingActionButton(
        onPressed: () => showModalBottomSheet( // This calls a bottom Modal Sheet which pops up while pressing the floating action button
            context: context, builder: (context) => const BottomSheet()),// The modal sheet displays the BottomSheet() Widget which I have defined down in this code.
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

// This is where the ListView will be shown
class HomePageBody extends StatelessWidget {
  const HomePageBody({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    List<String> listOfTasks = Provider.of<TaskProvider>(context).getTasks; // This is where the list is being accessed from the Provider file.
    return Container(
      padding: const EdgeInsets.all(20),
      child: ListView.builder(
        itemCount: listOfTasks.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(
              listOfTasks[index],
            ),
          );
        },
      ),
    );
  }
}

// This is the BottomSheet Widget where I decided to take User Input from
class BottomSheet extends StatefulWidget {
  const BottomSheet({Key? key}) : super(key: key);

  @override
  State<BottomSheet> createState() => _BottomSheetState();
}

class _BottomSheetState extends State<BottomSheet> {
  String task = ''; // This variable holds the tasks user wants to add
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.only(
        top: 20,
        left: 20,
        right: 20,
        bottom: MediaQuery.of(context).viewInsets.bottom + 20, // viewInsets.bottom adds padding from the bottom to avoid keyboard overlapping textfield widget
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          TextFormField(// You can use TextField Widget as well
            decoration: InputDecoration(
              border: OutlineInputBorder(
                borderRadius: BorderRadius.circular(10),
              ),
            ),
            onChanged: (value) { // This saves the value in the TextField for every character the user types
              task = value; // The value in the TextField is referred to by the 'value' variable
            },
          ),
          const SizedBox(
            height: 10,
          ),
          ElevatedButton(
            onPressed: () => saveTask(task),
            child: const Text('Save Task'),
          ),
        ],
      ),
    );
  }

  void saveTask(String task) {
    Provider.of<TaskProvider>(context, listen: false).addTasks(task); //This is where I am calling the function to add a task to the list. 
    // The 'addTasks()' function is defined in the provider file which is just below
  }
}

这是我在上面的代码中导入的 list_provider.dart 文件:

import 'package:flutter/foundation.dart';

class TaskProvider extends ChangeNotifier { // This is the class where your data exists
// and this is the only place where your data should be manipulated! I explain the reason below...
  final List<String> _tasks = [];

  List<String> get getTasks { // We use a getter to retrieve the list
    return _tasks; // We do that in order to avoid modifications to this list from any outside sources.
  }

  void addTasks(task) {
    _tasks.add(task); // This is simply how you add anything to a list
    notifyListeners(); // This is why we use providers. This function notifies all the children widgets
    // of the Widget where we initiated our provider (see the parent of MaterialApp Widget in the above code)
    // This is why changes to data should be made within this class only as it extends ChangeNotifier,
    // which provides us with notifyListeners() method. Which ultimately notifies the widgets that the data has been modified and its time to rebuild the widgets that rely on this data!
  }
}

您可以复制粘贴此代码,只需确保将提供程序包添加到您的 pubspec.yaml 文件中,如下所示。