Giter Club home page Giter Club logo

felangel / hydrated_bloc Goto Github PK

View Code? Open in Web Editor NEW
189.0 13.0 24.0 1.77 MB

An extension to the bloc state management library which automatically persists and restores bloc states.

Home Page: https://pub.dev/packages/hydrated_bloc

License: MIT License

Java 0.92% Ruby 15.30% Objective-C 2.00% Dart 52.05% Makefile 15.00% C++ 6.19% Swift 3.23% C 2.16% Kotlin 0.31% HTML 2.84%
flutter dart flutter-package state-management state dartlang dart-library dart-package persistence cache

hydrated_bloc's Introduction

⚠️ Attention: This repository has been moved to https://github.com/felangel/bloc and is now read-only!

Hydrated Bloc

Pub Version Build Status Code Coverage style: effective dart MIT License Starware Bloc Library

An extension to the bloc state management library which automatically persists and restores bloc states and is built on top of hydrated_cubit.

Overview

hydrated_bloc exports a Storage interface which means it can work with any storage provider. Out of the box, it comes with its own implementation: HydratedStorage.

HydratedStorage is built on top of path_provider for a platform-agnostic storage layer. The out-of-the-box storage implementation reads/writes to file using the toJson/fromJson methods on HydratedBloc and should perform very well for most use-cases (performance reports coming soon). HydratedStorage is supported for desktop (example).

Usage

1. Use HydratedStorage

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  HydratedBloc.storage = await HydratedStorage.build();
  runApp(App());
}

2. Extend HydratedBloc and override fromJson/toJson

enum CounterEvent { increment, decrement }

class CounterBloc extends HydratedBloc<CounterEvent, int> {
  CounterBloc() : super(0);

  @override
  Stream<int> mapEventToState(CounterEvent event) async* {
    switch (event) {
      case CounterEvent.decrement:
        yield state - 1;
        break;
      case CounterEvent.increment:
        yield state + 1;
        break;
    }
  }

  @override
  int fromJson(Map<String, dynamic> json) => json['value'] as int;

  @override
  Map<String, int> toJson(int state) => { 'value': state };
}

Now our CounterBloc is a HydratedBloc and will automatically persist its state. We can increment the counter value, hot restart, kill the app, etc... and our CounterBloc will always retain its state.

Custom Storage Directory

By default, all data is written to temporary storage which means it can be wiped by the operating system at any point in time.

An optional storageDirectory can be provided to override the default temporary storage directory:

HydratedBloc.storage = await HydratedStorage.build(
  storageDirectory: await getApplicationDocumentsDirectory(),
);

Custom Hydrated Storage

If the default HydratedStorage doesn't meet your needs, you can always implement a custom Storage by simply implementing the Storage interface and initializing HydratedBloc with the custom Storage.

// my_hydrated_storage.dart

class MyHydratedStorage implements Storage {
  @override
  dynamic read(String key) {
    // TODO: implement read
  }

  @override
  Future<void> write(String key, dynamic value) async {
    // TODO: implement write
  }

  @override
  Future<void> delete(String key) async {
    // TODO: implement delete
  }

  @override
  Future<void> clear() async {
    // TODO: implement clear
  }
}
// main.dart

HydratedBloc.storage = MyHydratedStorage();

Maintainers

Supporters

Starware

Hydrated Bloc is Starware.
This means you're free to use the project, as long as you star its GitHub repository.
Your appreciation makes us grow and glow up. ⭐

hydrated_bloc's People

Contributors

felangel avatar misterfourtytwo avatar orsenkucher avatar rodydavis avatar zepfietje avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

hydrated_bloc's Issues

Hydrated Bloc yields the error state

Describe the bug
So i switched from flutterbloc to hydaretdbloc, when i my switch on the data connection and i do not have an active internet connection the hydrated bloc works but when i complete shutdown the WiFi & the data connection it does not work

Expected behavior
Load the cached rest data rather than yielding the error state

Question: Scoping bloc persistence by ID

First, thank you for creating such an easy to use framework for creating and persisting blocs!

I've implemented a hydrated bloc which I want to restore bloc state based on a unique ID that is contextually relevant to the bloc. As an example, imagine a user profile page on Facebook. If I were to use the bloc persistence for a users profile, then go to another users profile it would restore the first user's profile for me since the persisted bloc wasn't scoped by ID for the user.

