A Flutter App with Dark and Light Theme Mini Project with Source Code
The best way to learn is to do. Then what better way to sharpen your flutter skills making this flutter app with light and dark theme. In our day to day life we use many mobile and web applications which switch from dark to light theme so seamlessly. This feature can make the users very comfortable in using our app. And user comfort generally means a happy customer which in terms is ultimately pleasing to the developer.
For those who have wondered about adding this to their app, this is the perfect blog in which we make a flutter app with light and dark theme step by step with the help of GetX and Hive.
Contents
Flutter Change app Theme With GetX
Flutter App Change Theme With Getx. Theme Stage saved using Hive. instead of hive you can use get-storage or shared preference also. This is a flutter app with light and dark theme.
Features
- Light/dark mode toggle
- save theme stage
- Custom Default font
- Android ios and web
PlugIn Use
Please make sure you add this plugins for running this.flutter pub install get,hive_flutter,google_fonts
Dependencies
- After you create your flutter app using
flutter create yourApp
command in the terminal, go inside thisyourApp
directory. - Inside
yourApp
open thepubspec.yaml
file. pubspec.yaml
:
// Dependency 1
name: flutter_theme_with_getx
version: 1.0.0+1
publish_to: none
description: Flutter App Change Theme With Getx.
environment:
sdk: ">=2.15.1 <3.0.0"
dependencies:
cupertino_icons: ^1.0.2
flutter:
sdk: flutter
get: 4.6.1
google_fonts: ^2.1.1 // Dependency 2
hive_flutter: ^1.1.0 // Dependency 3
dev_dependencies:
flutter_lints: ^1.0.0
flutter_test:
sdk: flutter
flutter:
uses-material-design: true
Source Code
- Inside our lib folder is where we store all the meat and potatoes of our app.
lib/main.dart
lib/app/modules/home
lib/app/modules/home/bindings/home_binding.dart
import 'package:get/get.dart';
import '../controllers/home_controller.dart';
class HomeBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<HomeController>(
() => HomeController(),
);
}
}
lib/app/modules/home/controllers/home_controller.dart
import 'package:flutter/material.dart';
import 'package:flutter_theme_with_getx/app/theme/controller/theme_controller.dart';
import 'package:get/get.dart';
class HomeController extends GetxController
with GetSingleTickerProviderStateMixin {
final ThemeController _themeController = Get.find<ThemeController>();
Rx<String> currentModeName = ''.obs;
RxBool isDarkMode = false.obs; // Current Theme Stage
//! Not Related To Theme> Just for Animation
late Color textColor;
double fontSize = 20;
late AnimationController animationController;
//! <Not Related To Theme Just for Animation
@override
void onInit() {
//Getting theme Stage from ThemeController when homeView initialized
isDarkMode.value = _themeController.isDarkTheme;
currentModeName.value = _themeController.isDarkTheme ? 'Dark' : 'Light';
//! Not Related To Theme > Just for Animation
animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 430));
animationController.reset();
textColor = isDarkMode.value ? Colors.white : Colors.red;
fontSize = isDarkMode.value ? 30 : 20;
//! <Not Related To Theme Just for Animation
super.onInit();
}
// Change Theme Method That will call From HomeView
void changeAppTheme() => _changeTheme();
// Toggleing the Theme
bool toggleTheme() {
_changeTheme();
return isDarkMode.value;
}
// Changeing Vale for Animation
void _animate() {
if (isDarkMode.value) {
fontSize = 30;
textColor = Colors.white;
animationController.reverse();
} else {
fontSize = 20;
textColor = Colors.red;
animationController.forward();
}
}
// Calling the changeTheme Method from ThemeController
void _changeTheme() {
_themeController.changeTheme(
isDarkMode: isDarkMode,
modeName: currentModeName,
);
_animate();
}
@override
void onClose() {
animationController.dispose();
super.onClose();
}
}
lib/app/modules/home/views/home_view.dart
// import 'package:flutter/material.dart';
import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:google_fonts/google_fonts.dart';
import '../controllers/home_controller.dart';
class HomeView extends GetView<HomeController> {
const HomeView({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Theme'),
centerTitle: true,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
AnimatedBuilder(
animation: controller.animationController,
builder: (context, child) {
return Transform.rotate(
angle: controller.animationController.value * 1 * pi,
child: child,
);
},
child: Icon(
controller.isDarkMode.value
? Icons.dark_mode
: Icons.light_mode,
size: 100,
),
),
SizedBox(
height: 50,
child: Center(
child: AnimatedDefaultTextStyle(
curve: Curves.easeInOut,
duration: const Duration(milliseconds: 500),
style: GoogleFonts.ubuntuMono(
fontSize: controller.fontSize,
color: controller.textColor,
fontWeight: FontWeight.w900,
),
child:
Text(controller.currentModeName.value.toUpperCase())),
),
),
Text(
'Current theme',
style: Theme.of(context).textTheme.headline4,
),
SelectableText(
'github.com/Monzim/flutter_theme_with_getx',
style: Theme.of(context).textTheme.overline,
),
const SizedBox(height: 20),
CupertinoSwitch(
value: controller.isDarkMode.value,
onChanged: (bool val) {
controller.toggleTheme();
Get.snackbar(
'',
'',
maxWidth: 300,
titleText: Text(
'App Theme Changed',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyText2,
),
messageText: Text(
'to ${controller.currentModeName.value.toUpperCase()}',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyText2,
),
snackStyle: SnackStyle.FLOATING,
// maxWidth: 150,
mainButton: TextButton(
style: TextButton.styleFrom(primary: Colors.black),
onPressed: () {
controller.changeAppTheme();
Get.back();
},
child: Text(
'Revert',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.button,
),
),
backgroundColor:
controller.isDarkMode.value ? Colors.white : Colors.black,
snackPosition: SnackPosition.BOTTOM,
);
},
),
const SizedBox(height: 2),
ElevatedButton(
onPressed: () {
controller.changeAppTheme();
if (Get.isOverlaysOpen) {
Get.back();
}
Get.snackbar(
'',
'',
maxWidth: 300,
titleText: Text(
'App Theme Changed',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyText2,
),
messageText: Text(
'to ${controller.currentModeName.value.toUpperCase()}',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyText2,
),
snackStyle: SnackStyle.FLOATING,
// maxWidth: 150,
mainButton: TextButton(
style: TextButton.styleFrom(primary: Colors.black),
onPressed: () {
controller.changeAppTheme();
Get.back();
},
child: Text(
'Revert',
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.button,
),
),
backgroundColor: controller.isDarkMode.value
? Colors.white
: Colors.black,
snackPosition: SnackPosition.BOTTOM,
);
},
child: const Text('Change Theme')),
const SizedBox(height: 5),
],
),
),
);
}
}
lib/app/modules/initial/bindings/initial_binding.dart
import 'package:flutter_theme_with_getx/app/theme/controller/theme_controller.dart';
import 'package:get/get.dart';
import '../controllers/initial_controller.dart';
class InitialBinding extends Bindings {
@override
void dependencies() {
Get.lazyPut<ThemeController>(() => ThemeController());
Get.lazyPut<InitialController>(() => InitialController());
}
}
lib/app/modules/initial/controllers/initial_controller.dart
import 'package:get/get.dart';
class InitialController extends GetxController {}
lib/app/routes/app_pages.dart
import 'package:get/get.dart';
import '../modules/home/bindings/home_binding.dart';
import '../modules/home/views/home_view.dart';
part 'app_routes.dart';
class AppPages {
AppPages._();
static const INITIAL = Routes.HOME;
static final routes = [
GetPage(
name: _Paths.HOME,
page: () => const HomeView(),
binding: HomeBinding(),
),
// GetPage(
// name: _Paths.INITIAL,
// page: () => InitialView(),
// binding: InitialBinding(),
// ),
];
}
lib/app/routes/app_routes.dart
part of 'app_pages.dart';
// DO NOT EDIT. This is code generated via package:get_cli/get_cli.dart
abstract class Routes {
Routes._();
static const HOME = _Paths.HOME;
// static const INITIAL = _Paths.INITIAL;
}
abstract class _Paths {
_Paths._();
static const HOME = '/home';
// static const INITIAL = '/initial';
}
lib/app/theme/controller/theme_controller.dart
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:hive_flutter/hive_flutter.dart';
class ThemeController extends GetxController {
late bool isDarkTheme;
late bool themeHiveSetting;
// HiveBox for storing the theme setting
final Box<dynamic> settingsHiveBox = Hive.box('settings');
//! Getting Theme Stage From HiveBox and Set it to the ThemeMode this will be used in main.dart file
ThemeMode get themeStateFromHiveSettingBox =>
_getThemeFromHiveBox() ? ThemeMode.dark : ThemeMode.light;
@override
void onInit() {
//! Getting the Theme State from the Hive Box and save it to the variable
isDarkTheme = _getThemeFromHiveBox();
super.onInit();
}
// private Method to Get HiveBox Storage theme Setting value adn Return it
bool _getThemeFromHiveBox() {
themeHiveSetting =
settingsHiveBox.get('isDarkMode', defaultValue: Get.isDarkMode);
print(themeHiveSetting);
return themeHiveSetting;
}
// private Method to update HiveBox Storage theme Setting value
void _updateHiveThemeSetting(bool boolData) {
settingsHiveBox.put('isDarkMode', boolData);
}
// Method to change the Theme State when the user call it via Theme Chaneg Button
void changeTheme({
required RxBool isDarkMode,
required Rx<String> modeName,
}) {
if (Get.isDarkMode) {
modeName.value = 'light';
isDarkMode.value = false;
isDarkTheme = false;
_updateHiveThemeSetting(false);
_changeThemeMode(ThemeMode.light);
} else {
modeName.value = 'dark';
isDarkMode.value = true;
isDarkTheme = true;
_updateHiveThemeSetting(true);
_changeThemeMode(ThemeMode.dark);
}
}
//Private Method to change theme
void _changeThemeMode(ThemeMode themeMode) => Get.changeThemeMode(themeMode);
}
lib/app/theme/element/text_theme.dart
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
// Custom Text Styles Class For Both Dark and Light Theme
class CustomTextTheme {
//! Default Font is GoogleFonts.Ubuntu You can change it as your Need
static const _textColorLight =
Color(0xFF000000); // Light Theme Default Text Color
static const _textColorDark =
Color(0xFFFFFFFF); // Light Theme Default Text Color
static TextTheme get textThemeLight {
return _textTheme(textColor: _textColorLight);
}
static TextTheme get textThemeDark {
return _textTheme(textColor: _textColorDark);
}
// Private Method For Text Theme so that we can change the vale for Both Dark And Light Theme
static TextTheme _textTheme({required Color textColor}) {
const FontWeight _light = FontWeight.w300;
const FontWeight _medium = FontWeight.w500;
const FontWeight _regular = FontWeight.w400;
return TextTheme(
headline1: GoogleFonts.ubuntu(
fontSize: 96,
color: textColor,
fontWeight: _light,
letterSpacing: -1.5,
),
headline2: GoogleFonts.ubuntu(
color: textColor,
fontSize: 60,
fontWeight: _light,
letterSpacing: -0.5,
),
headline3: GoogleFonts.ubuntu(
color: textColor,
fontSize: 48,
fontWeight: _regular,
letterSpacing: 0.0,
),
headline4: GoogleFonts.ubuntu(
color: textColor,
fontSize: 34,
fontWeight: _regular,
letterSpacing: 0.25,
),
headline5: GoogleFonts.ubuntu(
color: textColor,
fontSize: 24,
fontWeight: _regular,
letterSpacing: 0.0,
),
headline6: GoogleFonts.ubuntu(
color: textColor,
fontSize: 20,
fontWeight: _medium,
letterSpacing: 0.15,
),
bodyText1: GoogleFonts.ubuntu(
color: textColor,
fontSize: 16,
fontWeight: _regular,
letterSpacing: 0.5,
),
bodyText2: GoogleFonts.ubuntu(
color: textColor,
fontSize: 14,
fontWeight: _regular,
letterSpacing: 0.25,
),
button: GoogleFonts.ubuntu(
color: textColor,
fontSize: 14,
fontWeight: _medium,
letterSpacing: 1.25,
),
caption: GoogleFonts.ubuntu(
color: textColor,
fontSize: 12,
fontWeight: _regular,
letterSpacing: 0.4,
),
overline: GoogleFonts.ubuntu(
color: textColor,
fontSize: 10,
fontWeight: _regular,
letterSpacing: 1.5,
),
);
}
}
lib/app/theme/dark_theme.dart
import 'package:flutter/material.dart';
import 'element/text_theme.dart';
// ignore: non_constant_identifier_names
ThemeData DarkThemeData() {
return ThemeData(
brightness: Brightness
.dark, //Setting the Brightness to Dark so that this can be used as Dark ThemeData
scaffoldBackgroundColor: Colors.black,
textTheme:
CustomTextTheme.textThemeDark, //Setting the Text Theme to DarkTextTheme
appBarTheme: const AppBarTheme(centerTitle: true),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.grey),
)),
//! You Can Set All Your Custom Dark Theme Here
);
}
lib/app/theme/light_theme.dart
import 'package:flutter/material.dart';
import 'element/text_theme.dart';
// ignore: non_constant_identifier_names
ThemeData LightThemeData() {
return ThemeData(
brightness: Brightness
.light, //Setting the Brightness to light so that this can be used as Light ThemeData
scaffoldBackgroundColor: Colors.white,
textTheme: CustomTextTheme
.textThemeLight, //Setting the Text Theme to LightTextTheme
appBarTheme: const AppBarTheme(
backgroundColor: Colors.deepOrange,
elevation: 0,
centerTitle: true,
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all<Color>(Colors.black),
)),
);
}
lib/app/theme/theme.dart
import 'theme_p.dart';
// Custom Theme Class
class CustomTheme {
static final lightTheme = LightThemeData();
static final darkTheme = DarkThemeData();
}
lib/app/theme/theme_p.dart
export 'light_theme.dart';
export 'dark_theme.dart';
Output Screenshots
Inspired by a GitHub repository by monzim, check him out!!!