-->

CRUD Data in Flutter #2 Thêm sửa xóa dữ liệu

Trong bài trước, chúng ta đã thực hiện việc call, fetch API và hiển thị dữ liệu trên UI. Hôm nay chúng ta cùng hoàn thiện các chức năng còn lại trong series này nhé, thêm sửa xóa dữ liệu trong Flutter.


Ok, chúng ta cùng bắt đầu với từng chức năng, với các API có sẵn, mình đã giới thiệu ở bài trước.

Thêm mới dữ liệu

Tính năng sẽ được thực hiện lần này là tính năng thêm dữ liệu mới thông qua API đã được cung cấp. Nhiệm vụ của chúng ta là tạo một biểu mẫu đầu vào để người dùng có thể nhập dữ liệu họ muốn thêm vào. Tạo một tệp employee_add.dart bên trong một thư mục lib/pages và thêm code sau.

import 'package:flutter_crud_data/pages/employee.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/employee_provider.dart';

class EmployeeAdd extends StatefulWidget {
  @override
  _EmployeeAddState createState() => _EmployeeAddState();
}

class _EmployeeAddState extends State<EmployeeAdd> {
//COMMENT-3: DEFINE VARIABLE
  void submit(BuildContext context) {
    //COMMENT-2 LƯU DỮ LIỆU
  }
 @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: snackbarKey,
      appBar: AppBar(
        title: Text('Add Employee'),
        actions: <Widget>[
          FlatButton(
            child: Icon(
              Icons.save,
              color: Colors.white,
            ),
            onPressed: () => submit(context),
          )
        ],
      ),
      body: Container(
        margin: EdgeInsets.all(10),
        child: ListView(
          children: <Widget>[
            //COMMENT-1
          ],
        ),
      ),
    );
  }
}

* Lưu ý: Các bạn cần lưu ý các vị trí của các comment, sẽ cần cho các bước sau.

Giải thích: Cấu trúc đoạn mã trên sẽ hiển thị một Appbar với tiêu đề và một nút để lưu trữ dữ liệu. 

Khi nhấn vào nút (+) nó sẽ chuyển sang trang khác, để thực hiện, ta mở tệp lib/pages/employee.dart, sửa đổi mã sau thành.
floatingActionButton: FloatingActionButton(
  backgroundColor: Colors.pink,
  child: Text('+'),
  onPressed: () {
Navigator.of(context)
        .push(MaterialPageRoute(builder: (context) => EmployeeAdd()));
  },
),
Và đừng quên import:
import './employee_add.dart';
Trở lại lib/pages/employee_add.dart, thêm code sau đang để tạo ra các TextField (các input để nhập dữ liệu). Thay thế COMMENT-1 bằng code sau:
TextField(
              controller: _name,
              decoration: InputDecoration(
                focusedBorder: UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.pinkAccent,
                  ),
                ),
                hintText: 'Họ tên',
              ),
              onSubmitted: (_) {
                FocusScope.of(context).requestFocus(salaryNode);
              },
            ),
            TextField(
              controller: _salary,
              focusNode: salaryNode,
              decoration: InputDecoration(
                focusedBorder: UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.pinkAccent,
                  ),
                ),
                hintText: 'Mức lương',
              ),
              onSubmitted: (_) {
                FocusScope.of(context).requestFocus(ageNode);
              },
            ),
            TextField(
              controller: _age,
              focusNode: ageNode,
              decoration: InputDecoration(
                focusedBorder: UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.pinkAccent,
                  ),
                ),
                hintText: 'Tuổi',
              ),
            ),
Thay thế COMMENT-3 bằng code sau:
  final TextEditingController _name = TextEditingController();
  final TextEditingController _salary = TextEditingController();
  final TextEditingController _age = TextEditingController();
  final snackbarKey = GlobalKey<ScaffoldState>();

  FocusNode salaryNode = FocusNode();
  FocusNode ageNode = FocusNode();
Còn đối với hàm xử lý quá trình thêm dữ liệu, mở tệp lib/providers/employee_provider.dart và tạo phương thức mới theo đoạn mã sau
Future<bool> storeEmployee(String name, String salary, String age) async {
    final url = 'http://employee-crud-flutter.daengweb.id/add.php';
    final response = await http.post(url, body: {
      'employee_name': name,
      'employee_salary': salary,
      'employee_age': age
    });

    final result = json.decode(response.body);
    if (response.statusCode == 200 && result['status'] == 'success') {
      notifyListeners();
      return true;
    }
    return false;
  }