To get around this I made my LoadedState work with JsonSerializable() then implemented the following toJson/fromJson in my bloc

  @override
  StoryEpisodesState fromJson(Map<String, dynamic> json) {
    if (json['story_id'] == this.repository.client.storyID) {
      return StoryEpisodesStateLoaded.fromJson(json['state']);
    }
  }

  @override
  Map<String, dynamic> toJson(StoryEpisodesState state) {
    if (state is StoryEpisodesStateLoaded) {
      return {
        'story_id': this.repository.client.storyID,
        'state': state.toJson(),
      };
    }
  }

The problem with the above is that it is only storing one JSON at a time which means it will only persist the most recently visited story and all stories visited before that load from scratch again.

Is there a better way to accomplish this such that each page that I visit will be cached and stored for future use allowing me to store bloc states for as many pages as I want?

best,

Kieran

Add support for HiveDB

Hello,
Actually i'm using Hydrated_bloc not as a cache for my app but more as adatabase.
I have two problems with it :
First, hydrated bloc use temporary temporary storage so it can be wiped at any time.
Second this do not support any encryption.
I would sugget to implement Hive db as a way to persist data for two reasons :
First hive support encryption,
Hive is very light and powerfull and it can work on mobile, web and even desktop.

If you decide to add hive support please take in consideration to not init Hive inside a tempdir like is the case actually, so i could use it as a dataabase and not as a cache.
Thank you for your help,
Michel M

Read 1000 entries Write 1000 entries
SharedPreferences is on par with Hive when it comes to read performance. SQLite performs much worse. Hive greatly outperforms SQLite and SharedPreferences when it comes to writing or deleting.

BLoC Replay or Time Travel

With the implementation of this hydrater, I wonder if we can move towards better tooling that helps us by actually introducing a time-travel functionality, similar to what is done on React (but I guess since BLoC) are local to their components or widgets, we would need to have some kind of registry to replay all bloc events and register that. What are your thoughts on this?

Honestly, I love your BLoC library and all the useful tools you have built with this. The tutorial is also really well done. I'm super excited to see more features and functionality come out in the future.

In my personal opinion though, having a "replay" feature just really helps with debugging. What I'm currently doing is actually stepping through and seeing all the events and states I'm taking. This would greatly be simplified with time-travel.

Not sure if you are aware of this, but the package is here: https://medium.com/the-web-tub/time-travel-in-react-redux-apps-using-the-redux-devtools-5e94eba5e7c0

Support saving bloc's state instead of The state that bloc produces.

Is your feature request related to a problem? Please describe.
I have a repositoryBloc that caches results. When using HydratedBloc, I can only store the output of the bloc, not it's internal private state.
Describe the solution you'd like
Don't require the fromJson method of HydratedBloc<E, S> to be of type S
Describe alternatives you've considered
I've considered customizing the output of toJson and fromJson, but it seems complicated and probably impossible.
An alternative is to allow for persisting extra bloc data.

State is replicated in Hive file

Describe the bug
Each time a state transition happens a new copy of the state is appended to the Hive DB file hydrated_box.hive causing it to grow linearly in size.

To Reproduce
Steps to reproduce the behavior:

  1. Run the example app in the example folder
  2. Run adb shell in a terminal window
  3. Inside the adb shell run the command run-as com.example.example
  4. Run ls -l cache/hydrated_box.hive
  5. Press the plus button in the app
  6. Run ls -l cache/hydrated_box.hive again

Expected behavior
Size of hydrated_box.hive should be the same.

Is it possible sync with Firestore?

I have to write lot of CRUD lately.

It maybe a much better approach then using CRUD event in the Flutter Bloc

Then all I have to do is keep track of the uid, I think

Please advice.

Asynchronous fromJson and toJson

Is your feature request related to a problem? Please describe.
Greetings @felangel , hope you are healthy and safe. I would also like to thank you for the libraries you have provided to the community. I'm using bloc and hydrated bloc. I was thinking why not provide async support for both fromJson and toJson on the HydratedBloc (

return _state = fromJson(Map<String, dynamic>.from(stateJson));
)

Describe the solution you'd like
I have this use case where we have to perform compute, validate checksum and then consider the input from local cache for further processing. At first, glance looks like it is not possible to invoke async methods/functions inside fromJson. If it cannot be implemented due to the underlying API, how do you suggest I should handle this problem on my side?

Tercera fase de desarrollo

Describe the bug
A clear and concise description of what the bug is.

To Reproduce
Steps to reproduce the behavior:

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected behavior
A clear and concise description of what you expected to happen.

Screenshots
If applicable, add screenshots to help explain your problem.

**Logs **
Run flutter analyze and attach any output of that command below.
If there are any analysis errors, try resolving them before filing this issue.

Paste the output of running flutter doctor -v here.

