Flutter Hive 打开现有框,但不从中读取值

Flutter Hive opens existing box, but is not reading values from it

我正处于编程时刻之一,我觉得我快要疯了,所以希望有人能帮助我。

我有一个 Flutter 应用,它使用 Hive 在 运行 之间存储数据。当应用程序最初启动时,它会打开一个框并检索一些信息来为 MaterialApp 设置已保存的主题。然后它会为应用程序构建主页并检索一系列其他选项。这工作得很好(我的 phone 上有它的一个版本,工作得很好),但由于某种原因它停止工作了。

当应用程序执行时,初始 MyApp 声明 Hive 框已打开,但其中没有任何值。这对于调用选项 class 以检索选项数据来说是正确的。那个电话之后,盒子突然有了值,我能够检索并打印出钥匙。当应用程序随后构建主页时,它声明该框已打开并且它具有值并且能够从选项 class 检索选项数据。以前,我第一次阅读数据提取主题没有问题。我已经在下面发布了相关的代码部分以及 运行.

的打印输出

我正在 运行 在网络上安装该应用程序,并且还在移动模拟器上 运行 它。它以前在两个平台上都运行良好,但现在在 web 平台上无法运行。它似乎在移动模拟器上运行良好。

该应用正在使用以下版本:

Flutter 2.10.4 • channel stable • https://github.com/flutter/flutter.git
Framework • revision c860cba910 (6 days ago) • 2022-03-25 00:23:12 -0500
Engine • revision 57d3bac3dd
Tools • Dart 2.16.2 • DevTools 2.9.2

pubspec.yaml dependencies:
hive: ^2.0.6
hive_flutter: ^1.1.0 

我今天已经升级到最新版本的 Flutter,看看是否解决了问题。我在之前的稳定版本中遇到了同样的问题。

我已经更新到 hive 2.1.0 并获得相同的 problem/output。

我也尝试过使用 Dart 2.16.0 将 Flutter 降级到 2.10.0,我知道它工作正常,但并没有解决问题。

main.dart

import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:responsive_sizer/responsive_sizer.dart';
import 'package:lettercloud/data/colours.dart';
import 'package:lettercloud/options/option_page.dart';

const String _boxName = 'lettercloud';

void main() async {
  await Hive.initFlutter();
  Hive
    ..registerAdapter(CellAdapter())
    ..registerAdapter(ThemeModeOptionAdapter());
  await Hive.openBox(_boxName);
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  MyApp({Key? key}) : super(key: key);

  final Box _box = Hive.box(_boxName); // Object for hive data access
  final Options _options = Options();
  final Colours _colours = Colours();
  late bool _firstRun = true; // Flag to only read Hive options on first run

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    print('build() Before first run.  Extracting box keys. Attempt 1...');
    for (String key in _box.keys) {
      print('box keys: $key');
    }

    if (_firstRun) {
      print(
          'First run.  Hive box is open: ${_box.isOpen}  Box has values: ${_box.isNotEmpty}');
      _options.setHiveBox(_box); // Pass hive object and retrieve options
      _firstRun = false;
    }
    print('');
    print('build() After first run.  Extracting box keys. Attempt 2...');
    for (String key in _box.keys) {
      print('box keys: $key');
    }

    return AnimatedBuilder(
        animation: _options.getThemeNotifier(),
        builder: (context, child) {
          return MaterialApp(
            title: 'Lettercloud',
            theme: FlexThemeData.light(scheme: FlexScheme.jungle),
            darkTheme: FlexThemeData.dark(scheme: FlexScheme.jungle),
            themeMode: _options.getThemeMode(),
            home: ResponsiveSizer(
              builder: (context, orientation, screenType) {
                return const MyPage(title: 'Lettercloud Anagram Helper');
              },
            ),
          );
        });
  }
}

class MyPage extends StatefulWidget {
  const MyPage({Key? key, required this.title}) : super(key: key);
  final String title;

