Error on Initial request for local db by using riverpod and IsarDB @Flutter

62 views Asked by At

I try to make a simple app. App systems are like this: load json data → save inside db→display UI I use Isar db and Riverpod and want to realize it. However, I got an error when app launch at first. Error message is "flutter: type 'Null' is not a subtype of type 'User' in type cast" So I guess data is referenced before the data is stored. The process is asynchronous and well done, and I think I am handling it properly, but it doesn't seem to be working. I reload the app, the data is displayed. I also put Future.delay before return the build, I can get the display, but it isn't smart. Thank you for your cooperation.

isar_provider.dart

    import 'package:asyncronize_sample/user.dart';
    import 'package:isar/isar.dart';
    import 'package:riverpod_annotation/riverpod_annotation.dart';
    import 'package:path_provider/path_provider.dart';

  part 'isar_provider.g.dart';

  @Riverpod(keepAlive: true)
  Future<Isar> isar(IsarRef ref) async {
    final dir = await getApplicationDocumentsDirectory();
    return Isar.open([UserSchema], directory: dir.path, inspector: true);

data2_provider.dart

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:isar/isar.dart';
import 'package:asyncronize_sample/user.dart';
import 'package:asyncronize_sample/isar_provider.dart';
import 'package:flutter/services.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'data2_provider.g.dart';

@Riverpod(keepAlive: true)
class Data2 extends _$Data2 {
  @override
  FutureOr<User> build() async {
    final isar = await ref.read(isarProvider.future);
    final user = await isar.users.where().findFirst();
    
    // if (user == null) {
    //   await save();
    //   print("Finish save");
    //   // await Future.delayed(Duration(seconds: 2));
    //   return await Future.value(await isar.users.where().findFirst());
    // }
    return Future.value(user);
  }

  Future<void> save() async {
    // final json = await rootBundle.loadString('assets/data.json');
    final json = await rootBundle.loadString('assets/data2.json').then((value) => jsonDecode(value) as Map<String, dynamic>);
    final user = User()
      ..name = json['name']
      ..age = json['age'];
    final isar = await ref.read(isarProvider.future);
    isar.writeTxn(() async {
      await isar.users.put(user);
      print("Finish user writeTxn");
    });
  }
}

main.dart

class MainApp extends ConsumerWidget {
  const  MainApp({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    ref.read(data2Provider);
    return const MaterialApp(
      home: Scaffold(
        body: HomeView2()
      ),
    );
  }
}

class HomeView2 extends ConsumerStatefulWidget {
  const HomeView2({super.key});

  @override
  _HomeView2State createState() => _HomeView2State();
}

class _HomeView2State extends ConsumerState<HomeView2> {
  @override
  void initState() {
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    final user = ref.watch(data2Provider);
    return Scaffold(
      body: Center(
        child: user.when(data: (userData) {
          if (user.isLoading) {
            return const CircularProgressIndicator();
          }

          if (user.isRefreshing) {
            return const CircularProgressIndicator();
          }
          if (user.hasValue) {
            setState(() {
              debugPrint('user: ${userData.name}');
            });
          }
          return Text(userData.name);
        }
        , error: (o, e){
          debugPrint('Error: ${e.toString()}');
          debugPrint(o.toString());
        }, loading: (){
          return const CircularProgressIndicator();
        }),
      ),
    );
  }
}
1

There are 1 answers

0
BG Park On

You should load and override isar provider in main.dart.

isar_provider.dart

part 'isar_service.g.dart';

@Riverpod(keepAlive: true)
Isar isar(IsarRef ref) {
  return throw UnimplementedError();
}

main.dart

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize Isar
  final dir = await getApplicationDocumentsDirectory();
  final Isar isar = await Isar.openAsync(
    schemas: [UserSchema],
    directory: dir.path,
  );

  runApp(
    ProviderScope(
      overrides: [isarProvider.overrideWithValue(isar)],
      child: const MainApp(),
    ),
  );
}

Also, Don't use ref.read in build. (Check the MainApp Widget).