Dynamic Theming in Flutter using get-storage
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