Flutter

SQflite

Hari Ke-3

Struktur Folder

pubspec.yaml

  sqflite: ^1.2.0
  path_provider: ^1.6.0
  image_picker: ^0.6.3+1
  fluttertoast: ^3.1.3

core/database/database.dart

import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';

class DatabaseHelper {
  Database _database;
  String _dbName = "restofood.db";

  Future<Database> get database async {
    if (_database == null) {
      _database = await initializeDatabase();
    }
    return _database;
  }

  Future<Database> initializeDatabase() async {
    Directory directory = await getApplicationDocumentsDirectory();
    String path = "${directory.path}/${_dbName}";
    var database = await openDatabase(path, version: 1, onCreate: _createDB);
    return database;
  }

  void _createDB(Database db, int newVersion) async {
    await db.execute('''CREATE TABLE foods (id INTEGER PRIMARY KEY AUTOINCREMENT,
      title TEXT, description TEXT, full_description TEXT, price INTEGER, image TEXT)''');
  } 
}

core/database/food_db.dart


import 'package:restofood/core/database/database.dart';
import 'package:restofood/core/models/foods_mdl.dart';
import 'package:sqflite/sqflite.dart';

class FoodsDB {
  
  DatabaseHelper helper = new DatabaseHelper();

  Future getAll() async {
    Database db = await helper.database;
    var result = await db.query("foods");
    return result;
  }

  Future<int> create(FoodModel foodModel) async {
    Database db = await helper.database;
    var result = db.insert("foods", foodModel.toMap());
    return result;
  }

  Future<int> update(FoodModel foodModel, int id) async {
    Database db = await helper.database;
    var result = db.update("foods", foodModel.toMap(), where: 'id = ?', whereArgs: [id]);
    return result;
  }

  Future delete(String id) async {
    Database db = await helper.database;
    var result = await db.rawDelete("DELETE FROM foods where id=$id");
    return result;
  }
}

core/database/foods_mdl.dart


class FoodModel {
  String id;
  String title;
  String description;
  String fullDescription;
  int price;
  String image;

  FoodModel({
    this.id, this.title, this.description,
    this.fullDescription, this.price, this.image
  });


  factory FoodModel.fromMap(Map<String, dynamic> map) {
    return FoodModel(
      id: map['id'].toString(),
      title: map['title'],
      description: map['description'],
      fullDescription: map['full_description'],
      price: int.parse(map['price'].toString()),
      image: map['image']
    );
  }


  Map<String, dynamic> toMap() {
    var map = Map<String, dynamic>();
		if (id != null) {
			map['id'] = id;
		}
		map['title'] = title;
		map['description'] = description;
    map['full_description'] = fullDescription;
		map['price'] = price;
		map['image'] = image;
		
		return map;
  }
}

core/database/foods_services.dart


import 'package:restofood/core/database/foods_db.dart';
import 'package:restofood/core/models/foods_mdl.dart';

class FoodsServices {

  static FoodsDB _foodsDB;
  
  static Future<List<FoodModel>> getAll() async {
    _foodsDB = new FoodsDB();

    var _result = await _foodsDB.getAll();
    var data = new List<FoodModel>();
    _result.forEach((foods) {
      data.add(FoodModel.fromMap(foods));
    });

    return data;
  }

  static Future<bool> create(FoodModel foodModel) async {
    _foodsDB = new FoodsDB();
    
    var _result = await _foodsDB.create(foodModel);
    if (_result != null) {
      return true;
    } else {
      return false;
    }
  }

  static Future<bool> update(FoodModel foodModel, String id) async {
    _foodsDB = new FoodsDB();

    var _result = await _foodsDB.update(foodModel, int.parse(id));
    if (_result != null) {
      return true;
    } else {
      return false;
    }
  }

  static Future<bool> delete(FoodModel foodModel) async {
    _foodsDB = new FoodsDB();

    var _result = await _foodsDB.delete(foodModel.id);
    if (_result != null) {
      return true;
    } else {
      return false;
    }
  }
}
import 'package:flutter/material.dart';

class CustomTextField extends StatelessWidget {
  TextInputAction action;
  TextInputType type;
  TextEditingController controller;
  String hintText;

