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.3core/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