skip to Main Content

I tried to create a list of custom TextformField which can be deleted using a button with the Map function but the Textformfield is not deleting properly.

It is always the last Widget that is deleted.

Does anyone have a solution?

Est-ce qu’il est possible de créer un objet qui peut se suprrimer lui même ?

Thanks !

Here is my test code

you can try this code in Darpad

enter image description here

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  const MyHomePage({
    Key? key,
    required this.title,
  }) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late List<int> _listID = [0, 1, 2, 3];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
          children: _listID.map((id) {
        print(id);
        return TestWidget(
          id: id,
          onPressed: () {
            setState(() {
              _listID.remove(id);
              print(_listID);
            });
          },
        );
      }).toList()),
      floatingActionButton: FloatingActionButton(
        onPressed: () {},
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

class TestWidget extends StatefulWidget {
  TestWidget({super.key, this.onPressed, required this.id});
  late VoidCallback? onPressed;
  late int id;
  @override
  State<TestWidget> createState() =>
      _TestWidgetState(onPressed: this.onPressed, id: id);
}

class _TestWidgetState extends State<TestWidget> {
  late VoidCallback? onPressed;
  late int id;
  _TestWidgetState({required this.id, this.onPressed});

  @override
  Widget build(BuildContext context) {
    return TextFormField(
        decoration: InputDecoration(
      labelText: id.toString(),
      suffixIcon: IconButton(
          icon: const Icon(Icons.clear), onPressed: () => onPressed!()),
    ));
  }
}

3

Answers


  1. Chosen as BEST ANSWER

    Finally for a perfect behavior of what I wanted to do I had to define a class TestWidgetData to contain the data of each widget TestWidget, including the unique identifier and the user name. When the user presses the "clear" button in one of the TestWidget widgets, I use the removeWhere method of the _listData list to remove the item corresponding to the given id, rather than removing an item from the index given. This keeps the state of existing TestWidget widgets when I delete an item from the list, rather than rebuilding them all.

    Thank you for your answers they were very helpful!

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      final String title;
    
      const MyHomePage({
        Key? key,
        required this.title,
      }) : super(key: key);
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
       final List<TestWidgetData> _listData = [
        TestWidgetData(id: 0, textValue: ""),
        TestWidgetData(id: 1, textValue: ""),
        TestWidgetData(id: 2, textValue: ""),
        TestWidgetData(id: 3, textValue: ""),
      ];
      
      void removeTestWidgetData(int id) {
        setState(() {
          _listData.removeWhere((data) => data.id == id);
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text(widget.title),
          ),
          body: Column(
              children: _listData.map((data) {
            return TestWidget(
              key: ValueKey(data.id),
              data: data,
              onPressed: () {
                removeTestWidgetData(data.id);
              },
            );
          }).toList()),
          floatingActionButton: FloatingActionButton(
            onPressed: () {},
            tooltip: 'Increment',
            child: const Icon(Icons.add),
          ),
        );
      }
    }
    
    class TestWidget extends StatefulWidget {
      final TestWidgetData data;
      final VoidCallback? onPressed;
    
      const TestWidget({
        Key? key,
        required this.data,
        this.onPressed,
      }) : super(key: key);
    
      @override
      State<TestWidget> createState() => _TestWidgetState();
    }
    
    class _TestWidgetState extends State<TestWidget> {
       final TextEditingController textController = TextEditingController();
        @override
      void initState() {
        super.initState();
        textController.text = widget.data.textValue;
      }
      @override
      Widget build(BuildContext context) {
        return TextFormField(controller: textController,
            decoration: InputDecoration(
          labelText: widget.data.id.toString(),
          suffixIcon: IconButton(
              icon: const Icon(Icons.clear), onPressed: () => widget.onPressed!()),
        ));
      }
    }
    
    class TestWidgetData {
      final int id;
      String textValue;
    
      TestWidgetData({required this.id, required this.textValue});
    }
    

  2. You dont need to pass data to state class, you can use widget.variableName

    class TestWidget extends StatefulWidget {
      const TestWidget({super.key, this.onPressed, required this.id});
      final VoidCallback? onPressed;
      final int id;
      @override
      State<TestWidget> createState() => _TestWidgetState();
    }
    
    class _TestWidgetState extends State<TestWidget> {
      @override
      Widget build(BuildContext context) {
        return TextFormField(
            decoration: InputDecoration(
          labelText: widget.id.toString(),
          suffixIcon: IconButton(
              icon: const Icon(Icons.clear), onPressed: () => widget.onPressed!()),
        ));
      }
    }
    
    Login or Signup to reply.
  3. you have to pass a key to your TestWidget to preserve the state object of every widget here is how:

    return TestWidget(
          key: ValueKey(id), // a value key with id as a value for every TestWidget
          id: id,
          onPressed: () {
            setState(() {
              _listID.remove(id);
              print(_listID);
            });
          },
        );
    

    you can learn more about keys from the Flutter youtube channel

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search