Last active
July 31, 2023 09:18
-
-
Save Kounex/f301a5174e04343d9beaa2c0b7991d45 to your computer and use it in GitHub Desktop.
Firedart with Hive
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:firedart/firedart.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:hive/hive.dart'; | |
import 'package:path_provider/path_provider.dart'; | |
/// Using this enum to keep track of our [Hive] boxes and have a unified way | |
/// to use names used for [Hive.openBox(...)] and the [typeId] as used by the | |
/// adapters for [Hive] | |
enum HiveBox { | |
token; | |
int get typeId => index; | |
String get boxName => name; | |
} | |
/// [TokenStore] is an abstract class provided by the firedart package. To be | |
/// able to use the [FirebaseAuth.initialize(...)] later on, we need to have | |
/// an implementation of this class. The idea behind [TokenStore] is to have a | |
/// unified API to persist and retrieve the the token we receive by Firebase | |
/// later on. For the persisting part, we are going to use [Hive] | |
class HiveTokenStore extends TokenStore { | |
/// The key used in the [Token] box since [Hive] is a key-value store | |
static const _keyToken = "auth_token"; | |
/// Our singleton of this class | |
static HiveTokenStore? _instance; | |
/// We omitted the default constructor in place for a asynchronous [instance] | |
/// function which will take care of making the used [Hive] box ready and | |
/// initializing this class and keeping it as a singleton while returning | |
/// this instance | |
static Future<HiveTokenStore> instance() async { | |
if (_instance == null) { | |
/// Here we can see our enum in action. [Hive.openBox(...)] expects a | |
/// String as the identifier for the box (key-value store) to open. | |
/// With such an enum, we can make sure to be able to have an overview of | |
/// all active stores and have a type-safe way to access them (especially | |
/// because these names need to be unique) | |
var box = await Hive.openBox<Token>( | |
HiveBox.token.boxName, | |
compactionStrategy: (entries, deletedEntries) => deletedEntries > 50, | |
); | |
_instance = HiveTokenStore._internal(box); | |
} | |
return _instance!; | |
} | |
/// The [Hive] box which is being used to interact with the persistance of | |
/// our [Token] | |
final Box<Token> _box; | |
/// Only private constructor to be able to instantiate this class but only | |
/// from within - allows for the singleton approach | |
HiveTokenStore._internal(this._box); | |
@override | |
Token? read() => _box.get(_keyToken); | |
@override | |
void write(Token? token) { | |
if (token != null) { | |
_box.put(_keyToken, token); | |
} | |
/// We could throw an error here if we want | |
} | |
@override | |
void delete() => _box.delete(_keyToken); | |
} | |
/// The [TypeAdapter] for the [Token] class. Since [Token] is exposed by the | |
/// firedart package, we are not able to make use of the [Hive] annotations. | |
/// Usually when declaring a class which will serve as a persisted store, we | |
/// can make use of [Hive] annotations which with the help of code generation | |
/// will create such adapters automatically. But since this class is already | |
/// defined in the package, we need to create the adapter by ourself | |
class TokenAdapter extends TypeAdapter<Token> { | |
/// Same usage of the enum as for the name in the [Hive.openBox(...)] | |
/// function - a typesafe way to distribute unique typeIds | |
@override | |
final typeId = HiveBox.token.typeId; | |
@override | |
void write(BinaryWriter writer, Token obj) => writer.writeMap(obj.toMap()); | |
@override | |
Token read(BinaryReader reader) => Token.fromMap( | |
reader.readMap().map<String, dynamic>( | |
(key, value) => MapEntry<String, dynamic>(key, value), | |
), | |
); | |
} | |
void main() { | |
runApp(const App()); | |
} | |
class App extends StatefulWidget { | |
const App({super.key}); | |
@override | |
State<App> createState() => _AppState(); | |
} | |
class _AppState extends State<App> { | |
late Future<void> _init; | |
@override | |
void initState() { | |
super.initState(); | |
_init = _initApp(); | |
} | |
/// Since we should generally not encourage to have a asynchronous main | |
/// function (since it will block the UI because in main we are not in the | |
/// [runApp(...)] part of our app) we have a wrapper widget [App] which can | |
/// handle this asynchronous task in a [FutureBuilder] | |
Future<void> _initApp() async { | |
final appDir = await getApplicationDocumentsDirectory(); | |
Hive.init(appDir.path); | |
Hive.registerAdapter(TokenAdapter()); | |
final tokenStore = await HiveTokenStore.instance(); | |
/// TODO: add your API key here | |
FirebaseAuth.initialize('API_KEY', tokenStore); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
theme: ThemeData( | |
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), | |
useMaterial3: true, | |
), | |
home: FutureBuilder( | |
future: _init, | |
builder: (context, snapshot) { | |
if (snapshot.connectionState == ConnectionState.done && !snapshot.hasError) { | |
return const HomeView(); | |
} | |
if (snapshot.hasError) { | |
return Scaffold( | |
body: Center( | |
child: Text('Oh oh\n\n${snapshot.error!.toString()}'), | |
), | |
); | |
} | |
return const Scaffold( | |
body: Center( | |
child: CircularProgressIndicator(), | |
), | |
); | |
}, | |
), | |
); | |
} | |
} | |
class HomeView extends StatelessWidget { | |
const HomeView({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
return const Scaffold( | |
body: Center( | |
child: Text('Test'), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment