Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating single Listview item in Flutter using a showdialog and textfield

We have created a flutter app that displays a listview from a list of items. We want to be able to update these based on user preference. Sort of like if you had a to-do list and wanted to correct or update one of the items on the list using a modal or a dialog box.

I have been trying to update a single item in the listview with a dialog box which the user types on and then closes on submit. So far I've had no luck, but I feel I am very close. How can I get the name on the tile to update on submit and re-render the page? What is it that I am missing?

import 'package:flutter/material.dart';

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

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

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  List<Animal> animals = new List();
  String newName;
  TextEditingController nctrl = TextEditingController();

  @override
  void initState() {
    super.initState();
    animals = [
      Animal(id: 1, name: 'cat'),
      Animal(id: 2, name: 'dog'),
      Animal(id: 3, name: 'mouse'),
      Animal(id: 4, name: 'horse'),
      Animal(id: 5, name: 'frog'),
    ];
  }

  _changePetName() {
    newName = nctrl.text;
    Navigator.pop(context);
    return newName;
  }

  _showDialog(String name) {
    nctrl.text = name;
    showDialog(
      context: context,
      builder: (BuildContext context) {
        // return object of type Dialog
        return AlertDialog(
          elevation: 5,
          backgroundColor: Colors.blue,
          title: Text(
            "Rename this pet",
          ),
          content: Container(
              child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              TextField(
                controller: nctrl,
                onChanged: (e) {
                  setState(() {});
                },
              ),
            ],
          )),
          actions: <Widget>[
            // usually buttons at the bottom of the dialog
            FlatButton(
              child: Text("Close"),
              onPressed: () {
                Navigator.of(context).pop();
              },
            ),
            FlatButton(
              color: Colors.green,
              child: Text(
                "Submit",
              ),
              onPressed: () {
                var updateName = _changePetName();
                return updateName;
              },
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ListView.builder(
              scrollDirection: Axis.vertical,
              shrinkWrap: true,
              itemCount: animals.length,
              itemBuilder: (BuildContext context, int index) {
                var pet = animals[index];
                return ListTile(
                  key: ValueKey(pet.id),
                  enabled: true,
                  onTap: () async {
                    var rename = await _showDialog(pet.name);

                    if (rename != null) {
                      pet.name = rename;
                      setState(() {});
                    }
                    // setState(() {
                    //   pet.name = 'bob';
                    // });
                  },
                  title: Text(pet.name),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

class Animal {
  final int id;
  String name;

  Animal({this.id, this.name});

  factory Animal.fromJson(Map<dynamic, dynamic> json) {
    return new Animal(
      id: json['id'],
      name: json['name'],
    );
  }

  Map<dynamic, dynamic> toJson() {
    final Map<dynamic, dynamic> data = new Map<dynamic, dynamic>();
    data['id'] = this.id;
    data['name'] = this.name;
    return data;
  }
}
like image 561
Ivan Trejo Avatar asked Dec 31 '22 10:12

Ivan Trejo


1 Answers

Few corrections that will make your code work:

  1. show dialog should be of type String so that the result it returns is of type String.
  2. While poping dialog box you have to pass data in pop().
  3. await showDialog method to receive it's result.

Following is the working code for your reference:

import 'package:flutter/material.dart';

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

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

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  List<Animal> animals = new List();
  String newName;
  TextEditingController nctrl = TextEditingController();

  @override
  void initState() {
    super.initState();
    animals = [
      Animal(id: 1, name: 'cat'),
      Animal(id: 2, name: 'dog'),
      Animal(id: 3, name: 'mouse'),
      Animal(id: 4, name: 'horse'),
      Animal(id: 5, name: 'frog'),
    ];
  }

  _changePetName() {
    newName = nctrl.text;
    Navigator.pop(context, newName);
    return newName;
  }

  Future<String> _showDialog(String name) async {
    nctrl.text = name;
    return await showDialog<String>(
      context: context,
      builder: (BuildContext context) {
        // return object of type Dialog
        return AlertDialog(
          elevation: 5,
          backgroundColor: Colors.blue,
          title: Text(
            "Rename this pet",
          ),
          content: Container(
              child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              TextField(
                controller: nctrl,
                onChanged: (e) {
                  // setState(() {});
                },
              ),
            ],
          )),
          actions: <Widget>[
            // usually buttons at the bottom of the dialog
            FlatButton(
              child: Text("Close"),
              onPressed: () {
                Navigator.of(context).pop(nctrl.text);
              },
            ),
            FlatButton(
              color: Colors.green,
              child: Text(
                "Submit",
              ),
              onPressed: () {
                var updateName = _changePetName();
                return updateName;
              },
            ),
          ],
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ListView.builder(
              scrollDirection: Axis.vertical,
              shrinkWrap: true,
              itemCount: animals.length,
              itemBuilder: (BuildContext context, int index) {
                var pet = animals[index];
                return ListTile(
                  key: ValueKey(pet.id),
                  enabled: true,
                  onTap: () async {
                    var rename = await _showDialog(pet.name);
                    if (rename != null) {
                      pet.name = rename;
                      setState(() {});
                    }
                    // setState(() {
                    //   pet.name = 'bob';
                    // });
                  },
                  title: Text(pet.name),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

class Animal {
  final int id;
  String name;

  Animal({this.id, this.name});

  factory Animal.fromJson(Map<dynamic, dynamic> json) {
    return new Animal(
      id: json['id'],
      name: json['name'],
    );
  }

  Map<dynamic, dynamic> toJson() {
    final Map<dynamic, dynamic> data = new Map<dynamic, dynamic>();
    data['id'] = this.id;
    data['name'] = this.name;
    return data;
  }
}

If this solution works for you please accept and up-vote the answer.

like image 160
Kalpesh Kundanani Avatar answered Jan 02 '23 23:01

Kalpesh Kundanani