🔖

[Flutter]動的にTextFieldを追加, 削除できるUI

2021/05/19に公開

動的にTextFieldを追加, 削除できるUIを作るのに地味に手間取ってしまったので, メモとして残しておく.

Item

表示用のクラスとしてItemクラスを作成した.
適当なIDとテキストとコントローラーを保持する.
TextEditingControllerを持っているので, 不要になったらdiposeする必要がある.

class Item {
  final int id;
  final TextEditingController controller;
  final String text;

  Item({
     this.id,
     this.text,
     this.controller,
  });

  factory Item.create(String text) {
    return Item(
      id: Random().nextInt(99999),
      text: text,
      controller: TextEditingController(text: text),
    );
  }

  Item change(String text) {
    return Item(id: this.id, text: text, controller: this.controller);
  }

  void dispose() {
    controller.dispose();
  }

  
  String toString() {
    return text;
  }
}

UI

Itemを削除するときに, Itemの持っているcontrollerをdisposeする必要があると思うのだが, 直ぐに削除すると, まだcontrollerが使われている的なエラーが出たので, 1秒ほどおいてからdisposeした. もっといい方法がありそうだが動いたのでよしとする.

class Home extends StatefulWidget {
  
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  List<Item> items = [];

  
  void dispose() {
    items.forEach((element) {
      element.dispose();
    });

    super.dispose();
  }

  void add() {
    setState(() {
      items.add(Item.create(""));
    });
  }

  void remove(int id) {
    final removedItem = items.firstWhere((element) => element.id == id);
    setState(() {
      items.removeWhere((element) => element.id == id);
    });

    // itemのcontrollerをすぐdisposeすると怒られるので
    // 少し時間をおいてからdipose()
    Future.delayed(Duration(seconds: 1)).then((value) {
      removedItem.dispose();
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        padding: EdgeInsets.all(32),
        child: ListView(
          children: [
            Text(items.toString()),
            ...items.map((item) => textFieldItem(item)),
            ElevatedButton(
              onPressed: () {
                add();
              },
              child: Text("追加"),
            ),
          ],
        ),
      ),
    );
  }

  Widget textFieldItem(
    Item item,
  ) {
    return Row(
      children: [
        Expanded(
          child: TextField(
            controller: item.controller,
            onChanged: (text) {
              setState(() {
                items = items
                    .map((e) => e.id == item.id ? item.change(text) : e)
                    .toList();
              });
            },
          ),
        ),
        IconButton(
          icon: Icon(Icons.close),
          onPressed: () {
            remove(item.id);
          },
        )
      ],
    );
  }
}

続き: ([Flutter] 並び替えできて動的にTextFieldを追加, 削除できるUI)[https://zenn.dev/soraef/articles/999ae5da39ea61]

Discussion