Hello everyone, welcome back at porkaone. On this occasion we will learn to create a simple application that can carry out CRUD functions with local storage in Flutter. Come on, follow the complete tutorial below.
Local Storage
Local storage refers to the mechanism for storing data on a device locally. By using local storage, you can store data such as user settings, preferences, or even cache data persistently on the user's device.
By using local storage you don't need to access the internet to retrieve data from the database. On this occasion, we will create a simple contact application that utilizes local storage. In this tutorial you will learn CRUD, and local storage with flutter
How to Create a Contacts App in Flutter
1. Create a new flutter project with the name flutter_contact. Or with the name you want.
2. Open pubspec.yaml then add shared_preferences: ^2.0.17 then save the file to download the package, an example of which you can see in the image below.
3. Open lib/main.dart. Then fill in the script below.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_contact/add_data_page.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'edit_data_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: DataContact(),
);
}
}
class DataContact extends StatefulWidget {
@override
State<DataContact> createState() => _DataContactState();
}
class _DataContactState extends State<DataContact> {
//data variable to hold data from local storage
List<Map<String, String>> data = [];
@override
void initState() {
super.initState();
//when the state changes run the getData function
getData();
}
// function to retrieve data from SharedPreferences
Future<void> getData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
//used to get value from SharedPreferences with key 'data'
List<String> dataList = prefs.getStringList('data') ?? [];
//variable to hold the parsed data
List<Map<String, String>> parsedData = [];
//loop through dataList
for (String dataItem in dataList) {
//jsonDecode functions to convert json data into dart objects
//the converted data will be saved into the decodedData variable
Map<String, dynamic> decodedData = jsonDecode(dataItem);
Map<String, String> parsedItem = {};
//loop over decodedData
decodedData.forEach((key, value) {
parsedItem[key] = value.toString();
});
//parsed data is added to parsedData
parsedData.add(parsedItem);
}
//change state, update data
setState(() {
data = parsedData;
});
}
//method to delete data
Future<void> deleteData(int index) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
//used to get value from SharedPreferences with key 'data'
List<String> dataList = prefs.getStringList('data') ?? [];
//remove data from dataList based on index
dataList.removeAt(index);
//reset local storage with updated data
prefs.setStringList('data', dataList);
//run the getData function
getData();
}
/open EditPage page
void openEditPage(Map<String, String> data) {
Navigator.push(
context,
//function to switch pages
MaterialPageRoute(
builder: (context) => EditDataPage(data: data),
),
//function that will be executed after the update process is executed
).then((_) {
getData();
});
}
void openAddPage() {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => AddDataPage(),
),
).then((_) {
getData();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Contact App'),
),
body: data.isEmpty
? Center(
child: Text('Belum ada data'),
)
: ListView.builder(
itemCount: data.length, //total data length
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.only(top: 16),
child: ListTile(
title: Text('Name: ${data[index]['name']}'), //show name
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Email: ${data[index]['email']}'), //show email
Text('Phone: ${data[index]['phoneNumber']}'), //show phone number
Text('Address: ${data[index]['address']}'), //show address
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(Icons.edit), //icon edit
onPressed: () => openEditPage(data[index]),
),
IconButton(
icon: Icon(Icons.delete), //icon delete
onPressed: () => deleteData(index),
),
],
),
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => openAddPage(),
child: Icon(Icons.add), //FAB Icon
backgroundColor: Colors.blue, //FAB background
),
);
}
}
4. Create a new file in the lib/add_data_page.dart folder then fill it with the script below.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_contact/main.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AddDataPage extends StatefulWidget {
@override
_AddDataPageState createState() => _AddDataPageState();
}
class _AddDataPageState extends State<AddDataPage> {
//variable that we will use to check the state of the form
//used as validation variable
final formKey = GlobalKey<FormState>();
//initial text field
final TextEditingController nameController = TextEditingController();
final TextEditingController emailController = TextEditingController();
final TextEditingController addressController = TextEditingController();
final TextEditingController phoneNumberController = TextEditingController();
//function to save the form
void saveForm() {
//if the state is valid then execute data storage
if (formKey.currentState!.validate()) {
String name = nameController.text;
String email = emailController.text;
String address = addressController.text;
String phoneNumber = phoneNumberController.text;
//method to store data
saveData(name, email, address, phoneNumber);
}
}
Future<void> saveData(String name, String email, String address, String phoneNumber) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String> dataList = prefs.getStringList('data') ?? [];
Map<String, String> newData = {
'name': name,
'email': email,
'address': address,
'phoneNumber': phoneNumber
};
dataList.add(jsonEncode(newData)); // Add new data to the list
prefs.setStringList('data', dataList); // Save the data list to local storage
Navigator.pop(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add Contact Profile'),
),
//singlechildscrollview is a widget used so that the components in it can be scrolled
body: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(16),
child: Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
controller: nameController,
decoration: InputDecoration(labelText: 'Name'),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter your name';
}
return null;
},
),
TextFormField(
controller: emailController,
decoration: InputDecoration(labelText: 'Email'),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter your email';
}
return null;
},
),
TextFormField(
controller: phoneNumberController,
decoration: InputDecoration(labelText: 'Phone Number'),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter your Phone Number';
}
return null;
},
),
TextFormField(
controller: addressController,
maxLines: 5,
decoration: InputDecoration(labelText: 'Address'),
validator: (value) {
if (value!.isEmpty) {
return 'Please enter your address';
}
return null;
},
),
SizedBox(height: 16,),
ElevatedButton(
//when button is clicked execute saveForm function
onPressed: saveForm,
child: Text('Save'),
),
],
),
),
),
),
);
}
}
5. Create a new file in the lib/edit_data_page.dart folder then fill it with the script below.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class EditDataPage extends StatefulWidget {
final Map<String, String> data;
// Data variables as parameters for EditDataPage
EditDataPage({required this.data});
// EditDataPage constructor with data parameters that must be provided
@override
_EditDataPageState createState() => _EditDataPageState();
// Create EditDataPage state
}
class _EditDataPageState extends State<EditDataPage> {
TextEditingController nameController = TextEditingController();
TextEditingController emailController = TextEditingController();
TextEditingController phoneNumberController = TextEditingController();
TextEditingController addressController = TextEditingController();
// Create a TextEditingController object to control text input in the TextField
@override
void initState() {
super.initState();
initializeControllers();
// Calls the initializeControllers function when initState is called
}
void initializeControllers() {
nameController.text = widget.data['name'] ?? '';
emailController.text = widget.data['email'] ?? '';
phoneNumberController.text = widget.data['phoneNumber'] ?? '';
addressController.text = widget.data['address'] ?? '';
}
Future<void> saveData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
List<String> dataList = prefs.getStringList('data') ?? [];
Map<String, String> newData = {
'name': nameController.text,
'email': emailController.text,
'phoneNumber': phoneNumberController.text,
'address': addressController.text,
};
int dataIndex = dataList.indexOf(jsonEncode(widget.data));
// Look for the index of the data to be updated in the list using the jsonEncode function
if (dataIndex != -1) {
dataList[dataIndex] = jsonEncode(newData);
// If data is found, update it with newData
}
prefs.setStringList('data', dataList);
// Save the updated data list to SharedPreferences
Navigator.pop(context);
// Returns to the previous page after data is saved
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Edit Contact Profile'),
),
body: SingleChildScrollView(
child: Padding(
padding: EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
controller: nameController,
decoration: InputDecoration(labelText: 'Name'),
),
TextFormField(
controller: emailController,
decoration: InputDecoration(labelText: 'email'),
),
TextFormField(
controller: phoneNumberController,
decoration: InputDecoration(labelText: 'phoneNumber'),
),
TextFormField(
controller: addressController,
maxLines: 5,
decoration: InputDecoration(labelText: 'Address'),
),
ElevatedButton(
onPressed: saveData,
child: Text('Save'),
),
],
),
),
),
);
}
}
6. We have done all the steps, it's time to test it. I have included an explanation in the script above. If it's still not clear, please ask directly in the comments column below. Please run the project with the emulator, if successful then the display will look like the image below.
|
Page has no data, page adds data, page already has data |
That's it for our tutorial this time on how to create CRUD with local storage in Flutter. Hopefully this short tutorial helps. That's all and receive a salary.
0 Comments
Come on ask us and let's discuss together
Emoji