Additional context
Add any other context about the problem here.

Set expiry date or ttyl

Is it possible to set expiry date anyhow? Let's say on toJson set a expiry date and on toJson I will check if date is expired or not, if expired then fetch from API

[Feature Request] Hydrated blocs with different storages in the same app

From @ncuillery (original issue)

Is your feature request related to a problem? Please describe.
I have some hydrated blocs that use the default temp folder (as a cache for my information coming the network: perform the fetching request only if the data is older than 12 hours).
But I also would like to use a hydrated bloc using the shared preferences storage to store my user preferences such as language, newsletter optin, etc.
I can't do that because, if I implement a custom storage in a BlocDelegate, all my blocs will use this storage.

Describe the solution you'd like
I guess one solution would be to not using BlocDelegate for implementing custom storage. Or improve the BlocSupervisor/BlocDelegate API to allow multiple BlocDelegates.

Additional context
I think I'd have the same problem if I already use a SimpleBlocDelegate for logging purpose and I want to write an other BlocDelegate for custom hydrated bloc storage.

HydratedBloc catches failures to serialize json and continues without yielding the new state

Just found an issue where my HydratedBloc was rebuilding, but with the old states. There were no errors, and the state was being told to yield from the bloc correctly. Turns out the issue was that the toJson call was writing a non json-serializable map I.E. {'key1: ComplexClass}
Following the logic would not have gotten me there with the debugger
I appreciate it continuing to run, but I believe the state should still be yielded from Dart, even if it can't be yielded from json
Basically, the write should fail, not the yield

Question: OnTransition and onError for HydratedBloc

First of all, many thanks for your Bloc library, that's simply amazing!!!

Here is my issue:
I am adding HydratedBloc(s) to a project that does already contain many "SimpleBloc" that do not need to get Hydrated. So far I have been able to get the two sort of Blocs to co-exist with the following code:

void main() async {
BlocSupervisor.delegate = SimpleBlocDelegate();
BlocSupervisor.delegate = await HydratedBlocDelegate.build();
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((_) {
runApp(new App());
});
}

For the "SimpleBloc", I use the following to get the Transition and Error:
class SimpleBlocDelegate extends BlocDelegate {
@OverRide
onTransition(Bloc bloc, Transition transition) {
super.onTransition(bloc, transition);
}

@OverRide
void onError(Bloc bloc, Object error, StackTrace stacktrace) {
super.onError(bloc, error, stacktrace);
}
}

How do I achieve the same transition and error overrides for the HydratedBloc(s)?

Thanks again,

ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized.

hydrated_bloc version: v1.4.1

Describe the bug

Unhandled Exception: ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized.
If you're running an application and need to access the binary messenger before runApp() has been called (for example, during plugin initialization), then you need to explicitly call the WidgetsFlutterBinding.ensureInitialized() first.
If you're running a test, you can call the TestWidgetsFlutterBinding.ensureInitialized() as the first line in your test's main() method to initialize the binding.
defaultBinaryMessenger. (binary_messenger.dart:73)
defaultBinaryMessenger (binary_messenger.dart:86)
MethodChannel.binaryMessenger (platform_channel.dart:140)
MethodChannel.invokeMethod (platform_channel.dart:314)

getTemporaryDirectory (path_provider.dart:26)

HydratedBlocStorage.getInstance (hydrated_bloc_storage.dart:34)

HydratedBlocDelegate.build (hydrated_bloc_delegate.dart:19)

main (main.dart:18)

main (main.dart:1)
...

To Reproduce
Steps to reproduce the behavior:
Assuming here: Issue was caused after the update to Flutter 1.10.1, so update?

Paste the output of running flutter doctor -v here.
Flutter 1.10.1 • channel dev • https://github.com/flutter/flutter.git
Framework • revision ce45c2d3e6 (5 days ago) • 2019-09-06 20:11:41 -0400
Engine • revision b9ce2500d9
Tools • Dart 2.5.0 (build 2.5.0-dev.4.0 be66176534)

Additional context
My main() code is:

Future main() async {
  BlocSupervisor.delegate = await HydratedBlocDelegate.build();

Can't use HydratedBloc on non-Flutter platforms?

Describe the bug
Because of it's dependency on path_provider, the app fails on web and desktop (macOS in my case)

[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: MissingPluginException(No implementation found for method getTemporaryDirectory on channel plugins.flutter.io/path_provider)
#0      MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:319:7)
<asynchronous suspension>                                               
#1      getTemporaryDirectory (package:path_provider/path_provider.dart:35:22)
<asynchronous suspension>                                               
#2      HydratedBlocStorage.getInstance (package:hydrated_bloc/src/hydrated_bloc_storage.dart:39:49)
<asynchronous suspension>                                               
#3      HydratedBlocDelegate.build (package:hydrated_bloc/src/hydrated_bloc_delegate.dart:26:33)
<asynchronous suspension>                                               
#4      main (package:tavern/main.dart:21:56)                           

To Reproduce
Steps to reproduce the behavior:

  1. Attempt to use HydratedBloc on web or desktop
  2. See error

Expected behavior
Either have a check for the platform and use a platform io solution, or provide an alternative storage option for the dev to implement.

**Logs **
Run flutter analyze and attach any output of that command below.
If there are any analysis errors, try resolving them before filing this issue.

Paste the output of running flutter doctor -v here.

➜ ~ flutter doctor -v
[✓] Flutter (Channel master, v1.12.1-pre.59, on Mac OS X 10.15.2 19C39d, locale en-US)
• Flutter version 1.12.1-pre.59 at /Users/thinkdigital/development/flutter
• Framework revision 6cb6857f92 (4 days ago), 2019-11-14 16:50:17 -0500
• Engine revision 77c3512ec8
• Dart version 2.7.0

[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
• Android SDK at /Users/thinkdigital/Library/Android/sdk
• Android NDK location not configured (optional; useful for native profiling support)
• Platform android-29, build-tools 29.0.2
• ANDROID_SDK_ROOT = /Users/thinkdigital/Library/Android/sdk
• Java binary at: /Users/thinkdigital/Library/Application
Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/191.5977832/Android
Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)
• All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.2)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 11.2, Build version 11B44
• CocoaPods version 1.8.4

[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 3.5)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin version 41.1.2
• Dart plugin version 191.8593
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)

[✓] Android Studio (version 3.5)
• Android Studio at /Users/thinkdigital/Library/Application
Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/191.5977832/Android Studio.app/Contents
• Flutter plugin version 41.1.2
• Dart plugin version 191.8593
• Java version OpenJDK Runtime Environment (build 1.8.0_202-release-1483-b49-5587405)

[✓] IntelliJ IDEA Community Edition (version 2019.2.4)
• IntelliJ at /Applications/IntelliJ IDEA CE.app
• Flutter plugin version 41.1.4
• Dart plugin version 192.7402

[✓] VS Code (version 1.40.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.6.0

[✓] Connected device (5 available)
• SM G975U • R58M23BRC7J • android-arm64 • Android 9 (API
28)
• iPhone 11 Pro Max • F3A5BA32-2FC7-491B-9862-A42B54C487A5 • ios •
com.apple.CoreSimulator.SimRuntime.iOS-13-2 (simulator)
• macOS • macOS • darwin-x64 • Mac OS X 10.15.2
19C39d
• Chrome • chrome • web-javascript • Google Chrome
78.0.3904.97
• Web Server • web-server • web-javascript • Flutter Tools

Additional context
Add any other context about the problem here.

How Persist a List

Hello, thank for you awesome plugin.

I want to persist a list, but I don't find how, the example only work for on item, and I am loss How to persist the different type of data.

Thanks

FiveM ESX_Vehiclesshop issue

Hey,

i have a problem with my ESX_Vehiclesshop.
I can go into the job one time. All funktions are ok. But when i go the second time into the Marker i dont can open the Job!

Have anyone an idea?

Having an async operation in hydrated initialState

in my case I want to use this awesome package for localization.
for the first run of app , I have 2 option:

@freezed
abstract class InitialLocale with _$InitialLocale {
  const factory InitialLocale.systemLocale() = SystemLocale;
  const factory InitialLocale.preferedLocale(Locale locale) = PreferedLocale;
}

my problem is when user choose SystemLocale.
because in initialState , I have:

  @override
  LocalizationState get initialState =>
      super.initialState ??
      LocalizationState(
        initialLocale.when(
          // systemLocale: () => it should be an async operation from intl package
          preferedLocale: (locale) => locale,
        ),
      );

so what do you suggest about retrieving this async operation ?

Improving the documentation regarding how the state is persisted

It would be great if the documentation can explain a bit more about:

  • how, the state is persisted, for example, is it a database or a simple json file
  • where, is the persisted file saved
  • is it fast to persist, or should be used wisely
  • safety advice regarding persisted state (i.e. whether it is safe to persist user related infos)
  • is the persisted file can be encrypted

strange problems when migrate to new version

hi @felangel

what I has was:

class ThemeBloc extends HydratedBloc<ThemeEvent, ThemeState> {

  @override
  ThemeState get initialState =>
      super.initialState ??
      ThemeState(
        palette: Green(),
        isLight: true,
      );

  @override
  Stream<ThemeState> mapEventToState(ThemeEvent event) async* {
    yield* event.when(
      change: (palette, isLight) async* {
        yield state.copyWith(
          palette: palette ?? state.palette,
          isLight: isLight ?? state.isLight,
        );
      },
    );
  }

  @override
  ThemeState fromJson(Map<String, dynamic> json) {
    try {
      final state = ThemeState.fromJson(json);
      return state;
    } catch (_) {
      return null;
    }
  }

  @override
  Map<String, dynamic> toJson(ThemeState state) => state?.toJson();
}

but when I migrate to V5 , the toJson and fromJson always return null.
what I have done is to add:

ThemeState(
        palette: Green(),
        isLight: true,
      )

in the super constructor of my ThemeBloc in V5.
but I don't know what is the problem.

Tercera fase de desarrollo

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Maintain multiple states of a hydrated bloc

Is your feature request related to a problem? Please describe.
I'm wondering if it is possible to maintain multiple states of a hydrated bloc e.g. for each user of an app. If one user is logged out and another one is logged in the second user shouldn't see the data of the first one and vice versa but both data should be persisted.

Describe the solution you'd like
I would prefer to switch the storage directory based on a known user id but I don't know if this is possible during runtime.

Data lost when upgrading from 4.0.0 to 5.0.1

Describe the bug
After updating an app's dependencies to use hydrated_bloc 5.0.1, all saved data is lost from previous runs of the app. Verified on Android and iOS.

To Reproduce
Steps to reproduce the behavior:

  1. Run your app with hydrated_bloc 4.0.0
  2. Make some state changes which are saved via hydrated_bloc
  3. Re-run the app to see that the state changes were saved from the previous run
  4. Update the app to use hydrated_bloc 5.0.1
  5. Run the app and see that none of the previous state is retained

Expected behavior
Updating from 4.0.0 to 5.0.1 should be a non-destructive operation for users.

Logs

andre@Andres-MacBook-Pro gaming_news % flutter doctor -v
[✓] Flutter (Channel beta, 1.19.0-4.3.pre, on Mac OS X 10.15.5 19F101, locale en-US)
    • Flutter version 1.19.0-4.3.pre at /Users/andre/Development/flutter
    • Framework revision 8fe7655ed2 (5 days ago), 2020-07-01 14:31:18 -0700
    • Engine revision 9a28c3bcf4
    • Dart version 2.9.0 (build 2.9.0-14.1.beta)

 
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    • Android SDK at /Users/andre/Library/Android/sdk
    • Platform android-29, build-tools 28.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 11.5)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 11.5, Build version 11E608c
    • CocoaPods version 1.9.3

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 3.6)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 44.0.2
    • Dart plugin version 192.7761
    • Java version OpenJDK Runtime Environment (build 1.8.0_212-release-1586-b4-5784211)