  CustomTextField({
    this.action, this.type,
    this.controller, this.hintText
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      child: TextField(
        textInputAction: action,
        keyboardType: type,
        controller: controller,
        decoration: InputDecoration(
          hintText: hintText
        ),
      ),
    );
  }
}

ui/widgets/custom_textfield.dart

ui/screens/add_screen.dart

import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:image_picker/image_picker.dart';
import 'package:restofood/core/models/foods_mdl.dart';
import 'package:restofood/core/services/foods_services.dart';
import 'package:restofood/ui/widgets/custom_textfield.dart';

class AddScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.orange,
        title: Text("Add Food"),
      ),
      body: AddBody(),
    );
  }
}

class AddBody extends StatefulWidget {
  @override
  _AddBodyState createState() => _AddBodyState();
}

class _AddBodyState extends State<AddBody> {
  File image;
  var titleController = TextEditingController();
  var descriptionController = TextEditingController();
  var fullDescriptionControlelr = TextEditingController();
  var priceController = TextEditingController();

  void imagePick() async {
    var _image = await ImagePicker.pickImage(source: ImageSource.gallery);
    if (_image != null) {
      setState(() {
        image = _image;
      });
    }
  }

  void addMakanan() async {
    if (titleController.text.isNotEmpty && descriptionController.text.isNotEmpty
      && fullDescriptionControlelr.text.isNotEmpty && priceController.text.isNotEmpty
      && image != null
    ) {
      FoodModel foodModel = FoodModel(
        title: titleController.text,
        description: descriptionController.text,
        fullDescription: fullDescriptionControlelr.text,
        price: int.parse(priceController.text),
        image: base64Encode(image.readAsBytesSync())
      );

      var result = await FoodsServices.create(foodModel);

      //Jika sukses insert
      if (result) {
        Fluttertoast.showToast(
          msg: "Berhasil menambah makanan",
          toastLength: Toast.LENGTH_SHORT,
          gravity: ToastGravity.CENTER,
          timeInSecForIos: 1,
          backgroundColor: Colors.black87,
          textColor: Colors.white,
          fontSize: 16.0
        );

        Future.delayed(Duration(
          seconds: 1
        ), () {
          Navigator.pushNamedAndRemoveUntil(context, "/home", (Route<dynamic> routes) => false);
        });
        
      } else {
        Fluttertoast.showToast(
          msg: "Gagal menambah makanan",
          toastLength: Toast.LENGTH_SHORT,
          gravity: ToastGravity.CENTER,
          timeInSecForIos: 1,
          backgroundColor: Colors.black87,
          textColor: Colors.white,
          fontSize: 16.0
        );
      }
    } else {
      Fluttertoast.showToast(
        msg: "Silahkan isi semua bagian",
        toastLength: Toast.LENGTH_SHORT,
        gravity: ToastGravity.CENTER,
        timeInSecForIos: 1,
        backgroundColor: Colors.black87,
        textColor: Colors.white,
        fontSize: 16.0
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
          child: Container(
        padding: EdgeInsets.all(15),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            Center(
              child: Container(
                child: InkWell(
                  onTap: () => imagePick(),
                  child: image == null ? 
                    Icon(Icons.add_photo_alternate, color: Colors.orange, size: 100,) :
                    Image.file(image, width: 100, height: 100,),
                ),
              ),
            ),

            SizedBox(height: 20),
            CustomTextField(
              action: TextInputAction.done,
              type: TextInputType.text,
              controller: titleController,
              hintText: "Nama Makanan",
            ),
          
            SizedBox(height: 10),
            CustomTextField(
              action: TextInputAction.done,
              type: TextInputType.text,
              controller: descriptionController,
              hintText: "Deskripsi",
            ),

            SizedBox(height: 10),
            CustomTextField(
              action: TextInputAction.done,
              type: TextInputType.multiline,
              controller: fullDescriptionControlelr,
              hintText: "Full Deskripsi",
            ),

            SizedBox(height: 10),
            CustomTextField(
              action: TextInputAction.done,
              type: TextInputType.number,
              controller: priceController,
              hintText: "Harga",
            ),

            Container(
              margin: EdgeInsets.only(top: 20),
              height: 40,
              width: MediaQuery.of(context).size.width,
              child: RaisedButton(
                onPressed: () => addMakanan(),
                color: Colors.orange,
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(8)
                ),
                child: Text(
                  "Tambah Makanan",
                  style: TextStyle(
                    color: Colors.white
                  ),
                ),
              ),
            )

          ],
        ),
      ),
    );
  }
}

