Flutter callbacks - Removing a widget in a list from child

Viewed 1058

I have this app where you type in some text and press a button which adds this text to a custom widget. Here is the code:

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

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

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

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int count = 0;
  TextEditingController noteSend = TextEditingController();
  List<String> noteString = [];

  @override
  Widget build(BuildContext context) {
    List<Widget> children = new List.generate(
        count,
        (int i) => new InputWidget(i,
            noteRec: noteString[i], noteString: noteString, count: count));

    return new Scaffold(
        appBar: new AppBar(title: new Text('some title')),
        body: Column(
          children: <Widget>[
            Container(
              child: TextField(
                controller: noteSend,
              ),
              color: Colors.lightBlueAccent,
              width: 150,
              height: 50,
            ),
            Expanded(
              child: ListView(
                children: children,
                scrollDirection: Axis.vertical,
              ),
            ),
          ],
        ),
        floatingActionButton: new FloatingActionButton(
          child: new Icon(Icons.add),
          onPressed: () {
            setState(() {
              noteString.insert(
                noteString.length,
                noteSend.text,
              );
              count = count + 1;
            });
          },
        ));
  }
}

class InputWidget extends StatefulWidget {
  final int index;
  final String noteRec;
  final List<String> noteString;
  final int count;

  InputWidget(this.index, {Key key, this.noteRec, this.noteString, this.count})
      : super(key: key);

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

class _InputWidgetState extends State<InputWidget> {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onLongPress: () {
        //                            <-- onLongPress
        setState(() {
          widget.noteString.removeAt(widget.index);
        });
      },
      child: new Container(
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(10),
        ),
        child: Row(
          children: <Widget>[
            Column(
              children: <Widget>[
                Icon(
                  Icons.image,
                  size: 75,
                )
              ],
            ),
            Container(
              margin: EdgeInsets.only(left: 80, right: 30),
              child: Column(
                children: <Widget>[
                  Text('Note'),
                ],
              ),
            ),
            Column(
              children: <Widget>[
                Text("${widget.noteRec}"),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

I've tried to wrap the custom which in a gesture detector and then add a onLongPress which removes the widget at the index but it's not working. How can I remove the widget I long press on ? Thank you

1 Answers

For this scenario, you'll need to use a callback. Because of the scope of the variables, you can't directly delete a variable from noteString in the InputWidget() due to scope, however, it HAS to be triggered by InputWidget because the index information is contained in that widget and has to be used to remove the items from the noteString List, as well as removing the InputWidget from the children List. Therefore, it's callback time.

Callbacks work like this: 1. define a variable that will receive a function in the child.

final Function(int) onDelete;

2. Call the function in the child and pass in the needed variable:

onLongPress: () {
        widget.onDelete(widget.index);
      },

3. Define the function in the parent THAT YOU WANT TO USE IN THE PARENT and then pass it to the child:

Function(int) onDeleteVar = (int val) {
  setState(
    () => {
      noteString.removeAt(val),
      count--,
      children.removeAt(val),
    },
  );
};

children = List.generate(
    count,
    (int i) => new InputWidget(i,
        noteRec: noteString[i],
        noteString: noteString,
        count: count,
        onDelete: onDeleteVar));

Here's a dartpad to see it in action: http://dartpad.dev/a25e9c402a90fefc778bcfac27aee242

And here's the code:

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

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

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

class MyHomePage extends StatefulWidget {
  @override
  MyHomePageState createState() => MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  int count = 0;
  TextEditingController noteSend = TextEditingController();
  List<String> noteString = [];

  @override
  Widget build(BuildContext context) {
    List<Widget> children;
    Function(int) onDeleteVar = (int val) {
      setState(
        () => {
          print("noteStringBefore: $noteString"),
          print('childrenBefore: $children'),
          print(val),
          noteString.removeAt(val),
          count--,
          children.removeAt(val),
          print("noteStringAfter: $noteString"),
          print('childrenAfter $children'),
        },
      );
    };

    children = List.generate(
        count,
        (int i) => new InputWidget(i,
            noteRec: noteString[i],
            noteString: noteString,
            count: count,
            onDelete: onDeleteVar));

    return new Scaffold(
        appBar: new AppBar(title: new Text('some title')),
        body: Column(
          children: <Widget>[
            Container(
              child: TextField(
                controller: noteSend,
              ),
              color: Colors.lightBlueAccent,
              width: 150,
              height: 50,
            ),
            Expanded(
              child: ListView(
                children: children,
                scrollDirection: Axis.vertical,
              ),
            ),
          ],
        ),
        floatingActionButton: new FloatingActionButton(
          child: new Icon(Icons.add),
          onPressed: () {
            setState(() {
              noteString.insert(
                noteString.length,
                noteSend.text,
              );
              count = count + 1;
            });
          },
        ));
  }
}

class InputWidget extends StatefulWidget {
  final int index;
  final String noteRec;
  final List<String> noteString;
  final int count;
  final Function(int) onDelete;

  InputWidget(this.index,
      {Key key, this.noteRec, this.noteString, this.count, this.onDelete})
      : super(key: key);

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

class _InputWidgetState extends State<InputWidget> {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onLongPress: () {
        //                            <-- onLongPress
        widget.onDelete(widget.index);
      },
      child: new Container(
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(10),
        ),
        child: Row(
          children: <Widget>[
            Column(
              children: <Widget>[
                Icon(
                  Icons.image,
                  size: 75,
                )
              ],
            ),
            Container(
              margin: EdgeInsets.only(left: 80, right: 30),
              child: Column(
                children: <Widget>[
                  Text('Note'),
                ],
              ),
            ),
            Column(
              children: <Widget>[
                Text("${widget.noteRec}"),
              ],
            ),
          ],
        ),
      ),
    );
  }
}
Related