Menggunakan Widget Form

Pada tutorial ini kita akan bahas menggunakan form widget untuk menerima user input. Masih melanjutkan dari modul sebelumnya, kita akan gunakan aplikasi shopping cart. Agar mempermudah silakan download file aplikasi disini.

Kita buat file baru (nama file bebas), pada tutorial digunakan usr_products_scr.dart. File ini berguna sebagai screen yang akan menampilkan list product yang ditampilkan dari file usr_products_item.dart

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

import './app_drawer.dart';
import './usr_products_item.dart';
import '../models/products_provider.dart';
import './edit_prod_scr.dart';

class UsrProductsScr extends StatelessWidget {
  static const routeNm = '/usr-prods';
  @override
  Widget build(BuildContext context) {
    final prodDt = Provider.of<ProductsProvider>(context);
    return Scaffold(
      appBar: AppBar(
        title: Text('My Products'),
        actions: <Widget>[
          IconButton(
              icon: Icon(Icons.add),
              onPressed: () =>
                  Navigator.of(context).pushNamed(EditProdScr.routeNm)),
        ],
      ),
      drawer: AppDrawer(),
      body: Padding(
        padding: EdgeInsets.all(8),
        child: ListView.builder(
          itemCount: prodDt.productsList.length,
          itemBuilder: (_, i) => Column(
            children: <Widget>[
              UsrProductsItem(prodDt.productsList[i].id,
                  prodDt.productsList[i].title, prodDt.productsList[i].imgURL),
              Divider(),
            ],
          ),
        ),
      ),
    );
  }
}

Berikut isi file usr_products_item.dart. File ini berisi custom widget yang bertujuan menampilkan masing-masing product disertai IconButton edit dan delete.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './edit_prod_scr.dart';
import '../models/products_provider.dart';

class UsrProductsItem extends StatelessWidget {
  final String id;
  final String title;
  final String imgUrl;

  UsrProductsItem(this.id, this.title, this.imgUrl);

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(title),
      leading: CircleAvatar(
        backgroundImage: NetworkImage(imgUrl),
      ),
      trailing: Container(
        width: 100,
        child: Row(
          children: <Widget>[
            IconButton(
                icon: Icon(
                  Icons.edit,
                ),
                onPressed: () {
                  Navigator.of(context)
                      .pushNamed(EditProdScr.routeNm, arguments: id);
                }),
            IconButton(
                icon: Icon(
                  Icons.delete,
                  color: Colors.redAccent,
                ),
                onPressed: () {
                  Provider.of<ProductsProvider>(context, listen: false)
                      .delProduct(id);
                }),
          ],
        ),
      ),
    );
  }
}

Untuk melakukan penambahan dan editing product, kita buat file baru edit_prod_scr.dart. Disini akan digunakan widget Form.

Hal yang perlu diperhatikan adalah FocusNode() harus didispose untuk membersihkan memory yang dialokasikan.

  @override
  void dispose() {
    _imgFocusNode.removeListener(_imgListener);
    _prcFocusNode.dispose();
    _descFocusNode.dispose();
    _imgControler.dispose();
    _imgFocusNode.dispose();
    super.dispose();
  }
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/product.dart';
import '../models/products_provider.dart';

class EditProdScr extends StatefulWidget {
  static const routeNm = '/add-prod';

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

class _EditProdScrState extends State<EditProdScr> {
  final _prcFocusNode = FocusNode();
  final _descFocusNode = FocusNode();
  final _imgControler = TextEditingController();
  final _imgFocusNode = FocusNode();
  final _form = GlobalKey<FormState>();
  var _updProd = Product(id: null, title: '', desc: '', price: 0, imgURL: '');
  var _isInit = true;
  var _initVal = {'title': '', 'desc': '', 'price': '', 'imgURL': ''};

  @override
  void initState() {
    _imgFocusNode.addListener(_imgListener);
    super.initState();
  }

  @override
  void didChangeDependencies() {
    if (_isInit) {
      final pId = ModalRoute.of(context).settings.arguments as String;
      if (pId != null) {
        final _updProd = Provider.of<ProductsProvider>(context).findById(pId);
        _initVal = {
          'title': _updProd.title,
          'desc': _updProd.desc,
          'price': _updProd.price.toString(),
          'imgURL': '',
        };
        _imgControler.text = _updProd.imgURL;
      }
    }
    _isInit = false;
    super.didChangeDependencies();
  }

  @override
  void dispose() {
    _imgFocusNode.removeListener(_imgListener);
    _prcFocusNode.dispose();
    _descFocusNode.dispose();
    _imgControler.dispose();
    _imgFocusNode.dispose();
    super.dispose();
  }

  void _imgListener() {
    if (_imgFocusNode.hasFocus) {
      setState(() {});
    }
  }