ui/screens/detail_screen.dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:restofood/core/models/foods_mdl.dart';
import 'package:restofood/core/services/foods_services.dart';
import 'package:restofood/ui/screens/update_screen.dart';

class DetailScreen extends StatelessWidget {
  FoodModel foodModel;
  DetailScreen({this.foodModel});

  void deleteFood(BuildContext context) async {
    await FoodsServices.delete(foodModel);
    Fluttertoast.showToast(
      msg: "Berhasil menghapus makanan",
      toastLength: Toast.LENGTH_SHORT,
      gravity: ToastGravity.CENTER,
      timeInSecForIos: 1,
      backgroundColor: Colors.black87,
      textColor: Colors.white,
      fontSize: 16.0
    );

    Future.delayed(Duration(
      seconds: 1
    ), () {
      Navigator.pushNamedAndRemoveUntil(context, "/home", (Route<dynamic> routes) => false);
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.orange,
        title: Text(foodModel.title),
        actions: <Widget>[
          Padding(
            padding: EdgeInsets.only(right: 20),
            child: InkWell(
              onTap: () => Navigator.push(context, MaterialPageRoute(
                builder: (context) => UpdateScreen(
                  foodModel: foodModel,
                )
              )),
              child: Icon(Icons.edit, color: Colors.white,)
            ),
          ),

          Padding(
            padding: EdgeInsets.only(right: 20),
            child: InkWell(
              onTap: () => deleteFood(context),
              child: Icon(Icons.delete, color: Colors.white,)
            ),
          ),
        ],
      ),
      body: DetailBody(
        foodModel: foodModel,
      ),
    );
  }
}

class DetailBody extends StatelessWidget {
  FoodModel foodModel;
  DetailBody({this.foodModel});

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Column(
        children: <Widget>[
          //bagian untuk meload gambar
          new Stack(
            children: <Widget>[
              new ClipRRect(
                borderRadius: BorderRadius.only(
                    bottomLeft: Radius.circular(15.0),
                    bottomRight: Radius.circular(15.0)),
                child: Image.memory(
                base64Decode(foodModel.image),
                  width: double.infinity,
                  height: MediaQuery.of(context).size.width / 2,
                  fit: BoxFit.cover,
                ),
              ),
              new Positioned(
                bottom: 20,
                right: 15,
                child: Container(
                  width: 300,
                  color: Colors.black38,
                  padding: EdgeInsets.symmetric(vertical: 5, horizontal: 20),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.end,
                    children: <Widget>[
                      Text(
                        foodModel.title,
                        style: TextStyle(fontSize: 26, color: Colors.white),
                      ),
                      Text(
                        "Harga: Rp ${foodModel.price}", style: TextStyle(fontSize: 15, color: Colors.grey),
                        softWrap: true,
                        overflow: TextOverflow.fade,
                      )
                    ],
                  ),
                ),
              )
            ],
          ),
          new SizedBox(height: 4,),
          new Card(
            margin: EdgeInsets.all(10),
            child: Column(
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
                  child: Row(
                    children: <Widget>[
                      new Icon(Icons.assignment, size: 20),
                      Padding(
                        padding: const EdgeInsets.only(left: 10),
                        child: new Text(
                          "Description", style: TextStyle(fontSize: 20, ),
                        ),
                      ),
                    
                    ],
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(10.0),
                  child: new Text(foodModel.fullDescription, style: TextStyle(fontSize: 15,),),
                )
              ],
            ),
          )
        ],
      ),
    );
  }
}

ui/screens/update_screen.dart

import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:image_picker/image_picker.dart';
import 'package:restofood/core/models/foods_mdl.dart';
import 'package:restofood/core/services/foods_services.dart';
import 'package:restofood/ui/widgets/custom_textfield.dart';

class UpdateScreen extends StatelessWidget {
  FoodModel foodModel;
  UpdateScreen({this.foodModel});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.orange,
        title: Text("Update Food"),
      ),
      body: UpdateFood(
        foodModel: foodModel,
      ),
    );
  }
}

