In this tutorial we will build a super simple app. It will have a single switch which can toggle between dark mode and light mode. We will use Hive to persist the switch state.
(No repository yet.)
Below you can find the final code and test the app.
(Refresh this page to test persistence)
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
const darkModeBox = 'darkModeTutorial';
void main() async {
await Hive.initFlutter();
await Hive.openBox(darkModeBox);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: Hive.box(darkModeBox).listenable(),
builder: (context, box, widget) {
var darkMode = box.get('darkMode', defaultValue: false);
return MaterialApp(
themeMode: darkMode ? ThemeMode.dark : ThemeMode.light,
darkTheme: ThemeData.dark(),
home: Scaffold(
body: Center(
child: Switch(
value: darkMode,
onChanged: (val) {
box.put('darkMode', !darkMode);
},
),
),
),
);
},
);
}
}
First we create a new Flutter project:
flutter create dark_mode_switch
Now we need to add Hive to the pubspec.yaml
file in the project folder:
name: dark_mode_switch
environment:
sdk: ">=2.6.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
hive: ^1.3.0
hive_flutter: ^0.3.0+1
flutter:
uses-material-design: true
Now we can import hive
and hive_flutter
to initialize Hive.
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
const darkModeBox = 'darkModeTutorial';
void main() async {
await Hive.initFlutter();
await Hive.openBox(darkModeBox);
runApp(MyApp());
}
?> We open the box in the main()
method, so we can later use Hive.box()
and avoid dealing with async code.
The following is the main structure of our app. A Material themed app with a single Switch
in the center.
import 'package:flutter/material.dart';
void main() async {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
darkTheme: ThemeData.dark(),
home: Scaffold(
body: Center(
child: Switch(
value: false,
onChanged: (val) {},
),
),
),
);
}
}
Now we read the darkMode
entry from the box. We provide a defaultValue
because the value will be null
when the app starts for the first time.
Based on the darkMode
value we set the themeMode
of the MaterialApp
.
When the user toggles the switch, we update the darkMode
entry in the box.
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
const darkModeBox = 'darkModeTutorial';
void main() async {
await Hive.initFlutter();
await Hive.openBox(darkModeBox);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final box = Hive.box(darkModeBox);
@override
Widget build(BuildContext context) {
var darkMode = box.get('darkMode', defaultValue: false);
return MaterialApp(
themeMode: darkMode ? ThemeMode.dark : ThemeMode.light,
darkTheme: ThemeData.dark(),
home: Scaffold(
body: Center(
child: Switch(
value: darkMode,
onChanged: (val) {
box.put('darkMode', !darkMode);
},
),
),
),
);
}
}
?> We can use Hive.box(darkModeBox)
in MyApp
because we opened the box before this widget is used.
When you run the example, you will notice that it does not work as intended. The reason is that we don't refresh our widgets based on the changed darkMode
value.
The last step is to refresh the app when necessary. The easiest way to refresh widgets based on Hive changes is using box.listenable()
and ValueListenableBuilder
.
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
const darkModeBox = 'darkModeTutorial';
void main() async {
await Hive.initFlutter();
await Hive.openBox(darkModeBox);
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: Hive.box(darkModeBox).listenable(),
builder: (context, box, widget) {
var darkMode = box.get('darkMode', defaultValue: false);
return MaterialApp(
themeMode: darkMode ? ThemeMode.dark : ThemeMode.light,
darkTheme: ThemeData.dark(),
home: Scaffold(
body: Center(
child: Switch(
value: darkMode,
onChanged: (val) {
box.put('darkMode', !darkMode);
},
),
),
),
);
},
);
}
}
!> In this example we rebuild the entire app when the darkMode
value changes. You should only refresh the necessary widgets.