Photo by Artur Shamsutdinov on Unsplash
Flutter Basic to Advance Concepts!
Learning basic to advance concepts in Flutter using taking Note App.
Table of contents
Flutter Basic to Advance Concepts!
This is a crash course to learn a few of the advanced topics in flutter using a notes app such as state management
, changing themes
, storing data in local storage
, etc.
Final Demo video of the app:
Getting Started
Prerequisites
- Basic Knowledge about Flutter and Dart.
- Flutter SDK
- Android Studio or VSCode
To setup Flutter in Android Studio check here
To setup Flutter in VSCode check here
Running the app
- Make sure you have the Flutter SDK installed and other prerequisites.
- Fork the repository and clone it to your local machine.
- Open Android Studio or VSCode and import the project.
- To download the dependencies run
flutter pub get
at the project's root directory. - To run the app run
flutter run
at the project's root directory.
Features
- The features are divided into 3 parts
- But first, let's start with the Starter code for the app.
Starter
- In the starter code there is a
MyHomePage
class which is theHome page
, used to show the notes. - Also I have created a
custom textfiled
to use in other places. - And at last, there is a
Note
model which would contain thetitle
anddescription
of the note.
Below is the list of features that can be used to learn the topics.
Beginner level Features
- Add
date
to theNote
model. - Create a new screen to show the
Note.title
andNote.desc
. - Update and delete operations for notes.
- Create a
Snackbar
on the Update and Delete operation. - Add
Slidable
to denote the Update and Delete feature. - If you get stuck anywhere you can find the code here, and the guide can be found here.
Intermediate level Features
- Add provider package for state management.
- Separate theme data to be changed on theme toggle.
- Add shared preferences package to store the theme preference.
- Dynamic theme changing. (Light/Dark Theme)
- If you get stuck anywhere you can find the code here, and the guide can be found here.
Advance level Features
- Add sqllite database to store notes.
- Add CRUD operations in the database for notes.
- If you get stuck anywhere you can find the code here, and the guide can be found here.
Guide for implementing features:
Guide: Basic level Features:
- Code for basic level features can be found here.
Install Packages :
- To install any package run
flutter pub add <package name>
in the root of the project. - flutter_slidable
- intl
- To install any package run
Add the
date
property in theNote
model to store theDateTime
of note creation.After this, the constructor of the
Note
model will look like this: note.dartclass Note { final String title; final String desc; final DateTime date; Note({ required this.title, required this.desc, required this.date, }); }
Add
details.dart
screen to display note details.- Navigate to
details.dart
file when theListTile
is pressed. Code for details page is given below: details.dart
import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import '../models/note.dart'; class Details extends StatelessWidget { final Note note; const Details({ Key? key, required this.note, }) : super(key: key); @override Widget build(BuildContext context) { DateFormat _dateFormat = DateFormat('hh:mm, d MMMM yy'); return Scaffold( backgroundColor: Theme.of(context).backgroundColor, appBar: AppBar( backgroundColor: Theme.of(context).primaryColor, title: Text('Note Details!'), ), body: Center( child: Container( width: double.infinity, color: Theme.of(context).accentColor, margin: const EdgeInsets.all(16), padding: const EdgeInsets.all(16), child: Column( children: [ Text( note.title, textScaleFactor: 2, style: TextStyle( color: Theme.of(context).primaryColor, fontWeight: FontWeight.bold, ), ), SizedBox(height: 20), Text( 'Date: ${_dateFormat.format(note.date)}', textScaleFactor: 1.3, style: TextStyle(color: Theme.of(context).primaryColor), ), SizedBox(height: 30), Text( note.desc, textScaleFactor: 1.2, ), ]), ), ), ); } }
- Navigate to
Add a
snackbar
to show on note's CRUD operations.- Docs for
SnackBar
class can be found here - Code for snackbar is given below:
void snackbar(String message) { ScaffoldMessenger.of(context) .showSnackBar(SnackBar(content: Text(message))); }
- Docs for
Add
Slidable
as a parent widget to theListTile
and add update and delete operations to theactions
andsecondaryActions
of the widget.- Docs for
Slidable
package can be found here - With
update
anddelete
features to the_notes
list ie._notes[index] = <Updated Note>
and_notes.removeAt(index);
respectively. - (Note: I have modified the
addNote
function to_addUpdateNote
with optional parameters to update the note.) - After this, the
ListTile
withSlidable
as a parent widget will look like this: home.dartSlidable( actionPane: SlidableDrawerActionPane(), actions: [ IconSlideAction( caption: 'Update', color: Colors.blue, icon: Icons.update, onTap: () => _addUpdateDialog( title: _notes[index].title, desc: _notes[index].desc, type: 'Update', index: index, id: _notes[index].id, ), ), ], secondaryActions: [ IconSlideAction( caption: 'Delete', color: Colors.red, icon: Icons.delete, onTap: () async { setState(() { _notes.removeAt(index); }); snackbar("Deleted Note!"); }, ), ], child: ListTile( leading: Icon( Icons.note, color: Theme.of(context).primaryColor, ), title: Text( _notes[index].title, textScaleFactor: 1.2, style: TextStyle(fontWeight: FontWeight.bold), ), subtitle: Text(_notes[index].desc), trailing: Icon( CupertinoIcons.forward, color: Theme.of(context).primaryColor, size: 35, ), onTap: () => Navigator.push( context, MaterialPageRoute( builder: (context) => Details( note: _notes[index], ), ), ), ), );
- Docs for
- Finished with the Basic level Features of the app🎉.
Guide: Intermediate level Features:
- Code for intermediate-level features can be found here
Install Packages :
- To install any package run
flutter pub add <package name>
in the root of the project. - provider
- shared_preferences
- To install any package run
Create a
theme.dart
inside theutil
folder for storing theme-related constants.- Add the
isDark
property to change the theme data of the app based on the value. The
theme.dart
file will look like this: theme.dartimport 'package:flutter/material.dart'; class Styles { static ThemeData themeData({ required bool isDark, }) { return ThemeData( primarySwatch: Colors.red, primaryColor: Colors.red, accentColor: isDark ? const Color(0xFF161B22) : Colors.white, backgroundColor: isDark ? const Color(0xFF010409) : const Color(0xFFEEEEEE), brightness: isDark ? Brightness.dark : Brightness.light, visualDensity: VisualDensity.adaptivePlatformDensity, ); } }
- Add the
Add
dark_notifier.dart
for retrieving from local storage and notify the listeners of theme change- Docs for
SharedPreferences
package can be found here - Docs for
Provider
package can be found here - More details about local storage and theming be found video.
The
dark_notifier.dart
file will look like this: dark_notifier.dartimport 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; class DarkNotifier with ChangeNotifier { PrefsState _currentPrefs = const PrefsState(); DarkNotifier() { _loadDarkPref(); } Future<void> _loadDarkPref() async { await SharedPreferences.getInstance().then((prefs) { final bool darkPref = prefs.getBool('isDark') ?? false; _currentPrefs = PrefsState(darkMode: darkPref); }); notifyListeners(); } Future<void> _saveDarkPref() async { await SharedPreferences.getInstance().then((prefs) { prefs.setBool('isDark', _currentPrefs.darkMode); }); } bool get isDark => _currentPrefs.darkMode; set darkMode(bool newValue) { if (newValue == _currentPrefs.darkMode) return; _currentPrefs = PrefsState(darkMode: newValue); notifyListeners(); _saveDarkPref(); } } class PrefsState { final bool darkMode; const PrefsState({this.darkMode = false}); }
- Docs for
Changes at
main.dart
has to be done for state management and theming.The
main.dart
file will look like this: main.dartvoid main() { runApp(MultiProvider( providers: [ ChangeNotifierProvider( create: (_) => DarkNotifier(), ), ], child: MyApp(), )); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Workshop', theme: Styles.themeData( isDark: Provider.of<DarkNotifier>(context).isDark, ), debugShowCheckedModeBanner: false, home: MyHomePage(), ); } }
Add the
Switch
button on the home screen for switching the theme.- Code for the
Switch
button is given below: home.dartSwitch( value: _isDark, onChanged: (value) { _isDark = value; Provider.of<DarkNotifier>(context, listen: false).darkMode = _isDark; }, ),
- Code for the
Congratulations! Finished with the Intermediate level Features of the app🎉.
Guide: Advanced level Features:
- Code for advanced level features can be found here
Install Packages :
Add
SQlite
database to store notes.- Create SQL Table in the
main
function in the database for storing notes. - Docs of
SQlite
package can be found here - Flutter example for
SQlite
can be found here. Code to create table in
main
function is given below: main.dartvoid main() async { WidgetsFlutterBinding.ensureInitialized(); await openDatabase( join(await getDatabasesPath(), 'notes_database.db'), onCreate: (db, version) { return db.execute( 'CREATE TABLE note(id INTEGER PRIMARY KEY, title TEXT, desc Text, date Text)', ); }, version: 1, ); runApp(MultiProvider( providers: [ ChangeNotifierProvider( create: (_) => DarkNotifier(), ), ], child: MyApp(), )); }
Add CRUD operations for notes SQlite database in
models/note.dart
.- Add the
id
property inside theNote
class as the primary key for the database. Code for CRUD operations in
note.dart
is given below: note.dartimport 'package:path/path.dart'; import 'package:sqflite/sqflite.dart'; class Note { final int id; final String title; final String desc; final DateTime date; static late final Future<Database> _database; Note({ required this.id, required this.title, required this.desc, required this.date, }); static Future<void> _loadDatabase() async { _database = openDatabase( join(await getDatabasesPath(), 'notes_database.db'), version: 1, ); } Map<String, dynamic> toMap() { return { 'id': id, 'title': title, 'desc': desc, 'date': date.toString(), }; } @override String toString() { return 'Note{id: $id, title: $title, desc: $desc, data: ${date.toString()}}'; } static Future<void> insertNote(Note note) async { final db = await _database; await db.insert( 'note', note.toMap(), conflictAlgorithm: ConflictAlgorithm.replace, ); print('Note Inserted'); } static Future<List<Note>> getNotes() async { // Get a reference to the database. await _loadDatabase(); final db = await _database; // Query the table for all The Notes. final List<Map<String, dynamic>> maps = await db.query('note'); print('Note Got'); // Convert the List<Map<String, dynamic> into a List<Note>. return List.generate(maps.length, (i) { return Note( id: maps[i]['id'], title: maps[i]['title'], desc: maps[i]['desc'], date: DateTime.parse(maps[i]['date']), ); }); } static Future<void> updateNote(Note note) async { // Get a reference to the database. final db = await _database; // Update the given Note. await db.update( 'note', note.toMap(), // Ensure that the Note has a matching id. where: 'id = ?', // Pass the Note's id as a whereArg to prevent SQL injection. whereArgs: [note.id], ); print('Note Updated: ${note.id}'); } static Future<void> deleteNote(int id) async { // Get a reference to the database. final db = await _database; // Remove the Note from the database. await db.delete( 'note', // Use a `where` clause to delete a specific Note. where: 'id = ?', // Pass the Note's id as a whereArg to prevent SQL injection. whereArgs: [id], ); print('Note Deleted: $id'); } }
Check the addition of CRUD operations of
note
database in home.dart
- Add the
Congratulations! Finished with the Advanced level Features of the app🎉.