[✓] VS Code (version 1.46.1)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.12.1

• No issues found!

Additional context
After some time investigating the issue, it is caused by the underlying migration to the hydrated_cubit library. That migration in itself isn't the problem, but in the commit to switch to hydrated_cubit, the backwards-compatible migration code was deleted.

The file storage used for hydration in 4.0.0 was <directory>/.hydrated_bloc.json but was moved to <directory>/hydrated_box.hive[c] in 5.0.1. Commit 7cae45f had migration code for loading .hydrated_bloc.json if it existed.

See:

Workaround
For other users experiencing this issue and needing a timely resolution, you can use you own custom hydrated storage as described in the README.

The following workaround works for my use case with 5.0.1. It is based on HydratedStorage from 4.0.0 and does not use hive for storage. There are key changes to the read and write methods to support differences introduced in 5.0.0.

class HydratedBlocStorageWorkaround implements HydratedStorage {
  HydratedBlocStorageWorkaround._(this._storage, this._file);

  static final _lock = Lock();
  final Map<String, dynamic> _storage;
  final File _file;

  static Future<HydratedBlocStorageWorkaround> getInstance({
    Directory storageDirectory,
  }) {
    return _lock.synchronized(() async {
      final directory = storageDirectory ?? await getTemporaryDirectory();
      final file = File('${directory.path}/.hydrated_bloc.json');
      var storage = <String, dynamic>{};

      if (await file.exists()) {
        try {
          storage = json.decode(await file.readAsString()) as Map<String, dynamic>;
        } on dynamic catch (_) {
          await file.delete();
        }
      }

      return HydratedBlocStorageWorkaround._(storage, file);
    });
  }