class UpdateFood extends StatefulWidget {
  FoodModel foodModel;
  UpdateFood({this.foodModel});

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

class _UpdateFoodState extends State<UpdateFood> {
  File image;
  var titleController = TextEditingController();
  var descriptionController = TextEditingController();
  var fullDescriptionController = TextEditingController();
  var priceController = TextEditingController();

  void imagePick() async {
    var _image = await ImagePicker.pickImage(source: ImageSource.gallery);
    if (_image != null) {
      setState(() {
        image = _image;
      });
    }
  }

  void updateMakanan() async {
    if (titleController.text.isNotEmpty && descriptionController.text.isNotEmpty
      && fullDescriptionController.text.isNotEmpty && priceController.text.isNotEmpty
    ) {
      FoodModel foodModel = FoodModel(
        title: titleController.text,
        description: descriptionController.text,
        fullDescription: fullDescriptionController.text,
        price: int.parse(priceController.text),
        image: image != null ? base64Encode(image.readAsBytesSync()) : widget.foodModel.image
      );

      var result = await FoodsServices.update(foodModel, widget.foodModel.id);

      //Jika sukses insert
      if (result) {
        Fluttertoast.showToast(
          msg: "Berhasil mengupdate makanan",
          toastLength: Toast.LENGTH_SHORT,
          gravity: ToastGravity.CENTER,
          timeInSecForIos: 1,
          backgroundColor: Colors.black87,
          textColor: Colors.white,
          fontSize: 16.0
        );

        Future.delayed(Duration(
          seconds: 1
        ), () {
          Navigator.pushNamedAndRemoveUntil(context, "/home", (Route<dynamic> routes) => false);
        });
        
      } else {
        Fluttertoast.showToast(
          msg: "Gagal mengupdate makanan",
          toastLength: Toast.LENGTH_SHORT,
          gravity: ToastGravity.CENTER,
          timeInSecForIos: 1,
          backgroundColor: Colors.black87,
          textColor: Colors.white,
          fontSize: 16.0
        );
      }
    } else {
      Fluttertoast.showToast(
        msg: "Silahkan isi semua bagian",
        toastLength: Toast.LENGTH_SHORT,
        gravity: ToastGravity.CENTER,
        timeInSecForIos: 1,
        backgroundColor: Colors.black87,
        textColor: Colors.white,
        fontSize: 16.0
      );
    }
  }

  void loadFood() {
    setState(() {
      titleController.text = widget.foodModel.title;
      descriptionController.text = widget.foodModel.description;
      fullDescriptionController.text = widget.foodModel.fullDescription;
      priceController.text = widget.foodModel.price.toString();
    });
  }

  @override
  void initState() {
    super.initState();
    this.loadFood();
  }
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(15),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[
          Center(
            child: Container(
              child: InkWell(
                onTap: () => imagePick(),
                child: image == null ? 
                  Icon(Icons.add_photo_alternate, color: Colors.orange, size: 100,) :
                  Image.file(image, width: 100, height: 100,),
              ),
            ),
          ),

          SizedBox(height: 20),
          CustomTextField(
            action: TextInputAction.done,
            type: TextInputType.text,
            controller: titleController,
            hintText: "Nama Makanan",
          ),
        
          SizedBox(height: 10),
          CustomTextField(
            action: TextInputAction.done,
            type: TextInputType.text,
            controller: descriptionController,
            hintText: "Deskripsi",
          ),

          SizedBox(height: 10),
          CustomTextField(
            action: TextInputAction.done,
            type: TextInputType.multiline,
            controller: fullDescriptionController,
            hintText: "Full Deskripsi",
          ),

          SizedBox(height: 10),
          CustomTextField(
            action: TextInputAction.done,
            type: TextInputType.number,
            controller: priceController,
            hintText: "Harga",
          ),

          Container(
            margin: EdgeInsets.only(top: 20),
            height: 40,
            width: MediaQuery.of(context).size.width,
            child: RaisedButton(
              onPressed: () => updateMakanan(),
              color: Colors.orange,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(8)
              ),
              child: Text(
                "Update Makanan",
                style: TextStyle(
                  color: Colors.white
                ),
              ),
            ),
          )

        ],
      ),
    );
  }
}