  @override
  State<MyPage> createState() => MyPageState();
}

class MyPageState extends State<MyPage> {
  final Options _options = Options();
  late final Box _box; // Object for hive data access
  late Widget _displayGrid;

  @override
  void initState() {
    super.initState();
    print('Doing init MyPageState');
    _box = Hive.box(_boxName);
    _options.setHiveBox(_box); // Pass hive object and retrieve options
    _setGrid(_options.getGridType());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Lettercloud'),
      ),
      resizeToAvoidBottomInset: false, // Overlay on-screen keyboard
      body: SafeArea(
        child: _displayGrid,
      ),
    );
  }

  // Set the grid to display based on the grid type option
  void _setGrid(GridType type) {
    _displayGrid = _options.getGridType() == GridType.square
        ? GridSquare(box: _box, options: _options, update: updateGrid)
        : GridDiamond(box: _box, options: _options, update: updateGrid);
  }

  // Callback to set the grid type if the option changes
  void updateGrid(GridType type) {
    setState(() {
      _setGrid(type);
    });
  }
}

options.dart

import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:hive_flutter/hive_flutter.dart';

class Options {
  bool _lightMode = true; // Use light colours, or dark
  static const String _lightModeName = 'lightMode';

  bool _showGrid = true; // Show grid around tiles, or not
  static const String _showGridName = 'showGrid';

  bool _firstEdit =
      true; // Flag to show edit on first start, doesn't need saving
  bool _editOnStart = false; // Show edit at startup, or not
  static const String _editOnStartName = 'editOnStart';

  CharType _charType = CharType.mixed; // Type of letters to show
  static const String _charTypeName = 'charType';

  ThemeModeOption _themeMode = ThemeModeOption()..setMode(ThemeMode.light);
  static const String _themeModeName = 'themeMode';

  late Box _box; // Hive object
  late final double _tabletF; // Reduction factor for tablet displays

  late GridType _gridType = GridType.square;
  static const String _gridTypeName = 'gridType';

  late GridType _savedGridType = _gridType;
  static const String _savedGridTypeName = 'savedGridType';

  // last page name - used to control text entry on startup
  String _lastPage = PageName.main.toString();
  static const String _lastPageName = 'lastPageName';

  // Flag to show if the grid type has change.  Used to prevent 'show on start'
  // triggering the text entry box after the grid layout has been changed by the user
  bool _backFromOptionsPage = false;
  final String _backFromOptionsPageName = 'fromOptions';

  ///
  /// Hive management methods and global options setting
  ///
  void setHiveBox(Box b) {
    _box = b; // Pass the hive management object
    print(
        'Options hive box. Box is open: ${_box.isOpen}  Box has values: ${_box.isNotEmpty}.');
    // Set screen size factor for web vs tablet
    if (kIsWeb) {
      _tabletF = 0.4; // Factor components by 0.4 for web
    } else {
      _tabletF = 0.6; // Factor components by 0.6 for tablets
    }
    // Retrieve any option data values
    if (_box.get(_lightModeName) != null) {
      _lightMode = _box.get(_lightModeName);
    } else {
      print('Cannot find $_lightModeName');
      _box.put(_lightModeName, _lightMode);
    }
    if (_box.get(_showGridName) != null) {
      _showGrid = _box.get(_showGridName);
    } else {
      _box.put(_showGridName, _showGrid);
    }
    if (_box.get(_editOnStartName) != null) {
      _editOnStart = _box.get(_editOnStartName);
    } else {
      _box.put(_editOnStartName, _editOnStart);
    }
    if (_box.get(_charTypeName) != null) {
      String temp = _box.get(_charTypeName);
      _charType = getCharEnum(temp);
    } else {
      _box.put(_charTypeName, _charType.toString());
    }
    if (_box.get(_themeModeName) != null) {
      _themeMode = _box.get(_themeModeName);
    } else {
      _box.put(_themeModeName, _themeMode);
    }
    if (_box.get(_gridTypeName) != null) {
      String temp = _box.get(_gridTypeName);
      _gridType = getGridEnum(temp);
    } else {
      _box.put(_gridTypeName, _gridType.toString());
    }
    if (_box.get(_savedGridTypeName) != null) {
      String temp = _box.get(_savedGridTypeName);
      _savedGridType = getGridEnum(temp);
    } else {
      _box.put(_savedGridTypeName, _savedGridType.toString());
    }
    if (_box.get(_backFromOptionsPageName) != null) {
      _box.put(_backFromOptionsPageName, _backFromOptionsPage);
    } else {
      _box.put(_backFromOptionsPageName, _backFromOptionsPage);
    }
    // Load last page value or reset if doesn't exit
    if (_box.get(_lastPageName) != null) {
      _box.put(_lastPageName, _lastPage);
    } else {
      _box.put(_lastPageName, _lastPage);
    }
    _box.flush(); // Make sure everything is written to the disk
  }
}