  @override
  dynamic read(String key) {
    final String jsonString = _storage[key];
    return jsonString?.isNotEmpty == true
        ? Map<String, dynamic>.from(json.decode(jsonString))
        : null;
  }

  @override
  Future<void> write(String key, dynamic value) {
    return _lock.synchronized(() {
      _storage[key] = json.encode(value);
      return _file.writeAsString(json.encode(_storage));
    });
  }

  @override
  Future<void> delete(String key) {
    return _lock.synchronized(() {
      _storage[key] = null;
      return _file.writeAsString(json.encode(_storage));
    });
  }

  @override
  Future<void> clear() {
    return _lock.synchronized(
      () async {
        _storage.clear();
        if (await _file.exists()) {
          await _file.delete();
        }
      },
    );
  }
}

Scopes proposal

Using implicit bindings (new)

Preview

Protoversion of this change is available on my scopes-implicit

Resolved issues

  • Obligation to modify constructor is now completely relaxed
  • HydratedProvider does not depend on hydrated_bloc now, so can be merged with BlocProvider

Issues

  • Interference with package:hive? (Hive.init in multiple places)
  • Provisioning of directory should be done only once/or redone after all boxes are closed
  • Create scopes dynamically, i.e. encrypted scope after user logged in

Naive (deprecated)