Để sử dụng chức năng trên, hãy mở lại tệp employee_add.dart và sửa đổi phương thức submit() thành.
void submit(BuildContext context) {
    Provider.of<EmployeeProvider>(context, listen: false)
        .storeEmployee(_name.text, _salary.text, _age.text)
        .then((res) {
      if (res) {
        Navigator.of(context).pushReplacement(
            MaterialPageRoute(builder: (context) => Employee()));
      } else {
      }
    });
  }
Lưu lại và chạy để xem kết quả.

Chỉnh sửa dữ liệu

Bước đầu tiên để hoàn thành tính năng này là tạo một tệp mới sẽ xử lý dữ liệu chỉnh sửa từ biểu mẫu, tạo tệp có tên employee_edit.dart trong thư mục lib/pages và thêm code sau:
import 'package:flutter_crud_data/pages/employee.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/employee_provider.dart';

class EmployeeEdit extends StatefulWidget {
  final String id; 
  EmployeeEdit({this.id}); 

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

class _EmployeeEditState extends State<EmployeeEdit> {
  final TextEditingController _name = TextEditingController();
  final TextEditingController _salary = TextEditingController();
  final TextEditingController _age = TextEditingController();
  bool _isLoading = false;

  final snackbarKey = GlobalKey<ScaffoldState>();

  FocusNode salaryNode = FocusNode();
  FocusNode ageNode = FocusNode();
  
  @override
  void initState() {

    Future.delayed(Duration.zero, () {

      Provider.of<EmployeeProvider>(context, listen: false).findEmployee(widget.id).then((response) {
        _name.text = response.employeeName;
        _salary.text = response.employeeSalary;
        _age.text = response.employeeAge;
      });
    });
    super.initState();
  }

  void submit(BuildContext context) {
    if (!_isLoading) {
      setState(() {
        _isLoading = true;
      });
 Provider.of<EmployeeProvider>(context, listen: false)
          .updateEmployee(widget.id, _name.text, _salary.text, _age.text)
          .then((res) {
        if (res) {
          Navigator.of(context).pushReplacement(
              MaterialPageRoute(builder: (context) => Employee()));
        } else {
        }
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      key: snackbarKey,
      appBar: AppBar(
        title: Text('Edit Employee'),
        actions: <Widget>[
          FlatButton(
            child: _isLoading
                ? CircularProgressIndicator(
              valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
            )
                : Icon(
              Icons.save,
              color: Colors.white,
            ),
            onPressed: () => submit(context),
          )
        ],
      ),
	body: Container(
        margin: EdgeInsets.all(10),
        child: ListView(
          children: <Widget>[
            TextField(
              controller: _name,
              decoration: InputDecoration(
                focusedBorder: UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.pinkAccent,
                  ),
                ),
                hintText: 'Họ tên',
              ),
              onSubmitted: (_) {
                FocusScope.of(context).requestFocus(salaryNode);
              },
            ),
            TextField(
              controller: _salary,
              focusNode: salaryNode,
              decoration: InputDecoration(
                focusedBorder: UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.pinkAccent,
                  ),
                ),
                hintText: 'Mức lương',
              ),
              onSubmitted: (_) {
                FocusScope.of(context).requestFocus(ageNode);
              },
            ),
            TextField(
              controller: _age,
              focusNode: ageNode,
              decoration: InputDecoration(
                focusedBorder: UnderlineInputBorder(
                  borderSide: BorderSide(
                    color: Colors.pinkAccent,
                  ),
                ),
                hintText: 'Tuổi',
              ),
            ),
          ],
        ),
      ),
    );
  }
}
Sau đó mở tập tin lib/pages/employee.dart và quấn widget Card() bên ListView.build() vào:
return ListView.builder(
                    itemCount: data.dataEmployee.length,
                    itemBuilder: (context, i) {
                      return ListView.builder(
                        itemCount: data.dataEmployee.length,
                        itemBuilder: (context, i) {
                          return InkWell(
                            onTap: () {
                              Navigator.of(context).push(
                                MaterialPageRoute(
                                  builder: (context) => EmployeeEdit(id: data.dataEmployee[i].id,),
                                ),
                              );
                            },
                            child: Card(
                              elevation: 8,
                              child: ListTile(
                                title: Text(
                                  data.dataEmployee[i].employeeName,
                                  style: TextStyle(
                                      fontSize: 18, fontWeight: FontWeight.bold),
                                ),
                                subtitle: Text(
                                    'Tuổi: ${data.dataEmployee[i].employeeAge}'),
                                trailing: Text(
                                    "Lương: ${data.dataEmployee[i].employeeSalary}"),
                              ),
                            ),
                          );
                        },
                      );
                    },
                  );