ui/screens/home_screen.dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:restofood/core/models/foods_mdl.dart';
import 'package:restofood/core/services/foods_services.dart';
import 'package:restofood/ui/screens/add_screen.dart';
import 'package:restofood/ui/screens/detail_screen.dart';

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.orange,
        title: Text("Restofood"),
        leading: Icon(Icons.fastfood, color: Colors.white,),
        actions: <Widget>[
          Padding(
            padding: EdgeInsets.only(right: 10),
            child: InkWell(
              onTap: () => Navigator.push(context, MaterialPageRoute(
                builder: (context) => AddScreen()
              )),
              child: Icon(Icons.add_circle, color: Colors.white,)
            ),
          )
        ],
      ),
      body: HomeBody(),
    );
  }
}



class HomeBody extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.all(20),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: <Widget>[

          //Bagian ini untuk itemnya
          Text(
            "Daftar Makanan & Minuman",
            style: TextStyle(
              fontSize: 18,
              color: Colors.black87,
              fontWeight: FontWeight.bold
            ),
          ),
          SizedBox(height: 20),
          //Widget daftar makanan
          ListFood()
        ],
      ),
    );
  }
}


class ListFood extends StatefulWidget {
  @override
  _ListFoodState createState() => _ListFoodState();
}

class _ListFoodState extends State<ListFood> {

  //Instance data foods
  List<FoodModel> foods;

  //Function load data
  void loadData() async {
    var _foods = await FoodsServices.getAll();
    setState(() {
      foods = _foods;
    });
  }

  @override
  void initState() {
    super.initState();
    this.loadData();
  }
  @override
  Widget build(BuildContext context) {

    if (foods == null) {
      return Center(
        child: CircularProgressIndicator(
          backgroundColor: Colors.orange,
        ),
      );
    }

    return Container(
      child: ListView.builder(
        shrinkWrap: true,
        physics: NeverScrollableScrollPhysics(),
        itemCount: foods.length,
        itemBuilder: (context, index) {
          
          //Menambahkan item list
          return Padding(
            padding: const EdgeInsets.only(bottom: 10),
            child: Card(
              elevation: 1,
              child: InkWell(
                onTap: () => Navigator.push(context, MaterialPageRoute(
                  builder: (context) => DetailScreen(
                    foodModel: foods[index],
                  )
                )),
                child: Container(
                  padding: EdgeInsets.all(10),
                  child: Row(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[

                      //Bagian ini tambahkan image, title dan description
                      Container(
                        width: 64,
                        height: 64,
                        child: Image.memory(
                          base64Decode(foods[index].image),
                          fit: BoxFit.cover,
                        ),
                      ),
                      
                      //Memberi jarak
                      SizedBox(width: 10),

                      //Bagian untuk title dan description
                      Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          //Title
                          Text(
                            foods[index].title,
                            style: TextStyle(
                              fontSize: 16,
                              fontWeight: FontWeight.bold
                            ),
                          ),

                          //Memberi jarak
                          SizedBox(height: 5,),

                          //Description
                          Container(
                            width: MediaQuery.of(context).size.width * 0.5,
                            child: Text(
                              foods[index].description,
                              style: TextStyle(
                                fontSize: 14,
                                color: Colors.black54
                              ),
                              maxLines: 2,
                              overflow: TextOverflow.ellipsis,
                            ),
                          ),

                          //Harga
                          Container(
                            width: MediaQuery.of(context).size.width * 0.6,
                            child: Align(
                              alignment: Alignment.topRight,
                              child: Text(
                                "Rp ${foods[index].price.toString()}",
                                style: TextStyle(
                                  fontSize: 14,
                                  color: Colors.green,
                                  fontWeight: FontWeight.bold
                                ),
                              ),
                            ),
                          ),
                        ],
                      )
                    ],
                  ),
                ),
              ),
            ),
          );
        },
      )
    );
  }
}

main.dart

import 'package:flutter/material.dart';
import 'package:restofood/ui/screens/home_screen.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Restofood",
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: Colors.orange,
        accentColor: Colors.orange
      ),
      home: HomeScreen(),
      routes: {
        "/home": (context) => HomeScreen()
      },
    );
  }
}

10 Hari Jago Flutter - Hari 3

By flutter id

10 Hari Jago Flutter - Hari 3

10 Hari Jago Flutter - Hari 3

  • 930