Preview

Protoversion of this change is available on my scopes branch

Usage

Inside main() we configure Hydrated to init some storage boxes with tokens 'secure' and 'number':

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  BlocSupervisor.delegate = await HydratedBlocDelegate.build();

  final byteskey = sha256.convert(utf8.encode('password')).bytes;
  await Hydrated.config({
    'secure': ScopeConfig(encryptionCipher: HydratedAesCipher(byteskey)),
    'number': ScopeConfig(),
  });

  runApp(App());
}

Inside build() then, we use Hydrated.scope() to obtain ScopeBuilder and then build HydratedScope with child widget, for example HydratedProvider:

Widget build(BuildContext context) {
  return Hydrated.scope('number')(HydratedProvider<CounterBloc>(
    create: (context, scope) => CounterBloc(scope),
    child: Builder(builder: (context) {
      return Hydrated.scope('secure')(HydratedProvider<CounterBloc>(
        create: (context, scope) => CounterBloc(scope),
        child: MaterialApp(
          title: 'Flutter Demo',
          home: CounterPage(context.bloc<CounterBloc>()),
        ),
      ));
    }),
  ));
}

As you can see,

create: (context, scope) => CounterBloc(scope) 

now has second parameter - scope (or scopeToken/scopeName) i.e 'secure',
which is extracted from context in HydratedProvider's build() method.
It has to be passed down to CounterBloc via it's constructor:

class CounterBloc extends HydratedBloc<CounterEvent, CounterState> {
  CounterBloc(String scope) : super(scope);
...

So now user is responsible for providing scopeToken to super, as we can't pull data from InheritedWidget inside core lib.

And also custom HydratedProvider is not the most pleasant thing to have

No audio

Is your feature request related to a problem? Please describe.
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

Describe the solution you'd like
A clear and concise description of what you want to happen.

Describe alternatives you've considered
A clear and concise description of any alternative solutions or features you've considered.

Additional context
Add any other context or screenshots about the feature request here.

Cannot Write, unknown type

Describe the bug
A clear and concise description of what the bug is.

image

@immutable
@CopyWith()
@HiveType(typeId: 1)
@JsonSerializable(nullable: false)
class User extends Equatable {
  /// Display name of this.
  @JsonKey(name: "displayName")
  @HiveField(0)
  final String name;

  /// Score of this.
  @HiveField(1)
  final int score;

  /// Level of this.
  @HiveField(2)
  final int level;
  // more code...
  /// Creates a [User] from Json.
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);