命令行输出:

flutter run -d chrome --web-renderer html --web-port 5555
Launching lib\main.dart on Chrome in debug mode...
Waiting for connection from debug service on Chrome...             18.6s
This app is linked to the debug service: ws://127.0.0.1:54752/JAXqfQgauf4=/ws
Debug service listening on ws://127.0.0.1:54752/JAXqfQgauf4=/ws

 Running with sound null safety

  To hot restart changes while running, press "r" or "R".
For a more detailed help message, press "h". To quit, press "q".

An Observatory debugger and profiler on Chrome is available at: http://127.0.0.1:54752/JAXqfQgauf4=
The Flutter DevTools debugger and profiler on Chrome is available at:
http://127.0.0.1:9101?uri=http://127.0.0.1:54752/JAXqfQgauf4=

build() Before first run.  Extracting box keys. Attempt 1...
First run.  Hive box is open: true  Box has values: false
Options hive box. Box is open: true  Box has values: false.
Cannot find lightMode

build() After first run.  Extracting box keys. Attempt 2...
box keys: charType
box keys: editOnStart
box keys: fromOptions
box keys: gridType
box keys: lastPageName
box keys: lightMode
box keys: savedGridType
box keys: showGrid
box keys: themeMode
Doing init MyPageState
Options hive box. Box is open: true  Box has values: true.
Application finished.

更新#1 自最初发布以来,我尝试删除该框并重新 运行 应用程序,以防这是由损坏的文件引起的。这没有任何区别。

我也尝试在 openBox() 命令中添加 .then 以防这是另一个异步编程问题,但这也没有什么不同,即

  await Hive.openBox(_boxName).then((value) {
    print('value is $value');
    runApp(MyApp());
  });

更新#2 所以,我花了一些时间来解决这个问题,但如果它们尚不存在,我会在第一个 运行 上创建我的框值(以解决第一个 运行 的用例应用程序)。如果我删除 setHiveBox() 方法中的所有 put 语句,那么我会始终如一地遇到问题。换句话说,在应用程序 运行 时我的选项 class 创建它们之前,框中没有值。这表明应用程序未将数据保存到磁盘。我将 main.dart 和 options.dart 与最后已知的工作版本进行了比较,看不出有任何明显的差异。什么可以阻止应用程序将数据保存到磁盘?请注意,我已经测试了我开发的另一个使用 Hive 的应用程序,它继续完美运行。它使用与此应用程序相同版本的 Hive。

我通过对项目执行 flutter clean 修复此问题,删除 flutter 安装(从磁盘中完全删除安装文件夹),下载并 re-installing flutter 然后执行 flutter pub get 在项目文件夹中。

我之前曾单独尝试过 flutter clean flutter pub get,但这并没有解决问题,所以上次升级后 flutter 文件夹本身可能出了问题?无论如何,全新安装一切都解决了问题。