How to Create CRUD with Local Storage in Flutter

How to Create CRUD with Local Storage in Flutter

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.

CRUD Local Storage Flutter
Add Package




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.

membuat CRUD di Flutter
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.

Post a Comment

0 Comments