  /// Creates a Json from [User].
  Map<String, dynamic> toJson() => _$UserToJson(this);

To Reproduce
Steps to reproduce the behavior:
i think it's too much code, it worked prior on 3.1.0 perfectly, but now with hive i don't know what else to change.

Expected behavior
persisting data like in 3.1.0

Screenshots
image

**Logs **

I/flutter (10060): [2020-06-24 20:54:45.136155 | ConsoleHandler | INFO]
I/flutter (10060): [2020-06-24 20:54:45.136688 | ConsoleHandler | INFO] ------- STACK TRACE -------
I/flutter (10060): [2020-06-24 20:54:45.147581 | ConsoleHandler | INFO] #0      Bloc.onError.<anonymous closure> 
package:bloc/src/bloc.dart:146
I/flutter (10060): [2020-06-24 20:54:45.147917 | ConsoleHandler | INFO] #1      Bloc.onError 
package:bloc/src/bloc.dart:147
I/flutter (10060): [2020-06-24 20:54:45.148223 | ConsoleHandler | INFO] #2      HydratedBloc.initialState 
package:hydrated_bloc/src/hydrated_bloc.dart:38
I/flutter (10060): [2020-06-24 20:54:45.148455 | ConsoleHandler | INFO] #3      CafeteriaBloc.initialState 
package:tc_frontend//cafeteria/cafeteria_bloc.dart:33
I/flutter (10060): [2020-06-24 20:54:45.148553 | ConsoleHandler | INFO] #4      new Bloc 

Paste the output of running flutter doctor -v here.

[√] Flutter (Channel stable, v1.17.3, on Microsoft Windows [Version 10.0.18363.836], locale de-DE)
    • Flutter version 1.17.3 at C:\flutterSDK\flutter
    • Framework revision b041144f83 (3 weeks ago), 2020-06-04 09:26:11 -0700
    • Engine revision ee76268252
    • Dart version 2.8.4

[√] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    • Android SDK at C:\androidsdk
    • Platform android-29, build-tools 28.0.3
    • ANDROID_HOME = C:\androidsdk
    • Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)
    • All Android licenses accepted.

[√] Android Studio (version 3.2)
    • Android Studio at C:\Program Files\Android\Android Studio
    • Flutter plugin version 31.2.1
    • Dart plugin version 181.5656
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1136-b06)

[√] VS Code, 64-bit edition (version 1.45.0)
    • VS Code at C:\Program Files\Microsoft VS Code
    • Flutter extension version 3.11.0

[√] Connected device (1 available)
    • AOSP on IA Emulator • emulator-5554 • android-x86 • Android 9 (API 28) (emulator)

Additional context
i added the hive annotations, what else do i need to do to make it work again?
Cheers and thanks for the effort :)

[Feature Request] Add HydratedBloc.clear()

Since this operates as a sort of cache, caches have a clear/invalidate option.
I have some data I need to remove and the only way is to uninstall, or try to create a new empty state and yield it in order to do so

Question: persistence time

Hi. First, awesome work with hydrated too.

I have a question/issue 🤔

I'm using hydrated to store if it isn't the first time the app is opening. It works but after some time, like 4 o 5 days, the state is lost.

I'm using hydrated incorrectly?

class MainBloc extends HydratedBloc<MainEvent, MainState> {
  @override
  MainState get initialState =>
      super.initialState ?? MainState(true, false);

  @override
  Stream<MainState> mapEventToState(
    MainEvent event,
  ) async* {
    if (event is DisableFirstTime) {
      yield MainState(false, currentState.logged);
    }
  }

  void dispatchDisableFirstTime() {
    dispatch(DisableFirstTime());
  }

  @override
  MainState fromJson(Map<String, dynamic> json) =>
      MainState(json['firstTime'] as bool, json['logged'] as bool);

  @override
  Map<String, dynamic> toJson(MainState state) =>
      {'firstTime': state.firstTime, 'logged': state.logged};
}

class MainState {
  bool firstTime;
  bool logged;

  MainState(this.firstTime, this.logged);
}

class DisableFirstTime extends MainEvent {
  @override
  String toString() => 'DisableFirstTime';
}

Thank you for your time.

How Persist a List

Hello, thank for you awesome plugin.

I want to persist a list, but I don't find how, the example only work for on item, and I am loss How to persist the different type of data.

Is it compatible with List<dynamic>?

Thanks

Data loss when upgrading from v3.1.0 to latest version

Describe the bug
I believe transition from v3.1.0 to v5.0.1 should be as safe as from v3.1.0 to v4.1.1

To Reproduce
Steps to reproduce the behavior:

  • Upgrade project from v3 to v5

Expected behavior
a) No data loss
b) This behavior is very explicitly stated in changelog and readme

FormatException: Unexpected end of input error

Running into this error:

