Dynamic Theming in Flutter using get-storage

Feri Lukmansyah
Zetta Tech
Published in
4 min readSep 16, 2023
Photo by Grabster on Unsplash

Get Storage in Flutter is a high-speed, extra lightweight & synchronous key-value pair package that will help you to store app data in memory. It is written entirely in Dart and easily integrates with the Get framework of Flutter. Generally, In flutter, we use Shared preference to store app data. It is an alternative to app-shared preference.

Feature of Get storage

GetStorage is slow, it is absurdly fast for being memory-based.

All of its operations are instantaneous.

A backup of each operation is placed in a Container on the disk. Each container has its own file.

We have multiple benefits of using get storage in a flutter. Now, let’s check how we can implement get storage in a flutter.

The Study Case

I’m making a notes application using Django as the backend and Flutter as the frontend, where I’m working on the theme change notification feature, using Getx and Getx-storage, here I’m using the frontend approach first, here’s the discussion

Project Setup

Before Following this article make sure Flutter is already installed on your computer

Create a Flutter Project

create a flutter project with a command

flutter create notesapps

After creating the project, proceed with installing the required dependencies

flutter pub add get get_storage intl google_fonts flutter_local_notifications flutter_native_timezone

after installing the dependencies, override MaterialApp class to Getx Material App on main.dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';
import 'package:notesapps/services/themes_service.dart';
import 'package:notesapps/ui/pages/home_page.dart';
import 'package:notesapps/ui/partials/theme.dart';

void main() async {
// get storage initialization
WidgetsFlutterBinding.ensureInitialized();
await GetStorage.init();
runApp(const MyApp());
}

class MyApp extends StatefulWidget {
const MyApp({super.key});

@override
State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return GetMaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: Themes.light,
darkTheme: Themes.dark,
themeMode: ThemeService().theme,
home: const HomePage(),
);
}
}

Let’s jump to code, I will explain per code comments

The Code Explanation

on the main function, we initialize get storage, on this function, we can set up local notifications and send notifications to our apps, look at this line

void main() async {
// get storage initialization
WidgetsFlutterBinding.ensureInitialized();
await GetStorage.init();
runApp(const MyApp());
}

Create New Service for Theme

the first step let’s set up the logic for theme change if we click a button

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:get_storage/get_storage.dart';

class ThemeService {
final GetStorage _box = GetStorage();
final String _key = 'isDarkMode';

bool _loadThemeFromBox() => _box.read(_key) ?? false;
ThemeMode get theme => _loadThemeFromBox() ? ThemeMode.dark : ThemeMode.light;

// save theme to box
Future<void> _saveThemeToBox(bool isDarkMode) => _box.write(_key, isDarkMode);

// load theme
bool loadThemeFromBox() => _box.read(_key) ?? false;

// function (method) to change theme
void switchTheme() {
Get.changeThemeMode(_loadThemeFromBox() ? ThemeMode.light : ThemeMode.dark);
_saveThemeToBox(!_loadThemeFromBox());
}
}

next is to set up the homepage UI and Theme UI

// lib/ui/pages/home_pages.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:notesapps/services/notification_service.dart';
import 'package:notesapps/ui/partials/app_bar.dart';
import 'package:notesapps/ui/partials/theme.dart';

class HomePage extends StatefulWidget {
const HomePage({super.key});

// initialize notifications

@override
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
late NotifyHelper notifyHelper;

@override
void initState() {
// TODO: implement initState
super.initState();
notifyHelper = NotifyHelper();
notifyHelper.initializeNotification();
notifyHelper.requestIOSPermissions();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: appBar(),
body: Column(
children: [
Row(
children: [
Container(
margin: const EdgeInsets.symmetric(horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
DateFormat.yMMMMd().format(DateTime.now()),
style: subHeadingStyle,
),
Text("Today", style: headingStyle,)
],
),
)
],
)
],
));
}
}

after creating the homepage setup theme partials, font, and subheading

// lib/ui/partials/theme.dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:get/get.dart';

// colors pallets
const Color bluishClr = Color(0xFF4e5ae8);
const Color orangeClr = Color(0xCFFF8746);
const Color pinkClr = Color(0xFFff4667);
const Color white = Colors.white;
const Color primaryClr = bluishClr;
const Color darkGreyClr = Color(0xFF121212);
const Color darkHeaderClr = Color(0xFF424242);

// ! Custom Theme on Dark and Light Theme
class Themes {
static final ThemeData light = ThemeData(
primaryColor: Colors.red,
brightness: Brightness.light,
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.deepPurple),
);

static final ThemeData dark = ThemeData(
primaryColor: darkGreyClr,
brightness: Brightness.dark,
);
}

// subheading style

TextStyle get subHeadingStyle {
return GoogleFonts.lato(
textStyle: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Get.isDarkMode ? Colors.grey[400] : Colors.grey));
}

// heading style

TextStyle get headingStyle {
return GoogleFonts.lato(
textStyle: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
color: Get.isDarkMode ? Colors.grey[400] : Colors.grey));
}

we can set UI for themes on this file, so we can custom on theme.dart after that create app_bar.dart

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:notesapps/services/notification_service.dart';
import 'package:notesapps/services/themes_service.dart';

var notifyHelper = NotifyHelper();

AppBar appBar() {
return AppBar(
elevation: 0,
leading: GestureDetector(
onTap: () {
ThemeService().switchTheme();
notifyHelper.displayNotification(
title: "Theme Changed",
body: Get.isDarkMode
? "Activated Light Theme"
: "Activate Dark Theme");
},
child: Icon(
Get.isDarkMode ? Icons.wb_sunny_outlined : Icons.nightlight_round_outlined,
size: 20,
color: Get.isDarkMode ? Colors.white : Colors.black),
),
actions: const [
Icon(
Icons.person,
size: 20,
),
SizedBox(
width: 20,
)
],
);
}

Creating Notification Services

For Creating a notification service

import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:get/get.dart';

class NotifyHelper {
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin(); //

initializeNotification() async {
//tz.initializeTimeZones();
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
onDidReceiveLocalNotification: onDidReceiveLocalNotification);

const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings("appicon");

final InitializationSettings initializationSettings =
InitializationSettings(
iOS: initializationSettingsIOS,
android: initializationSettingsAndroid,
);

await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: selectNotification);
}

// display notification service
displayNotification({required String title, required String body}) async {
print("doing test");
var androidPlatformChannelSpecifics = const AndroidNotificationDetails(
'your channel id', 'your channel name', 'your channel description',
importance: Importance.max, priority: Priority.high);
var iOSPlatformChannelSpecifics = const IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: iOSPlatformChannelSpecifics);

// core setting of notifications
await flutterLocalNotificationsPlugin.show(
0,
title,
body,
platformChannelSpecifics,
payload: 'Default_Sound',
);
}

// request IOS Permission
void requestIOSPermissions() {
flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
}

Future selectNotification(String? payload) async {
if (payload != null) {
print('notification payload: $payload');
} else {
print("Notification Done");
}
Get.to(() => Container(
color: Colors.white,
));
}

Future onDidReceiveLocalNotification(
int id, String? title, String? body, String? payload) async {

Get.dialog(const Text("Welcome to Flutter"));
}
}

so we can send a notification directly when we changed the theme mode

--

--