Và đừng quên import
import './employee_edit.dart';
Chức năng xử lý tìm kiếm dữ liệu nhân viên theo id là thêm đoạn code bên dưới vào tệp lib/providers/employee_provider.dart
Future<EmployeeModel> findEmployee(String id) async {
    return _data.firstWhere((i) => i.id == id); 
  }
Và tiếp tục thêm code sau bên dưới:
Future<bool> updateEmployee(id, name, salary, age) async {
    final url = 'http://employee-crud-flutter.daengweb.id/update.php';

    final response = await http.post(url, body: {
      'id': id,
      'employee_name': name,
      'employee_salary': salary,
      'employee_age': age
    });

    final result = json.decode(response.body); 
    if (response.statusCode == 200 && result['status'] == 'success') {
      notifyListeners(); 
      return true;
    }
    return false;
  }
Ta lưu lại và chạy thử: 
Để chỉnh sửa các item, ta nhấp vào item đó để chỉnh sửa:

Xóa dữ liệu

Có hai giai đoạn để hoàn thành chức năng xóa dữ liệu, trong đó bước đầu tiên là loại bỏ bằng hộp thoại xác nhận và bước thứ hai là chức năng đưa ra yêu cầu đối với API. Mở tệp lib/pages/employee.dart và bọc Card() bên trong ListView.builder() bằng tiện ích Dismissible.
child: Dismissible(
                          key: UniqueKey(), 
                          direction: DismissDirection.endToStart, 
                         
                          confirmDismiss: (DismissDirection direction) async {
                            
                            final bool res = await showDialog(context: context, builder: (BuildContext context) {
                              
                              return AlertDialog(
                                title: Text('Xác nhận'),
                                content: Text('Bạn có chắc muốn xóa item này?'),
                                actions: <Widget>[
                                  FlatButton(onPressed: () => Navigator.of(context).pop(true), child: Text('Xóa'),),
                                  FlatButton(onPressed: () => Navigator.of(context).pop(false), child: Text('Hủy bỏ'),)
                                ],
                              );
                            });
                            return res;
                          },
                          
			onDismissed: (value) {
                            Provider.of<EmployeeProvider>(context, listen: false).deleteEmployee(data.dataEmployee[i].id);
                          },
                          child: Card(
                            elevation: 8,
                            child: ListTile(
                              title: Text(
                                data.dataEmployee[i].employeeName,
                                style: TextStyle(
                                    fontSize: 18, fontWeight: FontWeight.bold),
                              ),
                              subtitle: Text(
                                  'Tuổi: ${data.dataEmployee[i].employeeAge}'),
                              trailing: Text(
                                  "Lương: ${data.dataEmployee[i].employeeSalary}"),
                            ),
                          ),
                        ),
Cuối cùng là tạo một hàm để thực hiện các request đến server dựa trên API đã được xác định. Mở tệp employee_provider.dart và thêm phương thức sau
 Future<void> deleteEmployee(String id) async {
    final url = 'http://employee-crud-flutter.daengweb.id/delete.php';
    await http.get(url + '?id=$id');
  }
Lưu lại và chạy thử, ta thực hiện xóa bằng cách lướt item sang trái và xác nhận để xóa:

Lời kết

Và cuối cùng mình sẽ kết thúc series CRUD data trong Flutter tại đây, các bước thực hiện khá là đơn giản, nhưng vẫn còn nhiều cách mới hơn để thực hiện CRUD. Và cụ thể sắp tới làm sẽ tiếp tục với series với hơn, với cách mới hơn, và rút gọn nhiều thời gian hơn.

Mong series này hữu ích với bạn. Chúc các bạn thành công.

By Hiếu Quốc.

Có lẽ bạn thích?

10 comments

  1. A mới comback :v rãnh ghé a chơi nha :v

    ReplyDelete
  2. Thích cái countdown kia ghê :V

    ReplyDelete
  3. Mình xin cái countdown được ko ạ lecanhduc.hutech@gmail.com

    ReplyDelete
    Replies
    1. Chào bạn, mình gửi bạn bài viết nhé.

      https://www.quochieu.com/2021/01/share-widget-em-nguoc-tet-2021-cho-blogger.html

      Delete
  4. Replies
    1. cảm ơn anh nhé. blog em cũng đang maintain 🙌

      Delete
  5. Anh cho em xin code với ạ
    buicanhcntt138@gmail.com

    ReplyDelete

Post a Comment