Exception has occurred.
FormatException (FormatException: Unexpected end of input (at character 1)

On this line:

`void main() async {

BlocSupervisor.delegate = SimpleBlocDelegate(
await HydratedBlocStorage.getInstance(), <-----error throwing here
);`

Any idea what might be causing this?
Thanks,
Bob

Allow for optional storagePath override

Is your feature request related to a problem? Please describe.
As a developer, I want to have the ability to override the storage location in cases where I don't want the data to reside in temporary storage.

Describe the solution you'd like

final applicationDocumentsDirectory = await getApplicationDocumentsDirectory();
BlocSupervisor.delegate = await HydratedBlocDelegate.build(
  storageDirectory: applicationDocumentsDirectory,
);

Additional context

  • Partially addresses #27

Unhandled Exception: HiveError: Cannot write, unknown type: _$Orange. Did you forget to register an adapter?

I am using hydrated_bloc with Freezed package.

State is:

@freezed
abstract class ThemeState with _$ThemeState {
  const factory ThemeState({
    @required ThemeScheme theme,
    @required ThemeBrightness brightness,
  }) = _ThemeState;

  factory ThemeState.fromJson(Map<String, dynamic> json) =>
      _$ThemeStateFromJson(json);
}

ThemeScheme is:

@freezed
abstract class ThemeScheme with _$ThemeScheme {
  const factory ThemeScheme.orange() = Orange;
  const factory ThemeScheme.blue() = Blue;

  factory ThemeScheme.fromJson(Map<String, dynamic> json) =>
      _$ThemeSchemeFromJson(json);
}

ThemeBrightness is:

@freezed
abstract class ThemeBrightness with _$ThemeBrightness {
  const factory ThemeBrightness.light() = Light;
  const factory ThemeBrightness.dark() = Dark;

  factory ThemeBrightness.fromJson(Map<String, dynamic> json) =>
      _$ThemeBrightnessFromJson(json);
}

and:

@override
  ThemeState fromJson(Map<String, dynamic> json) {
    try {
      final state = ThemeState.fromJson(json);
      return state;
    } catch (_) {
      return null;
    }
  }

  @override
  Map<String, dynamic> toJson(ThemeState state) {
    return state.toJson();
  }

but I get :

E/flutter (22951): [ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: HiveError: Cannot write, unknown type: _$Orange. Did you forget to register an adapter?
E/flutter (22951): #0      BinaryWriterImpl.write 
package:hive/…/binary/binary_writer_impl.dart:310
E/flutter (22951): #1      BinaryWriterImpl.writeMap 
package:hive/…/binary/binary_writer_impl.dart:215
E/flutter (22951): #2      BinaryWriterImpl.write 
package:hive/…/binary/binary_writer_impl.dart:306
E/flutter (22951): #3      BinaryWriterImpl.writeFrame 
package:hive/…/binary/binary_writer_impl.dart:254
E/flutter (22951): #4      StorageBackendVm.writeFrames.<anonymous closure> 
package:hive/…/vm/storage_backend_vm.dart:123
E/flutter (22951): #5      ReadWriteSync.syncWrite.<anonymous closure> 
package:hive/…/vm/read_write_sync.dart:26
E/flutter (22951): #6      _rootRunUnary  (dart:async/zone.dart:1192:38)
E/flutter (22951): #7      _CustomZone.runUnary  (dart:async/zone.dart:1085:19)
E/flutter (22951): #8      _FutureListener.handleValue  (dart:async/future_impl.dart:141:18)
E/flutter (22951): #9      Future._propagateToListeners.handleValueCallback  (dart:async/future_impl.dart:686:45)
E/flutter (22951): #10     Future._propagateToListeners  (dart:async/future_impl.dart:715:32)
E/flutter (22951): #11     Future._addListener.<anonymous closure>  (dart:async/future_impl.dart:391:9)
E/flutter (22951): #12     _rootRun  (dart:async/zone.dart:1184:13)
E/flutter (22951): #13     _CustomZone.run  (dart:async/zone.dart:1077:19)
E/flutter (22951): #14     _CustomZone.runGuarded  (dart:async/zone.dart:979:7)
E/flutter (22951): #15     _CustomZone.bindCallbackGuarded.<anonymous closure>  (dart:async/zone.dart:1019:23)
E/flutter (22951): #16     _microtaskLoop  (dart:async/schedule_microtask.dart:43:21)
E/flutter (22951): #17     _startMicrotaskLoop  (dart:async/schedule_microtask.dart:52:5)
E/flutter (22951):

Bloc lifecycle w/Hydrated_Bloc?

In a bloc pre-Hydrated_Bloc you might have a lifecycle that looks like this:

Init\Load -> New State -> Change Value -> New State (etc)

in that Init\Load event, you might go fetch a locally stored value (using something like mmkv) and if it didn't exist there, go get it from a database or service.

The readme says that state is "automatically" persisted and restored. But what is the trigger/when is toJson() and fromJson() called?

In your init, how do know if a previous state was saved?

Sorry if I have missed this somewhere.
Thanks,
Bob

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.