  void _saveForm() {
    final isValid = _form.currentState.validate();
    if (!isValid) {
      return;
    }
    _form.currentState.save();
    if (_updProd.id != null) {
      Provider.of<ProductsProvider>(context, listen: false)
          .updProduct(_updProd.id, _updProd);
    } else {
      Provider.of<ProductsProvider>(context, listen: false)
          .addProduct(_updProd);
    }
    Navigator.of(context).pop();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Add / Edit Product'),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.save),
            onPressed: _saveForm,
          )
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Form(
          key: _form,
          child: ListView(
            children: <Widget>[
              TextFormField(
                initialValue: _initVal['title'],
                decoration: InputDecoration(labelText: 'Title'),
                textInputAction: TextInputAction.next,
                onFieldSubmitted: (_) {
                  FocusScope.of(context).requestFocus(_prcFocusNode);
                },
                validator: (val) {
                  if (val.isEmpty) {
                    return 'Please input product title';
                  }
                  return null;
                },
                onSaved: (newVal) => _updProd = Product(
                    id: _updProd.id,
                    title: newVal,
                    desc: _updProd.desc,
                    price: _updProd.price,
                    imgURL: _updProd.imgURL,
                    isFav: _updProd.isFav),
              ),
              TextFormField(
                initialValue: _initVal['price'],
                decoration: InputDecoration(labelText: 'Price'),
                textInputAction: TextInputAction.next,
                keyboardType: TextInputType.number,
                focusNode: _prcFocusNode,
                onFieldSubmitted: (_) {
                  FocusScope.of(context).requestFocus(_descFocusNode);
                },
                validator: (val) {
                  if (val.isEmpty) {
                    return 'Please input product price';
                  }
                  if (double.tryParse(val) == null) {
                    return 'Please input a valid number';
                  }
                  if (double.parse(val) <= 0) {
                    return 'Please input a price greater than zero';
                  }
                  return null;
                },
                onSaved: (newVal) => _updProd = Product(
                    id: _updProd.id,
                    title: _updProd.title,
                    desc: _updProd.desc,
                    price: double.parse(newVal),
                    imgURL: _updProd.imgURL,
                    isFav: _updProd.isFav),
              ),
              TextFormField(
                initialValue: _initVal['desc'],
                decoration: InputDecoration(labelText: 'Description'),
                maxLines: 3,
                keyboardType: TextInputType.multiline,
                focusNode: _descFocusNode,
                validator: (val) {
                  if (val.isEmpty) {
                    return 'Please input product description';
                  }
                  return null;
                },
                onSaved: (newVal) => _updProd = Product(
                  id: _updProd.id,
                  title: _updProd.title,
                  desc: newVal,
                  price: _updProd.price,
                  imgURL: _updProd.imgURL,
                  isFav: _updProd.isFav,
                ),
              ),
              Row(
                crossAxisAlignment: CrossAxisAlignment.end,
                children: <Widget>[
                  Container(
                    margin: EdgeInsets.only(right: 10, top: 8),
                    width: 100,
                    height: 100,
                    decoration: BoxDecoration(
                        border: Border.all(width: 1, color: Colors.grey)),
                    child: _imgControler.text.isEmpty
                        ? Text('Enter URL')
                        : FittedBox(
                            child: Image.network(_imgControler.text),
                            fit: BoxFit.cover,
                          ),
                  ),
                  Expanded(
                    child: TextFormField(
                      decoration: InputDecoration(labelText: 'Image URL'),
                      keyboardType: TextInputType.url,
                      textInputAction: TextInputAction.done,
                      controller: _imgControler,
                      focusNode: _imgFocusNode,
                      onFieldSubmitted: (_) => _saveForm(),
                      validator: (val) {
                        if (val.isEmpty) {
                          return 'Please input product image URL';
                        }
                        if (!val.startsWith('http') &&
                            !val.startsWith('https')) {
                          return 'Please input valid URL';
                        }
                        return null;
                      },
                      onSaved: (newVal) => _updProd = Product(
                        id: _updProd.id,
                        title: _updProd.title,
                        desc: _updProd.desc,
                        price: _updProd.price,
                        imgURL: newVal,
                        isFav: _updProd.isFav,
                      ),
                    ),
                  )
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Pada products_provider.dart kita tambahkan method untuk update product

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

class ProductsProvider with ChangeNotifier {
  List<Product> _products = [
    Product(
      id: 'p1',
      title: 'Red Shirt',
      desc: 'A red shirt - it is pretty red!',
      price: 29.24,
      imgURL:
          'https://cdn.pixabay.com/photo/2020/05/07/07/25/woman-5140454_960_720.jpg',
    ),
    Product(
      id: 'p2',
      title: 'Sport Wear',
      desc: 'A good sport',
      price: 59.21,
      imgURL:
          'https://cdn.pixabay.com/photo/2017/12/17/08/01/asia-3023824_960_720.jpg',
    ),
    Product(
      id: 'p3',
      title: 'MackBook',
      desc: 'MacBook not Surface',
      price: 1000.14,
      imgURL:
          'https://cdn.pixabay.com/photo/2014/05/02/21/47/workstation-336369_960_720.jpg',
    ),
    Product(
      id: 'p4',
      title: 'Royal Enfield',
      desc: 'Born to ride..',
      price: 3049.15,
      imgURL:
          'https://cdn.pixabay.com/photo/2015/08/27/09/06/bike-909690_960_720.jpg',
    ),
  ];

  List<Product> get productsList {
    return [..._products];
  }

  List<Product> get productsFav {
    return _products.where((element) => element.isFav).toList();
  }

  Product findById(String pid) {
    return _products.firstWhere((el) => el.id == pid);
  }

  void addProduct(Product prod) {
    final newProd = Product(
        id: DateTime.now().toString(),
        title: prod.title,
        desc: prod.desc,
        price: prod.price,
        imgURL: prod.imgURL);
    _products.add(newProd);
    notifyListeners();
  }

  void updProduct(String id, Product updProd) {
    final idx = _products.indexWhere((prod) => prod.id == id);
    if (idx >= 0) {
      _products[idx] = updProd;
      notifyListeners();
    }
  }

  void delProduct(String id) {
    _products.removeWhere((prod) => prod.id == id);
    notifyListeners();
  }

}
Sharing is caring:

Leave a Comment